@@ -346,6 +346,7 @@ size_t ExternalStreamingStream::FillBuffer(size_t position) {
346346 current_data_length_ = source_stream_->GetMoreData (¤t_data_);
347347 current_data_offset_ = 0 ;
348348 bool data_ends = current_data_length_ == 0 ;
349+ bookmark_data_is_from_current_data_ = false ;
349350
350351 // A caveat: a data chunk might end with bytes from an incomplete UTF-8
351352 // character (the rest of the bytes will be in the next chunk).
@@ -405,6 +406,15 @@ bool ExternalStreamingStream::SetBookmark() {
405406 // - buffer_[buffer_cursor_ .. buffer_end_] => bookmark_buffer_
406407 // - current_data_[.._offset_ .. .._length_] => bookmark_data_
407408 // - utf8_split_char_buffer_* => bookmark_utf8_split...
409+ //
410+ // To make sure we don't unnecessarily copy data, we also maintain
411+ // whether bookmark_data_ contains a copy of the current current_data_
412+ // block. This is done with:
413+ // - bookmark_data_is_from_current_data_
414+ // - bookmark_data_offset_: offset into bookmark_data_
415+ //
416+ // Note that bookmark_data_is_from_current_data_ must be maintained
417+ // whenever current_data_ is updated.
408418
409419 bookmark_ = pos_;
410420
@@ -414,10 +424,21 @@ bool ExternalStreamingStream::SetBookmark() {
414424 CopyCharsUnsigned (bookmark_buffer_.start (), buffer_cursor_, buffer_length);
415425
416426 size_t data_length = current_data_length_ - current_data_offset_;
417- bookmark_data_.Dispose ();
418- bookmark_data_ = Vector<uint8_t >::New (static_cast <int >(data_length));
419- CopyBytes (bookmark_data_.start (), current_data_ + current_data_offset_,
420- data_length);
427+ size_t bookmark_data_length = static_cast <size_t >(bookmark_data_.length ());
428+ if (bookmark_data_is_from_current_data_ &&
429+ data_length < bookmark_data_length) {
430+ // Fast case: bookmark_data_ was previously copied from the current
431+ // data block, and we have enough data for this bookmark.
432+ bookmark_data_offset_ = bookmark_data_length - data_length;
433+ } else {
434+ // Slow case: We need to copy current_data_.
435+ bookmark_data_.Dispose ();
436+ bookmark_data_ = Vector<uint8_t >::New (static_cast <int >(data_length));
437+ CopyBytes (bookmark_data_.start (), current_data_ + current_data_offset_,
438+ data_length);
439+ bookmark_data_is_from_current_data_ = true ;
440+ bookmark_data_offset_ = 0 ;
441+ }
421442
422443 bookmark_utf8_split_char_buffer_length_ = utf8_split_char_buffer_length_;
423444 for (size_t i = 0 ; i < utf8_split_char_buffer_length_; i++) {
@@ -436,12 +457,14 @@ void ExternalStreamingStream::ResetToBookmark() {
436457
437458 // bookmark_data_* => current_data_*
438459 // (current_data_ assumes ownership of its memory.)
439- uint8_t * data = new uint8_t [bookmark_data_.length ()];
440460 current_data_offset_ = 0 ;
441- current_data_length_ = bookmark_data_.length ();
442- CopyCharsUnsigned (data, bookmark_data_.begin (), bookmark_data_.length ());
461+ current_data_length_ = bookmark_data_.length () - bookmark_data_offset_;
462+ uint8_t * data = new uint8_t [current_data_length_];
463+ CopyCharsUnsigned (data, bookmark_data_.begin () + bookmark_data_offset_,
464+ current_data_length_);
443465 delete[] current_data_;
444466 current_data_ = data;
467+ bookmark_data_is_from_current_data_ = true ;
445468
446469 // bookmark_buffer_ needs to be copied to buffer_.
447470 CopyCharsUnsigned (buffer_, bookmark_buffer_.begin (),
@@ -462,6 +485,7 @@ void ExternalStreamingStream::FlushCurrent() {
462485 current_data_ = NULL ;
463486 current_data_length_ = 0 ;
464487 current_data_offset_ = 0 ;
488+ bookmark_data_is_from_current_data_ = false ;
465489}
466490
467491
0 commit comments