Index: runtime/lib/string.cc |
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc |
index 5b450f4a71adf581e2bf87f04e07698e365f575d..5c05ace4b08f43a0e900ffdbd04c429e5bc71554 100644 |
--- a/runtime/lib/string.cc |
+++ b/runtime/lib/string.cc |
@@ -103,6 +103,141 @@ 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()); |
zerny-google
2015/01/20 13:43:11
Nit: space after ||
Lasse Reichstein Nielsen
2015/01/21 10:48:43
Done.
|
+ // Maybe do loop unrolling, and handle two uint16_t in a single uint32_t |
+ // operation. |
+ NoGCScope no_gc; |
+ uint16_t result = 0; |
+ for (intptr_t i = start; i < end; i++) { |
+ result |= TwoByteString::CharAt(string, i); |
Florian Schneider
2015/01/20 16:39:33
This should be ExternalTwoByteString::CharAt in th
Lasse Reichstein Nielsen
2015/01/21 10:48:43
Done.
Is there a simple way to get the uint16_t* o
|
+ } |
+ return result; |
+} |
+ |
+ |
+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(matches_growable.data()); |
siva
2015/01/21 00:29:20
Since 'isolate' is available to the native method
Lasse Reichstein Nielsen
2015/01/21 10:48:43
Done in this code. The rest of the file could do i
|
+ |
+ const intptr_t kLengthShift = 15; |
+ const intptr_t kStartMask = (1 << kLengthShift) - 1; |
+ |
+ 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()) { |
+ 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 & kStartMask; |
+ slice_end = slice_start + (bits >> kLengthShift); |
+ } else { |
+ i++; |
+ object ^= matches.At(i); |
siva
2015/01/21 00:29:20
Missing check for if (i < len) here?
Lasse Reichstein Nielsen
2015/01/21 10:48:43
Done.
|
+ if (!object.IsSmi()) { |
+ // Should fail, but just continue and handle the failure later. |
+ // This branch isn't called in all cases. |
+ is_onebyte = false; |
+ break; |
+ } |
+ slice_end = Smi::Cast(object).Value(); |
+ } |
+ uint16_t char_limit = CharacterLimit(base, slice_start, slice_end); |
+ if (char_limit > 0xff) { |
+ is_onebyte = false; |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ const intptr_t base_length = base.Length(); |
+ String& result = String::Handle(); |
+ if (is_onebyte) { |
+ result ^= OneByteString::New(length, Heap::kNew); |
+ } else { |
+ result ^= TwoByteString::New(length, Heap::kNew); |
+ } |
+ Instance& object = Instance::Handle(); |
+ intptr_t writeIndex = 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 & kStartMask; |
+ slice_length = bits >> kLengthShift; |
+ } else { |
+ i++; |
+ if (i < len) { |
+ 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 && |
+ writeIndex + slice_length <= length) { |
+ String::Copy(result, writeIndex, |
+ base, slice_start, |
+ slice_length); |
+ writeIndex += 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); |
+ } else if (object.IsString()) { |
+ const String& replacement = String::Cast(object); |
+ intptr_t replacement_length = replacement.Length(); |
+ if (writeIndex + replacement_length > length) { |
+ // Invalid input data, either in matches list or the total length. |
+ Exceptions::ThrowArgumentError(matches); |
+ } |
+ String::Copy(result, writeIndex, replacement, 0, replacement_length); |
+ writeIndex += replacement_length; |
+ } |
+ } |
+ if (writeIndex < length) { |
+ Exceptions::ThrowArgumentError(matches); |
+ } |
+ return result.raw(); |
siva
2015/01/21 00:29:20
This function seems to be long, do you think it wo
Lasse Reichstein Nielsen
2015/01/21 10:48:43
I think I'll move the first loop into a helper met
|
+} |
+ |
DEFINE_NATIVE_ENTRY(OneByteString_substringUnchecked, 3) { |
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0)); |
ASSERT(receiver.IsOneByteString()); |