Commit 0973a408 authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

Revert "[scanner] Rewrite character streams by separating underlying bytestreams from buffering."

This reverts commit 5f2f418d.

Reason for revert: Speculative revert for LayoutTest timeouts

https://ci.chromium.org/buildbot/client.v8.fyi/V8-Blink%20Linux%2064/24596
https://ci.chromium.org/p/v8/builders/luci.v8.ci/V8-Blink%20Linux%2064%20-%20future/4707
https://ci.chromium.org/buildbot/client.v8.fyi/V8-Blink%20Linux%2064%20(dbg)/12467

Original change's description:
> [scanner] Rewrite character streams by separating underlying bytestreams from buffering.
> 
> Additionally now we only scan over flat heap strings.
> 
> Change-Id: Ic449b19aecd7fc3f283a04a3df6a39772d471565
> Reviewed-on: https://chromium-review.googlesource.com/1125854
> Reviewed-by: Marja Hölttä <marja@chromium.org>
> Commit-Queue: Toon Verwaest <verwaest@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#54224}

TBR=marja@chromium.org,verwaest@chromium.org

Change-Id: Ica3026f318a85ec6bb24a38a8cd998f12c146d7e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/1126819Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54231}
parent 064a3b18
...@@ -134,7 +134,7 @@ void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) { ...@@ -134,7 +134,7 @@ void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) {
Handle<String> source(String::cast(script->source()), isolate); Handle<String> source(String::cast(script->source()), isolate);
if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) { if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For( std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
isolate, source, shared_->StartPosition(), shared_->EndPosition())); source, shared_->StartPosition(), shared_->EndPosition()));
parse_info_->set_character_stream(std::move(stream)); parse_info_->set_character_stream(std::move(stream));
} else { } else {
source = String::Flatten(isolate, source); source = String::Flatten(isolate, source);
...@@ -192,7 +192,7 @@ void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) { ...@@ -192,7 +192,7 @@ void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) {
} }
wrapper_ = isolate->global_handles()->Create(*wrapper); wrapper_ = isolate->global_handles()->Create(*wrapper);
std::unique_ptr<Utf16CharacterStream> stream( std::unique_ptr<Utf16CharacterStream> stream(
ScannerStream::For(isolate, wrapper_, shared_->StartPosition() - offset, ScannerStream::For(wrapper_, shared_->StartPosition() - offset,
shared_->EndPosition() - offset)); shared_->EndPosition() - offset));
parse_info_->set_character_stream(std::move(stream)); parse_info_->set_character_stream(std::move(stream));
} }
......
...@@ -25,9 +25,9 @@ bool ParseProgram(ParseInfo* info, Isolate* isolate) { ...@@ -25,9 +25,9 @@ bool ParseProgram(ParseInfo* info, Isolate* isolate) {
// Create a character stream for the parser. // Create a character stream for the parser.
Handle<String> source(String::cast(info->script()->source()), isolate); Handle<String> source(String::cast(info->script()->source()), isolate);
source = String::Flatten(isolate, source);
isolate->counters()->total_parse_size()->Increment(source->length()); isolate->counters()->total_parse_size()->Increment(source->length());
std::unique_ptr<Utf16CharacterStream> stream( std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(source));
ScannerStream::For(isolate, source));
info->set_character_stream(std::move(stream)); info->set_character_stream(std::move(stream));
Parser parser(info); Parser parser(info);
...@@ -60,10 +60,10 @@ bool ParseFunction(ParseInfo* info, Handle<SharedFunctionInfo> shared_info, ...@@ -60,10 +60,10 @@ bool ParseFunction(ParseInfo* info, Handle<SharedFunctionInfo> shared_info,
// Create a character stream for the parser. // Create a character stream for the parser.
Handle<String> source(String::cast(info->script()->source()), isolate); Handle<String> source(String::cast(info->script()->source()), isolate);
source = String::Flatten(isolate, source);
isolate->counters()->total_parse_size()->Increment(source->length()); isolate->counters()->total_parse_size()->Increment(source->length());
std::unique_ptr<Utf16CharacterStream> stream( std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
ScannerStream::For(isolate, source, shared_info->StartPosition(), source, shared_info->StartPosition(), shared_info->EndPosition()));
shared_info->EndPosition()));
info->set_character_stream(std::move(stream)); info->set_character_stream(std::move(stream));
VMState<PARSER> state(isolate); VMState<PARSER> state(isolate);
......
...@@ -19,335 +19,175 @@ namespace { ...@@ -19,335 +19,175 @@ namespace {
const unibrow::uchar kUtf8Bom = 0xFEFF; const unibrow::uchar kUtf8Bom = 0xFEFF;
} // namespace } // namespace
template <typename Char> // ----------------------------------------------------------------------------
struct HeapStringType; // BufferedUtf16CharacterStreams
//
template <> // A buffered character stream based on a random access character
struct HeapStringType<uint8_t> { // source (ReadBlock can be called with pos() pointing to any position,
typedef SeqOneByteString String; // even positions before the current).
}; class BufferedUtf16CharacterStream : public Utf16CharacterStream {
template <>
struct HeapStringType<uint16_t> {
typedef SeqTwoByteString String;
};
template <typename Char>
struct Range {
const Char* start;
const Char* end;
size_t length() { return static_cast<size_t>(end - start); }
bool empty() const { return start == end; }
bool unaligned_start() const {
return reinterpret_cast<intptr_t>(start) % 2 == 1;
}
};
// A Char stream backed by an on-heap SeqOneByteString or SeqTwoByteString.
template <typename Char>
class OnHeapStream {
public: public:
typedef typename HeapStringType<Char>::String String; BufferedUtf16CharacterStream();
OnHeapStream(Handle<String> string, size_t start_offset, size_t end) protected:
: string_(string), start_offset_(start_offset), length_(end) {} static const size_t kBufferSize = 512;
Range<Char> GetDataAt(size_t pos) { bool ReadBlock() override;
return {&string_->GetChars()[start_offset_ + Min(length_, pos)],
&string_->GetChars()[start_offset_ + length_]};
}
static const bool kCanAccessHeap = true; // FillBuffer should read up to kBufferSize characters at position and store
// them into buffer_[0..]. It returns the number of characters stored.
virtual size_t FillBuffer(size_t position) = 0;
private: // Fixed sized buffer that this class reads from.
Handle<String> string_; // The base class' buffer_start_ should always point to buffer_.
const size_t start_offset_; uc16 buffer_[kBufferSize];
const size_t length_;
}; };
// A Char stream backed by an off-heap ExternalOneByteString or BufferedUtf16CharacterStream::BufferedUtf16CharacterStream()
// ExternalTwoByteString. : Utf16CharacterStream(buffer_, buffer_, buffer_, 0) {}
template <typename Char>
class ExternalStringStream {
public:
ExternalStringStream(const Char* data, size_t end)
: data_(data), length_(end) {}
Range<Char> GetDataAt(size_t pos) { bool BufferedUtf16CharacterStream::ReadBlock() {
return {&data_[Min(length_, pos)], &data_[length_]}; DCHECK_EQ(buffer_start_, buffer_);
}
static const bool kCanAccessHeap = false; size_t position = pos();
buffer_pos_ = position;
buffer_cursor_ = buffer_;
buffer_end_ = buffer_ + FillBuffer(position);
DCHECK_EQ(pos(), position);
DCHECK_LE(buffer_end_, buffer_start_ + kBufferSize);
return buffer_cursor_ < buffer_end_;
}
private: // ----------------------------------------------------------------------------
const Char* const data_; // GenericStringUtf16CharacterStream.
const size_t length_; //
}; // A stream w/ a data source being a (flattened) Handle<String>.
// A Char stream backed by multiple source-stream provided off-heap chunks. class GenericStringUtf16CharacterStream : public BufferedUtf16CharacterStream {
template <typename Char>
class ChunkedStream {
public: public:
explicit ChunkedStream(ScriptCompiler::ExternalSourceStream* source, GenericStringUtf16CharacterStream(Handle<String> data, size_t start_position,
RuntimeCallStats* stats) size_t end_position);
: source_(source), stats_(stats) {
// Immediately fetch a first chunk upon creation.
const uint8_t* data = nullptr;
size_t length;
{
RuntimeCallTimerScope scope(stats_,
RuntimeCallCounterId::kGetMoreDataCallback);
length = source_->GetMoreData(&data);
}
chunks_.push_back({data, 0, length, false});
}
Range<Char> GetDataAt(size_t pos) {
Chunk chunk = FindChunk(pos);
size_t buffer_end = chunk.length();
size_t buffer_pos = Min(buffer_end, pos - chunk.position);
return {&chunk.data()[buffer_pos], &chunk.data()[buffer_end]};
}
~ChunkedStream() {
for (size_t i = 0; i < chunks_.size(); i++) {
delete[] chunks_[i].raw_data;
}
}
static const bool kCanAccessHeap = false;
private:
// A single chunk of Chars. There may be a lonely bytes at the start and end
// in case sizeof(Char) > 1. They just need to be ignored since additional
// chunks are added by FetchChunk that contain the full character.
// TODO(verwaest): Make sure that those characters are added by blink instead
// so we can get rid of this complexity here.
struct Chunk {
// A raw chunk of Chars possibly including a lonely start and/or a lonely
// end byte.
const uint8_t* const raw_data;
// The logical position of data() (possibly skipping a lonely start byte).
const size_t position;
// The length of the raw_data.
const size_t raw_length : sizeof(size_t) * 8 - 1;
// Tells us whether the first byte of raw_data is a lonely start byte and
// should be skipped because it's combined with a lonely end byte from the
// previous chunk.
const bool lonely_start : 1;
size_t end_position() const { return position + length(); }
// The chunk includes a lonely end byte if the chunk is 2-byte but has an
// uneven number of chars (possibly ignoring a lonely start byte that is
// merged with the lonely end byte of the previous chunk).
bool lonely_end() const {
return (raw_length - lonely_start) % sizeof(Char) == 1;
}
uint8_t lonely_end_byte() const {
DCHECK(lonely_end());
return raw_data[raw_length - 1];
}
size_t length() const {
return (raw_length - lonely_start) >> (sizeof(Char) - 1);
}
bool has_chars() const { return raw_length - lonely_start > 0; } bool can_access_heap() override { return true; }
const Char* data() const { protected:
return reinterpret_cast<const Char*>(raw_data + lonely_start); size_t FillBuffer(size_t position) override;
}
};
Chunk FindChunk(size_t position) {
// Walk forwards while the position is in front of the current chunk..
if (chunks_.back().position <= position) {
while (position >= chunks_.back().end_position() &&
chunks_.back().has_chars()) {
FetchChunk();
}
// Return if the final chunk's starting position is before the position.
if (chunks_.back().position <= position) return chunks_.back();
// Otherwise walk backwards to find the intermediate chunk added to
// support lonely bytes.
// TODO(verwaest): Remove once we don't need to support lonely bytes here
// anymore.
}
// Walk backwards.
for (auto reverse_it = chunks_.rbegin() + 1; reverse_it != chunks_.rend();
++reverse_it) {
if (reverse_it->position <= position) return *reverse_it;
}
UNREACHABLE();
}
void FetchChunk() {
DCHECK(!chunks_.empty());
const uint8_t* data = nullptr;
size_t length;
{
RuntimeCallTimerScope scope(stats_,
RuntimeCallCounterId::kGetMoreDataCallback);
length = source_->GetMoreData(&data);
}
const Chunk& last_chunk = chunks_.back(); Handle<String> string_;
bool lonely_start = last_chunk.lonely_end(); size_t length_;
DCHECK(last_chunk.has_chars()); };
size_t position = last_chunk.end_position(); GenericStringUtf16CharacterStream::GenericStringUtf16CharacterStream(
Handle<String> data, size_t start_position, size_t end_position)
: string_(data), length_(end_position) {
DCHECK_GE(end_position, start_position);
DCHECK_GE(static_cast<size_t>(string_->length()),
end_position - start_position);
buffer_pos_ = start_position;
}
if (lonely_start) { size_t GenericStringUtf16CharacterStream::FillBuffer(size_t from_pos) {
uint8_t* intermediate = NewArray<uint8_t>(2); if (from_pos >= length_) return 0;
intermediate[0] = last_chunk.lonely_end_byte();
intermediate[1] = length == 0 ? 0 : data[0];
chunks_.push_back({intermediate, position, 2, false});
position += 1;
}
chunks_.push_back({data, position, length, lonely_start}); size_t length = i::Min(kBufferSize, length_ - from_pos);
} String::WriteToFlat<uc16>(*string_, buffer_, static_cast<int>(from_pos),
static_cast<int>(from_pos + length));
return length;
}
std::vector<struct Chunk> chunks_; // ----------------------------------------------------------------------------
ScriptCompiler::ExternalSourceStream* source_; // ExternalTwoByteStringUtf16CharacterStream.
RuntimeCallStats* stats_; //
}; // A stream whose data source is a Handle<ExternalTwoByteString>. It avoids
// all data copying.
// Provides a buffered utf-16 view on the bytes from the underlying ByteStream. class ExternalTwoByteStringUtf16CharacterStream : public Utf16CharacterStream {
// Chars are buffered if either the underlying stream isn't utf-16 or the
// underlying utf-16 stream might move (is on-heap).
template <typename Char, template <typename T> class ByteStream>
class BufferedCharacterStream : public Utf16CharacterStream {
public: public:
template <class... TArgs> ExternalTwoByteStringUtf16CharacterStream(Handle<ExternalTwoByteString> data,
BufferedCharacterStream(size_t pos, TArgs... args) : byte_stream_(args...) { size_t start_position,
buffer_pos_ = pos; size_t end_position);
ReadBlock();
}
protected:
bool ReadBlock() override {
size_t position = pos();
buffer_pos_ = position;
buffer_start_ = &buffer_[0];
buffer_cursor_ = buffer_start_;
Range<Char> range = byte_stream_.GetDataAt(position); bool can_access_heap() override { return false; }
if (range.empty()) {
buffer_end_ = buffer_start_;
return false;
}
size_t length = Min(kBufferSize, range.length());
i::CopyCharsUnsigned(buffer_, range.start, length);
buffer_end_ = &buffer_[length];
return true;
}
bool can_access_heap() override {
return ByteStream<uint16_t>::kCanAccessHeap;
}
private: private:
static const size_t kBufferSize = 512; bool ReadBlock() override;
uc16 buffer_[kBufferSize];
ByteStream<Char> byte_stream_; const uc16* raw_data_; // Pointer to the actual array of characters.
size_t start_pos_;
size_t end_pos_;
}; };
// Provides a (partially) unbuffered utf-16 view on the bytes from the ExternalTwoByteStringUtf16CharacterStream::
// underlying ByteStream. It is only partially unbuffered when running on MIPS ExternalTwoByteStringUtf16CharacterStream(
// due to lonely start bytes making chunks unaligned. In that case, unaligned Handle<ExternalTwoByteString> data, size_t start_position,
// chars in a chunk (due to lonely start) are locally buffered. size_t end_position)
template <template <typename T> class ByteStream> : raw_data_(data->GetTwoByteData(static_cast<int>(start_position))),
class UnbufferedCharacterStream : public Utf16CharacterStream { start_pos_(start_position),
public: end_pos_(end_position) {
template <class... TArgs> buffer_start_ = raw_data_;
UnbufferedCharacterStream(size_t pos, TArgs... args) : byte_stream_(args...) { buffer_cursor_ = raw_data_;
DCHECK(!ByteStream<uint16_t>::kCanAccessHeap); buffer_end_ = raw_data_ + (end_pos_ - start_pos_);
buffer_pos_ = pos; buffer_pos_ = start_pos_;
ReadBlock(); }
}
protected: bool ExternalTwoByteStringUtf16CharacterStream::ReadBlock() {
bool ReadBlock() override {
size_t position = pos(); size_t position = pos();
bool have_data = start_pos_ <= position && position < end_pos_;
if (have_data) {
buffer_pos_ = start_pos_;
buffer_cursor_ = raw_data_ + (position - start_pos_),
buffer_end_ = raw_data_ + (end_pos_ - start_pos_);
} else {
buffer_pos_ = position; buffer_pos_ = position;
Range<uint16_t> range = byte_stream_.GetDataAt(position); buffer_cursor_ = raw_data_;
buffer_start_ = range.start; buffer_end_ = raw_data_;
buffer_end_ = range.end;
buffer_cursor_ = buffer_start_;
if (range.empty()) return false;
// TODO(verwaest): Make sure that this cannot happen by dealing with lonely
// bytes on the blink side.
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
// Buffer anyway in case the chunk is unaligned due to a lonely start.
if (range.unaligned_start()) {
size_t length = Min(kBufferSize, range.length());
i::CopyCharsUnsigned(buffer_, buffer_start_, length);
buffer_start_ = &buffer_[0];
buffer_cursor_ = buffer_start_;
buffer_end_ = &buffer_[length];
} }
#endif return have_data;
DCHECK_LE(buffer_start_, buffer_end_); }
return true;
}
bool can_access_heap() override { return false; }
private:
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
static const size_t kBufferSize = 512;
uc16 buffer_[kBufferSize];
#endif
ByteStream<uint16_t> byte_stream_;
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// BufferedUtf16CharacterStreams // ExternalOneByteStringUtf16CharacterStream
//
// A buffered character stream based on a random access character
// source (ReadBlock can be called with pos() pointing to any position,
// even positions before the current).
// //
// TODO(verwaest): Remove together with Utf8 external streaming streams. // A stream whose data source is a Handle<ExternalOneByteString>.
class BufferedUtf16CharacterStream : public Utf16CharacterStream {
class ExternalOneByteStringUtf16CharacterStream
: public BufferedUtf16CharacterStream {
public: public:
BufferedUtf16CharacterStream(); ExternalOneByteStringUtf16CharacterStream(Handle<ExternalOneByteString> data,
size_t start_position,
size_t end_position);
protected: // For testing:
static const size_t kBufferSize = 512; ExternalOneByteStringUtf16CharacterStream(const char* data, size_t length);
bool ReadBlock() override; bool can_access_heap() override { return false; }
// FillBuffer should read up to kBufferSize characters at position and store protected:
// them into buffer_[0..]. It returns the number of characters stored. size_t FillBuffer(size_t position) override;
virtual size_t FillBuffer(size_t position) = 0;
// Fixed sized buffer that this class reads from. const uint8_t* raw_data_; // Pointer to the actual array of characters.
// The base class' buffer_start_ should always point to buffer_. size_t length_;
uc16 buffer_[kBufferSize];
}; };
BufferedUtf16CharacterStream::BufferedUtf16CharacterStream() ExternalOneByteStringUtf16CharacterStream::
: Utf16CharacterStream(buffer_, buffer_, buffer_, 0) {} ExternalOneByteStringUtf16CharacterStream(
Handle<ExternalOneByteString> data, size_t start_position,
size_t end_position)
: raw_data_(data->GetChars()), length_(end_position) {
DCHECK(end_position >= start_position);
buffer_pos_ = start_position;
}
bool BufferedUtf16CharacterStream::ReadBlock() { ExternalOneByteStringUtf16CharacterStream::
DCHECK_EQ(buffer_start_, buffer_); ExternalOneByteStringUtf16CharacterStream(const char* data, size_t length)
: raw_data_(reinterpret_cast<const uint8_t*>(data)), length_(length) {}
size_t position = pos(); size_t ExternalOneByteStringUtf16CharacterStream::FillBuffer(size_t from_pos) {
buffer_pos_ = position; if (from_pos >= length_) return 0;
buffer_cursor_ = buffer_;
buffer_end_ = buffer_ + FillBuffer(position); size_t length = Min(kBufferSize, length_ - from_pos);
DCHECK_EQ(pos(), position); i::CopyCharsUnsigned(buffer_, raw_data_ + from_pos, length);
DCHECK_LE(buffer_end_, buffer_start_ + kBufferSize); return length;
return buffer_cursor_ < buffer_end_;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
...@@ -357,9 +197,6 @@ bool BufferedUtf16CharacterStream::ReadBlock() { ...@@ -357,9 +197,6 @@ bool BufferedUtf16CharacterStream::ReadBlock() {
// may 'cut' arbitrarily into utf-8 characters. Also, seeking to a given // may 'cut' arbitrarily into utf-8 characters. Also, seeking to a given
// character position is tricky because the byte position cannot be dericed // character position is tricky because the byte position cannot be dericed
// from the character position. // from the character position.
//
// TODO(verwaest): Decode utf8 chunks into utf16 chunks on the blink side
// instead so we don't need to buffer.
class Utf8ExternalStreamingStream : public BufferedUtf16CharacterStream { class Utf8ExternalStreamingStream : public BufferedUtf16CharacterStream {
public: public:
...@@ -631,47 +468,393 @@ size_t Utf8ExternalStreamingStream::FillBuffer(size_t position) { ...@@ -631,47 +468,393 @@ size_t Utf8ExternalStreamingStream::FillBuffer(size_t position) {
return buffer_end_ - buffer_cursor_; return buffer_end_ - buffer_cursor_;
} }
// ----------------------------------------------------------------------------
// Chunks - helper for One- + TwoByteExternalStreamingStream
namespace {
struct Chunk {
const uint8_t* data;
size_t byte_length;
size_t byte_pos;
};
typedef std::vector<struct Chunk> Chunks;
void DeleteChunks(Chunks& chunks) {
for (size_t i = 0; i < chunks.size(); i++) delete[] chunks[i].data;
}
// Return the chunk index for the chunk containing position.
// If position is behind the end of the stream, the index of the last,
// zero-length chunk is returned.
size_t FindChunk(Chunks& chunks, ScriptCompiler::ExternalSourceStream* source,
size_t position, RuntimeCallStats* stats) {
size_t end_pos =
chunks.empty() ? 0 : (chunks.back().byte_pos + chunks.back().byte_length);
// Get more data if needed. We usually won't enter the loop body.
bool out_of_data = !chunks.empty() && chunks.back().byte_length == 0;
{
RuntimeCallTimerScope scope(stats,
RuntimeCallCounterId::kGetMoreDataCallback);
while (!out_of_data && end_pos <= position + 1) {
const uint8_t* chunk = nullptr;
size_t len = source->GetMoreData(&chunk);
chunks.push_back({chunk, len, end_pos});
end_pos += len;
out_of_data = (len == 0);
}
}
// Here, we should always have at least one chunk, and we either have the
// chunk we were looking for, or we're out of data. Also, out_of_data and
// end_pos are current (and designate whether we have exhausted the stream,
// and the length of data received so far, respectively).
DCHECK(!chunks.empty());
DCHECK_EQ(end_pos, chunks.back().byte_pos + chunks.back().byte_length);
DCHECK_EQ(out_of_data, chunks.back().byte_length == 0);
DCHECK(position < end_pos || out_of_data);
// Edge case: position is behind the end of stream: Return the last (length 0)
// chunk to indicate the end of the stream.
if (position >= end_pos) {
DCHECK(out_of_data);
return chunks.size() - 1;
}
// We almost always 'stream', meaning we want data from the last chunk, so
// let's look at chunks back-to-front.
size_t chunk_no = chunks.size() - 1;
while (chunks[chunk_no].byte_pos > position) {
DCHECK_NE(chunk_no, 0u);
chunk_no--;
}
DCHECK_LE(chunks[chunk_no].byte_pos, position);
DCHECK_LT(position, chunks[chunk_no].byte_pos + chunks[chunk_no].byte_length);
return chunk_no;
}
} // anonymous namespace
// ----------------------------------------------------------------------------
// OneByteExternalStreamingStream
//
// A stream of latin-1 encoded, chunked data.
class OneByteExternalStreamingStream : public BufferedUtf16CharacterStream {
public:
explicit OneByteExternalStreamingStream(
ScriptCompiler::ExternalSourceStream* source, RuntimeCallStats* stats)
: source_(source), stats_(stats) {}
~OneByteExternalStreamingStream() override { DeleteChunks(chunks_); }
bool can_access_heap() override { return false; }
protected:
size_t FillBuffer(size_t position) override;
private:
Chunks chunks_;
ScriptCompiler::ExternalSourceStream* source_;
RuntimeCallStats* stats_;
};
size_t OneByteExternalStreamingStream::FillBuffer(size_t position) {
const Chunk& chunk = chunks_[FindChunk(chunks_, source_, position, stats_)];
if (chunk.byte_length == 0) return 0;
size_t start_pos = position - chunk.byte_pos;
size_t len = i::Min(kBufferSize, chunk.byte_length - start_pos);
i::CopyCharsUnsigned(buffer_, chunk.data + start_pos, len);
return len;
}
#if !(V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64)
// ----------------------------------------------------------------------------
// TwoByteExternalStreamingStream
//
// A stream of ucs-2 data, delivered in chunks. Chunks may be 'cut' into the
// middle of characters (or even contain only one byte), which adds a bit
// of complexity. This stream avoid all data copying, except for characters
// that cross chunk boundaries.
class TwoByteExternalStreamingStream : public Utf16CharacterStream {
public:
explicit TwoByteExternalStreamingStream(
ScriptCompiler::ExternalSourceStream* source, RuntimeCallStats* stats);
~TwoByteExternalStreamingStream() override;
bool can_access_heap() override { return false; }
protected:
bool ReadBlock() override;
Chunks chunks_;
ScriptCompiler::ExternalSourceStream* source_;
RuntimeCallStats* stats_;
uc16 one_char_buffer_;
};
TwoByteExternalStreamingStream::TwoByteExternalStreamingStream(
ScriptCompiler::ExternalSourceStream* source, RuntimeCallStats* stats)
: Utf16CharacterStream(&one_char_buffer_, &one_char_buffer_,
&one_char_buffer_, 0),
source_(source),
stats_(stats),
one_char_buffer_(0) {}
TwoByteExternalStreamingStream::~TwoByteExternalStreamingStream() {
DeleteChunks(chunks_);
}
bool TwoByteExternalStreamingStream::ReadBlock() {
size_t position = pos();
// We'll search for the 2nd byte of our character, to make sure we
// have enough data for at least one character.
size_t chunk_no = FindChunk(chunks_, source_, 2 * position + 1, stats_);
// Out of data? Return 0.
if (chunks_[chunk_no].byte_length == 0) {
buffer_pos_ = position;
buffer_cursor_ = buffer_start_;
buffer_end_ = buffer_start_;
return false;
}
Chunk& current = chunks_[chunk_no];
// Annoying edge case: Chunks may not be 2-byte aligned, meaning that a
// character may be split between the previous and the current chunk.
// If we find such a lonely byte at the beginning of the chunk, we'll use
// one_char_buffer_ to hold the full character.
bool lonely_byte = (chunks_[chunk_no].byte_pos == (2 * position + 1));
if (lonely_byte) {
DCHECK_NE(chunk_no, 0u);
Chunk& previous_chunk = chunks_[chunk_no - 1];
#ifdef V8_TARGET_BIG_ENDIAN
uc16 character = current.data[0] |
previous_chunk.data[previous_chunk.byte_length - 1] << 8;
#else
uc16 character = previous_chunk.data[previous_chunk.byte_length - 1] |
current.data[0] << 8;
#endif
one_char_buffer_ = character;
buffer_pos_ = position;
buffer_start_ = &one_char_buffer_;
buffer_cursor_ = &one_char_buffer_;
buffer_end_ = &one_char_buffer_ + 1;
return true;
}
// Common case: character is in current chunk.
DCHECK_LE(current.byte_pos, 2 * position);
DCHECK_LT(2 * position + 1, current.byte_pos + current.byte_length);
// Determine # of full ucs-2 chars in stream, and whether we started on an odd
// byte boundary.
bool odd_start = (current.byte_pos % 2) == 1;
size_t number_chars = (current.byte_length - odd_start) / 2;
// Point the buffer_*_ members into the current chunk and set buffer_cursor_
// to point to position. Be careful when converting the byte positions (in
// Chunk) to the ucs-2 character positions (in buffer_*_ members).
buffer_start_ = reinterpret_cast<const uint16_t*>(current.data + odd_start);
buffer_end_ = buffer_start_ + number_chars;
buffer_pos_ = (current.byte_pos + odd_start) / 2;
buffer_cursor_ = buffer_start_ + (position - buffer_pos_);
DCHECK_EQ(position, pos());
return true;
}
#else
// ----------------------------------------------------------------------------
// TwoByteExternalBufferedStream
//
// This class is made specifically to address unaligned access to 16-bit data
// in MIPS and ARM architectures. It replaces class
// TwoByteExternalStreamingStream which in some cases does have unaligned
// accesse to 16-bit data
class TwoByteExternalBufferedStream : public Utf16CharacterStream {
public:
explicit TwoByteExternalBufferedStream(
ScriptCompiler::ExternalSourceStream* source, RuntimeCallStats* stats);
~TwoByteExternalBufferedStream();
bool can_access_heap() override { return false; }
protected:
static const size_t kBufferSize = 512;
bool ReadBlock() override;
// FillBuffer should read up to kBufferSize characters at position and store
// them into buffer_[0..]. It returns the number of characters stored.
size_t FillBuffer(size_t position, size_t chunk_no);
// Fixed sized buffer that this class reads from.
// The base class' buffer_start_ should always point to buffer_.
uc16 buffer_[kBufferSize];
Chunks chunks_;
ScriptCompiler::ExternalSourceStream* source_;
RuntimeCallStats* stats_;
};
TwoByteExternalBufferedStream::TwoByteExternalBufferedStream(
ScriptCompiler::ExternalSourceStream* source, RuntimeCallStats* stats)
: Utf16CharacterStream(buffer_, buffer_, buffer_, 0),
source_(source),
stats_(stats) {}
TwoByteExternalBufferedStream::~TwoByteExternalBufferedStream() {
DeleteChunks(chunks_);
}
bool TwoByteExternalBufferedStream::ReadBlock() {
size_t position = pos();
// Find chunk in which the position belongs
size_t chunk_no = FindChunk(chunks_, source_, 2 * position + 1, stats_);
// Out of data? Return 0.
if (chunks_[chunk_no].byte_length == 0) {
buffer_pos_ = position;
buffer_cursor_ = buffer_start_;
buffer_end_ = buffer_start_;
return false;
}
Chunk& current = chunks_[chunk_no];
bool odd_start = current.byte_pos % 2;
// Common case: character is in current chunk.
DCHECK_LE(current.byte_pos, 2 * position + odd_start);
DCHECK_LT(2 * position + 1, current.byte_pos + current.byte_length);
// If character starts on odd address copy text in buffer so there is always
// aligned access to characters. This is important on MIPS and ARM
// architectures. Otherwise read characters from memory directly.
if (!odd_start) {
buffer_start_ = reinterpret_cast<const uint16_t*>(current.data);
size_t number_chars = current.byte_length / 2;
buffer_end_ = buffer_start_ + number_chars;
buffer_pos_ = current.byte_pos / 2;
buffer_cursor_ = buffer_start_ + (position - buffer_pos_);
DCHECK_EQ(position, pos());
return true;
} else {
buffer_start_ = buffer_;
buffer_pos_ = position;
buffer_cursor_ = buffer_;
buffer_end_ = buffer_ + FillBuffer(position, chunk_no);
DCHECK_EQ(pos(), position);
DCHECK_LE(buffer_end_, buffer_start_ + kBufferSize);
return buffer_cursor_ < buffer_end_;
}
}
size_t TwoByteExternalBufferedStream::FillBuffer(size_t position,
size_t chunk_no) {
DCHECK_EQ(chunks_[chunk_no].byte_pos % 2, 1u);
bool odd_start = true;
// Align buffer_pos_ to the size of the buffer.
{
size_t new_pos = position / kBufferSize * kBufferSize;
if (new_pos != position) {
chunk_no = FindChunk(chunks_, source_, 2 * new_pos + 1, stats_);
buffer_pos_ = new_pos;
buffer_cursor_ = buffer_start_ + (position - buffer_pos_);
position = new_pos;
odd_start = chunks_[chunk_no].byte_pos % 2;
}
}
Chunk* current = &chunks_[chunk_no];
// Annoying edge case: Chunks may not be 2-byte aligned, meaning that a
// character may be split between the previous and the current chunk.
// If we find such a lonely byte at the beginning of the chunk, we'll copy
// it to the first byte in buffer_.
size_t totalLength = 0;
bool lonely_byte = (current->byte_pos == (2 * position + 1));
if (lonely_byte) {
DCHECK_NE(chunk_no, 0u);
Chunk& previous_chunk = chunks_[chunk_no - 1];
*reinterpret_cast<uint8_t*>(buffer_) =
previous_chunk.data[previous_chunk.byte_length - 1];
totalLength++;
}
// Common case: character is in current chunk.
DCHECK_LE(current->byte_pos, 2 * position + odd_start);
DCHECK_LT(2 * position + 1, current->byte_pos + current->byte_length);
// Copy characters from current chunk starting from chunk_pos to the end of
// buffer or chunk.
size_t chunk_pos = position - current->byte_pos / 2;
size_t start_offset = odd_start && chunk_pos != 0;
size_t bytes_to_move =
i::Min(2 * kBufferSize - lonely_byte,
current->byte_length - 2 * chunk_pos + start_offset);
i::MemMove(reinterpret_cast<uint8_t*>(buffer_) + lonely_byte,
current->data + 2 * chunk_pos - start_offset, bytes_to_move);
// Fill up the rest of the buffer if there is space and data left.
totalLength += bytes_to_move;
position = (current->byte_pos + current->byte_length) / 2;
if (position - buffer_pos_ < kBufferSize) {
chunk_no = FindChunk(chunks_, source_, 2 * position + 1, stats_);
current = &chunks_[chunk_no];
odd_start = current->byte_pos % 2;
bytes_to_move = i::Min(2 * kBufferSize - totalLength, current->byte_length);
while (bytes_to_move) {
// Common case: character is in current chunk.
DCHECK_LE(current->byte_pos, 2 * position + odd_start);
DCHECK_LT(2 * position + 1, current->byte_pos + current->byte_length);
i::MemMove(reinterpret_cast<uint8_t*>(buffer_) + totalLength,
current->data, bytes_to_move);
totalLength += bytes_to_move;
position = (current->byte_pos + current->byte_length) / 2;
chunk_no = FindChunk(chunks_, source_, 2 * position + 1, stats_);
current = &chunks_[chunk_no];
odd_start = current->byte_pos % 2;
bytes_to_move =
i::Min(2 * kBufferSize - totalLength, current->byte_length);
}
}
return totalLength / 2;
}
#endif
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ScannerStream: Create stream instances. // ScannerStream: Create stream instances.
Utf16CharacterStream* ScannerStream::For(Isolate* isolate, Utf16CharacterStream* ScannerStream::For(Handle<String> data) {
Handle<String> data) { return ScannerStream::For(data, 0, data->length());
return ScannerStream::For(isolate, data, 0, data->length());
} }
Utf16CharacterStream* ScannerStream::For(Isolate* isolate, Handle<String> data, Utf16CharacterStream* ScannerStream::For(Handle<String> data, int start_pos,
int start_pos, int end_pos) { int end_pos) {
DCHECK_GE(start_pos, 0); DCHECK_GE(start_pos, 0);
DCHECK_LE(start_pos, end_pos); DCHECK_LE(start_pos, end_pos);
DCHECK_LE(end_pos, data->length()); DCHECK_LE(end_pos, data->length());
size_t start_offset = 0;
if (data->IsSlicedString()) {
SlicedString* string = SlicedString::cast(*data);
start_offset = string->offset();
data = handle(string->parent(), string->GetIsolate());
} else {
data = String::Flatten(data->GetIsolate(), data);
}
if (data->IsExternalOneByteString()) { if (data->IsExternalOneByteString()) {
return new BufferedCharacterStream<uint8_t, ExternalStringStream>( return new ExternalOneByteStringUtf16CharacterStream(
static_cast<size_t>(start_pos), Handle<ExternalOneByteString>::cast(data),
ExternalOneByteString::cast(*data)->GetChars() + start_offset, static_cast<size_t>(start_pos), static_cast<size_t>(end_pos));
static_cast<size_t>(end_pos));
} else if (data->IsExternalTwoByteString()) { } else if (data->IsExternalTwoByteString()) {
return new UnbufferedCharacterStream<ExternalStringStream>( return new ExternalTwoByteStringUtf16CharacterStream(
static_cast<size_t>(start_pos), Handle<ExternalTwoByteString>::cast(data),
ExternalTwoByteString::cast(*data)->GetChars() + start_offset, static_cast<size_t>(start_pos), static_cast<size_t>(end_pos));
static_cast<size_t>(end_pos));
} else if (data->IsSeqOneByteString()) {
return new BufferedCharacterStream<uint8_t, OnHeapStream>(
static_cast<size_t>(start_pos), Handle<SeqOneByteString>::cast(data),
start_offset, static_cast<size_t>(end_pos));
} else if (data->IsSeqTwoByteString()) {
return new BufferedCharacterStream<uint16_t, OnHeapStream>(
static_cast<size_t>(start_pos), Handle<SeqTwoByteString>::cast(data),
start_offset, static_cast<size_t>(end_pos));
} else { } else {
UNREACHABLE(); // TODO(vogelheim): Maybe call data.Flatten() first?
return new GenericStringUtf16CharacterStream(
data, static_cast<size_t>(start_pos), static_cast<size_t>(end_pos));
} }
} }
...@@ -683,9 +866,7 @@ std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting( ...@@ -683,9 +866,7 @@ std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting( std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
const char* data, size_t length) { const char* data, size_t length) {
return std::unique_ptr<Utf16CharacterStream>( return std::unique_ptr<Utf16CharacterStream>(
new BufferedCharacterStream<uint8_t, ExternalStringStream>( new ExternalOneByteStringUtf16CharacterStream(data, length));
static_cast<size_t>(0), reinterpret_cast<const uint8_t*>(data),
static_cast<size_t>(length)));
} }
Utf16CharacterStream* ScannerStream::For( Utf16CharacterStream* ScannerStream::For(
...@@ -694,15 +875,18 @@ Utf16CharacterStream* ScannerStream::For( ...@@ -694,15 +875,18 @@ Utf16CharacterStream* ScannerStream::For(
RuntimeCallStats* stats) { RuntimeCallStats* stats) {
switch (encoding) { switch (encoding) {
case v8::ScriptCompiler::StreamedSource::TWO_BYTE: case v8::ScriptCompiler::StreamedSource::TWO_BYTE:
return new UnbufferedCharacterStream<ChunkedStream>( #if !(V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64)
static_cast<size_t>(0), source_stream, stats); return new TwoByteExternalStreamingStream(source_stream, stats);
#else
return new TwoByteExternalBufferedStream(source_stream, stats);
#endif
case v8::ScriptCompiler::StreamedSource::ONE_BYTE: case v8::ScriptCompiler::StreamedSource::ONE_BYTE:
return new BufferedCharacterStream<uint8_t, ChunkedStream>( return new OneByteExternalStreamingStream(source_stream, stats);
static_cast<size_t>(0), source_stream, stats);
case v8::ScriptCompiler::StreamedSource::UTF8: case v8::ScriptCompiler::StreamedSource::UTF8:
return new Utf8ExternalStreamingStream(source_stream, stats); return new Utf8ExternalStreamingStream(source_stream, stats);
} }
UNREACHABLE(); UNREACHABLE();
return nullptr;
} }
} // namespace internal } // namespace internal
......
...@@ -19,9 +19,9 @@ class String; ...@@ -19,9 +19,9 @@ class String;
class V8_EXPORT_PRIVATE ScannerStream { class V8_EXPORT_PRIVATE ScannerStream {
public: public:
static Utf16CharacterStream* For(Isolate* isolate, Handle<String> data); static Utf16CharacterStream* For(Handle<String> data);
static Utf16CharacterStream* For(Isolate* isolate, Handle<String> data, static Utf16CharacterStream* For(Handle<String> data, int start_pos,
int start_pos, int end_pos); int end_pos);
static Utf16CharacterStream* For( static Utf16CharacterStream* For(
ScriptCompiler::ExternalSourceStream* source_stream, ScriptCompiler::ExternalSourceStream* source_stream,
ScriptCompiler::StreamedSource::Encoding encoding, ScriptCompiler::StreamedSource::Encoding encoding,
......
...@@ -315,7 +315,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length, ...@@ -315,7 +315,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length,
i::Handle<i::String> uc16_string( i::Handle<i::String> uc16_string(
factory->NewExternalStringFromTwoByte(&resource).ToHandleChecked()); factory->NewExternalStringFromTwoByte(&resource).ToHandleChecked());
std::unique_ptr<i::Utf16CharacterStream> uc16_stream( std::unique_ptr<i::Utf16CharacterStream> uc16_stream(
i::ScannerStream::For(isolate, uc16_string, start, end)); i::ScannerStream::For(uc16_string, start, end));
TestCharacterStream(one_byte_source, uc16_stream.get(), length, start, end); TestCharacterStream(one_byte_source, uc16_stream.get(), length, start, end);
// This avoids the GC from trying to free a stack allocated resource. // This avoids the GC from trying to free a stack allocated resource.
...@@ -335,7 +335,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length, ...@@ -335,7 +335,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length,
factory->NewExternalStringFromOneByte(&one_byte_resource) factory->NewExternalStringFromOneByte(&one_byte_resource)
.ToHandleChecked()); .ToHandleChecked());
std::unique_ptr<i::Utf16CharacterStream> one_byte_stream( std::unique_ptr<i::Utf16CharacterStream> one_byte_stream(
i::ScannerStream::For(isolate, ext_one_byte_string, start, end)); i::ScannerStream::For(ext_one_byte_string, start, end));
TestCharacterStream(one_byte_source, one_byte_stream.get(), length, start, TestCharacterStream(one_byte_source, one_byte_stream.get(), length, start,
end); end);
// This avoids the GC from trying to free a stack allocated resource. // This avoids the GC from trying to free a stack allocated resource.
...@@ -347,7 +347,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length, ...@@ -347,7 +347,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length,
// 1-byte generic i::String // 1-byte generic i::String
{ {
std::unique_ptr<i::Utf16CharacterStream> string_stream( std::unique_ptr<i::Utf16CharacterStream> string_stream(
i::ScannerStream::For(isolate, one_byte_string, start, end)); i::ScannerStream::For(one_byte_string, start, end));
TestCharacterStream(one_byte_source, string_stream.get(), length, start, TestCharacterStream(one_byte_source, string_stream.get(), length, start,
end); end);
} }
...@@ -357,7 +357,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length, ...@@ -357,7 +357,7 @@ void TestCharacterStreams(const char* one_byte_source, unsigned length,
i::Handle<i::String> two_byte_string = i::Handle<i::String> two_byte_string =
factory->NewStringFromTwoByte(two_byte_vector).ToHandleChecked(); factory->NewStringFromTwoByte(two_byte_vector).ToHandleChecked();
std::unique_ptr<i::Utf16CharacterStream> two_byte_string_stream( std::unique_ptr<i::Utf16CharacterStream> two_byte_string_stream(
i::ScannerStream::For(isolate, two_byte_string, start, end)); i::ScannerStream::For(two_byte_string, start, end));
TestCharacterStream(one_byte_source, two_byte_string_stream.get(), length, TestCharacterStream(one_byte_source, two_byte_string_stream.get(), length,
start, end); start, end);
} }
......
...@@ -1174,7 +1174,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source, ...@@ -1174,7 +1174,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
if (test_preparser) { if (test_preparser) {
i::Scanner scanner(isolate->unicode_cache()); i::Scanner scanner(isolate->unicode_cache());
std::unique_ptr<i::Utf16CharacterStream> stream( std::unique_ptr<i::Utf16CharacterStream> stream(
i::ScannerStream::For(isolate, source)); i::ScannerStream::For(source));
i::Zone zone(CcTest::i_isolate()->allocator(), ZONE_NAME); i::Zone zone(CcTest::i_isolate()->allocator(), ZONE_NAME);
i::AstValueFactory ast_value_factory( i::AstValueFactory ast_value_factory(
&zone, CcTest::i_isolate()->ast_string_constants(), &zone, CcTest::i_isolate()->ast_string_constants(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment