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
This commit is contained in:
dcarney@chromium.org 2013-06-06 13:16:52 +00:00
parent 98b97d9edb
commit 6f5d899248
4 changed files with 138 additions and 1 deletions

View File

@ -1524,10 +1524,18 @@ 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

View File

@ -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<i::String> 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 {

View File

@ -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<size_t>(this->length()) == resource->length());
if (this->IsTwoByteRepresentation()) {
ScopedVector<uint16_t> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
ASSERT(String::IsOneByte(smart_chars.start(), this->length()));
}
ScopedVector<char> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
ASSERT(memcmp(smart_chars.start(),

View File

@ -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<uint16_t> 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 = 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<String> base = String::NewFromUtf8(isolate, "a");
Handle<String> left = base;
Handle<String> right = base;
for (int i = 0; i < 1000; i++) {
left = String::Concat(base, left);
right = String::Concat(right, base);
}
Handle<String> balanced = String::Concat(left, base);
balanced = String::Concat(balanced, right);
Handle<String> cons_strings[] = {left, balanced, right};
Handle<String> 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<v8::Object> target,
v8::AccessType type,