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
}
+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 {
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(),
}
+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,