Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Unified Diff: runtime/lib/string.cc

Issue 858543002: Avoid extra duplication of substrings during string.replaceAll. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | runtime/lib/string_patch.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/lib/string.cc
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc
index 5b450f4a71adf581e2bf87f04e07698e365f575d..2dd69e54f47d279453292688d874bf02d85513e9 100644
--- a/runtime/lib/string.cc
+++ b/runtime/lib/string.cc
@@ -103,6 +103,155 @@ DEFINE_NATIVE_ENTRY(StringBase_substringUnchecked, 3) {
}
+
+// Return the bitwise-or of all characters in the slice from start to end.
+static uint16_t CharacterLimit(const String& string,
+ intptr_t start, intptr_t end) {
+ ASSERT(string.IsTwoByteString() || string.IsExternalTwoByteString());
+ // Maybe do loop unrolling, and handle two uint16_t in a single uint32_t
+ // operation.
+ NoGCScope no_gc;
+ uint16_t result = 0;
+ if (string.IsTwoByteString()) {
+ for (intptr_t i = start; i < end; i++) {
+ result |= TwoByteString::CharAt(string, i);
+ }
+ } else {
+ for (intptr_t i = start; i < end; i++) {
+ result |= ExternalTwoByteString::CharAt(string, i);
+ }
+ }
+ return result;
+}
+
+static const intptr_t kLengthSize = 11;
+static const intptr_t kLengthMask = (1 << kLengthSize) - 1;
+
+static bool CheckSlicesOneByte(const String& base,
+ const Array& matches,
+ const int len) {
+ Instance& object = Instance::Handle();
+ // Check each slice for one-bytedness.
+ for (intptr_t i = 0; i < len; i++) {
+ object ^= matches.At(i);
+ if (object.IsSmi()) {
+ intptr_t slice_start = Smi::Cast(object).Value();
+ intptr_t slice_end;
+ if (slice_start < 0) {
+ intptr_t bits = -slice_start;
+ slice_start = bits >> kLengthSize;
+ slice_end = slice_start + (bits & kLengthMask);
+ } else {
+ i++;
+ if (i >= len) {
+ // Bad format, handled later.
+ return false;
+ }
+ object ^= matches.At(i);
+ if (!object.IsSmi()) {
+ // Bad format, handled later.
+ return false;
+ }
+ slice_end = Smi::Cast(object).Value();
+ }
+ uint16_t char_limit = CharacterLimit(base, slice_start, slice_end);
+ if (char_limit > 0xff) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+DEFINE_NATIVE_ENTRY(StringBase_joinReplaceAllResult, 4) {
+ const String& base = String::CheckedHandle(arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray,
+ matches_growable, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length_obj, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_onebyte_obj, arguments->NativeArgAt(3));
+
+ intptr_t len = matches_growable.Length();
+ const Array& matches = Array::Handle(isolate, matches_growable.data());
+
+ const intptr_t length = length_obj.Value();
+ if (length < 0) {
+ Exceptions::ThrowArgumentError(length_obj);
+ }
+
+ // Start out assuming result is one-byte if replacements are.
+ bool is_onebyte = is_onebyte_obj.value();
+ if (is_onebyte) {
+ // If any of the base string slices are not one-byte, the result will be
+ // a two-byte string.
+ if (!base.IsOneByteString() && !base.IsExternalOneByteString()) {
+ is_onebyte = CheckSlicesOneByte(base, matches, len);
+ }
+ }
+
+ const intptr_t base_length = base.Length();
+ String& result = String::Handle(isolate);
+ if (is_onebyte) {
+ result ^= OneByteString::New(length, Heap::kNew);
+ } else {
+ result ^= TwoByteString::New(length, Heap::kNew);
+ }
+ Instance& object = Instance::Handle(isolate);
+ intptr_t write_index = 0;
+ for (intptr_t i = 0; i < len; i++) {
+ object ^= matches.At(i);
+ if (object.IsSmi()) {
+ intptr_t slice_start = Smi::Cast(object).Value();
+ intptr_t slice_length = -1;
+ // Slices with limited ranges are stored in a single negative Smi.
+ if (slice_start < 0) {
+ intptr_t bits = -slice_start;
+ slice_start = bits >> kLengthSize;
+ slice_length = bits & kLengthMask;
+ } else {
+ i++;
+ if (i < len) { // Otherwise slice_length stays at -1.
+ object ^= matches.At(i);
+ if (object.IsSmi()) {
+ intptr_t slice_end = Smi::Cast(object).Value();
+ slice_length = slice_end - slice_start;
+ }
+ }
+ }
+ if (slice_length > 0) {
+ if (0 <= slice_start &&
+ slice_start + slice_length <= base_length &&
+ write_index + slice_length <= length) {
+ String::Copy(result, write_index,
+ base, slice_start,
+ slice_length);
+ write_index += slice_length;
+ continue;
+ }
+ }
+ // Either the slice_length was zero,
+ // or the first smi was positive and not followed by another smi,
+ // or the smis were not a valid slice of the base string,
+ // or the slice was too large to fit in the result.
+ // Something is wrong with the matches array!
+ Exceptions::ThrowArgumentError(matches_growable);
+ } else if (object.IsString()) {
+ const String& replacement = String::Cast(object);
+ intptr_t replacement_length = replacement.Length();
+ if (write_index + replacement_length > length) {
+ // Invalid input data, either in matches list or the total length.
+ Exceptions::ThrowArgumentError(matches_growable);
+ }
+ String::Copy(result, write_index, replacement, 0, replacement_length);
+ write_index += replacement_length;
+ }
+ }
+ if (write_index < length) {
+ Exceptions::ThrowArgumentError(matches_growable);
+ }
+ return result.raw();
+}
+
DEFINE_NATIVE_ENTRY(OneByteString_substringUnchecked, 3) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(receiver.IsOneByteString());
« no previous file with comments | « no previous file | runtime/lib/string_patch.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698