From: dcarney@chromium.org Date: Thu, 6 Jun 2013 13:16:52 +0000 (+0000) Subject: add function to test whether string contents are definitely one byte X-Git-Tag: upstream/4.7.83~13985 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6f5d899248e92d2b0f5d54d65d911a99578f7672;p=platform%2Fupstream%2Fv8.git add function to test whether string contents are definitely one byte R=yangguo@chromium.org BUG= Review URL: https://codereview.chromium.org/16530003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14976 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index 5526705..7a38c1e 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1524,11 +1524,19 @@ class V8EXPORT String : public Primitive { V8_DEPRECATED(V8_INLINE(bool MayContainNonAscii()) const) { return true; } /** - * Returns whether this string contains only one byte data. + * Returns whether this string is known to contain only one byte data. + * Does not read the string. + * False negatives are possible. */ bool IsOneByte() const; /** + * Returns whether this string contain only one byte data. + * Will read the entire string in some cases. + */ + bool ContainsOnlyOneByte() const; + + /** * Write the contents of the string to an external buffer. * If no arguments are given, expects the buffer to be large * enough to hold the entire string and NULL terminator. Copies diff --git a/src/api.cc b/src/api.cc index b2dc2e1..4cdd77e 100644 --- a/src/api.cc +++ b/src/api.cc @@ -4304,6 +4304,85 @@ bool String::IsOneByte() const { } +class ContainsOnlyOneByteHelper { + public: + ContainsOnlyOneByteHelper() : is_one_byte_(true) {} + bool Check(i::String* string) { + i::ConsString* cons_string = i::String::VisitFlat(this, string, 0); + if (cons_string == NULL) return is_one_byte_; + return CheckCons(cons_string); + } + void VisitOneByteString(const uint8_t* chars, int length) { + // Nothing to do. + } + // TODO(dcarney): do word aligned read. + void VisitTwoByteString(const uint16_t* chars, int length) { + // Check whole string without breaking. + uint16_t total = 0; + for (int i = 0; i < length; i++) { + total |= chars[i] >> 8; + } + if (total != 0) is_one_byte_ = false; + } + + private: + bool CheckCons(i::ConsString* cons_string) { + while (true) { + // Check left side if flat. + i::String* left = cons_string->first(); + i::ConsString* left_as_cons = + i::String::VisitFlat(this, left, 0); + if (!is_one_byte_) return false; + // Check right side if flat. + i::String* right = cons_string->second(); + i::ConsString* right_as_cons = + i::String::VisitFlat(this, right, 0); + if (!is_one_byte_) return false; + // Standard recurse/iterate trick. + if (left_as_cons != NULL && right_as_cons != NULL) { + if (left->length() < right->length()) { + CheckCons(left_as_cons); + cons_string = right_as_cons; + } else { + CheckCons(right_as_cons); + cons_string = left_as_cons; + } + // Check fast return. + if (!is_one_byte_) return false; + continue; + } + // Descend left in place. + if (left_as_cons != NULL) { + cons_string = left_as_cons; + continue; + } + // Descend right in place. + if (right_as_cons != NULL) { + cons_string = right_as_cons; + continue; + } + // Terminate. + break; + } + return is_one_byte_; + } + bool is_one_byte_; + DISALLOW_COPY_AND_ASSIGN(ContainsOnlyOneByteHelper); +}; + + +bool String::ContainsOnlyOneByte() const { + i::Handle str = Utils::OpenHandle(this); + if (IsDeadCheck(str->GetIsolate(), + "v8::String::ContainsOnlyOneByte()")) { + return false; + } + if (str->HasOnlyOneByteChars()) return true; + ContainsOnlyOneByteHelper helper; + return helper.Check(*str); +} + + class Utf8LengthHelper : public i::AllStatic { public: enum State { diff --git a/src/objects.cc b/src/objects.cc index ca1c650..24c60ec 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1205,6 +1205,11 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { if (FLAG_enable_slow_asserts) { // Assert that the resource and the string are equivalent. ASSERT(static_cast(this->length()) == resource->length()); + if (this->IsTwoByteRepresentation()) { + ScopedVector smart_chars(this->length()); + String::WriteToFlat(this, smart_chars.start(), 0, this->length()); + ASSERT(String::IsOneByte(smart_chars.start(), this->length())); + } ScopedVector smart_chars(this->length()); String::WriteToFlat(this, smart_chars.start(), 0, this->length()); ASSERT(memcmp(smart_chars.start(), diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index bc72e37..d514b1e 100755 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -16881,6 +16881,51 @@ THREADED_TEST(TwoByteStringInAsciiCons) { } +TEST(ContainsOnlyOneByte) { + v8::V8::Initialize(); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope scope(isolate); + // Make a buffer long enough that it won't automatically be converted. + const int length = 200; + i::SmartArrayPointer string_contents(new uint16_t[length]); + // Set to contain only one byte. + for (int i = 0; i < length-1; i++) { + string_contents[i] = 0x41; + } + string_contents[length-1] = 0; + // Simple case. + Handle string; + string = String::NewExternal(new TestResource(*string_contents)); + CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); + // Counter example. + string = String::NewFromTwoByte(isolate, *string_contents); + CHECK(string->IsOneByte() && string->ContainsOnlyOneByte()); + // Test left right and balanced cons strings. + Handle base = String::NewFromUtf8(isolate, "a"); + Handle left = base; + Handle right = base; + for (int i = 0; i < 1000; i++) { + left = String::Concat(base, left); + right = String::Concat(right, base); + } + Handle balanced = String::Concat(left, base); + balanced = String::Concat(balanced, right); + Handle cons_strings[] = {left, balanced, right}; + Handle two_byte = + String::NewExternal(new TestResource(*string_contents)); + for (size_t i = 0; i < ARRAY_SIZE(cons_strings); i++) { + // Base assumptions. + string = cons_strings[i]; + CHECK(string->IsOneByte() && string->ContainsOnlyOneByte()); + // Test left and right concatentation. + string = String::Concat(two_byte, cons_strings[i]); + CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); + string = String::Concat(cons_strings[i], two_byte); + CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); + } +} + + // Failed access check callback that performs a GC on each invocation. void FailedAccessCheckCallbackGC(Local target, v8::AccessType type,