1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/base/strings.h"
6 #include "src/execution/isolate-inl.h"
7 #include "src/objects/fixed-array-inl.h"
8 #include "src/objects/js-array-inl.h"
9 #include "src/strings/string-builder-inl.h"
10
11 namespace v8 {
12 namespace internal {
13
14 template <typename sinkchar>
StringBuilderConcatHelper(String special,sinkchar * sink,FixedArray fixed_array,int array_length)15 void StringBuilderConcatHelper(String special, sinkchar* sink,
16 FixedArray fixed_array, int array_length) {
17 DisallowGarbageCollection no_gc;
18 int position = 0;
19 for (int i = 0; i < array_length; i++) {
20 Object element = fixed_array.get(i);
21 if (element.IsSmi()) {
22 // Smi encoding of position and length.
23 int encoded_slice = Smi::ToInt(element);
24 int pos;
25 int len;
26 if (encoded_slice > 0) {
27 // Position and length encoded in one smi.
28 pos = StringBuilderSubstringPosition::decode(encoded_slice);
29 len = StringBuilderSubstringLength::decode(encoded_slice);
30 } else {
31 // Position and length encoded in two smis.
32 Object obj = fixed_array.get(++i);
33 DCHECK(obj.IsSmi());
34 pos = Smi::ToInt(obj);
35 len = -encoded_slice;
36 }
37 String::WriteToFlat(special, sink + position, pos, len);
38 position += len;
39 } else {
40 String string = String::cast(element);
41 int element_length = string.length();
42 String::WriteToFlat(string, sink + position, 0, element_length);
43 position += element_length;
44 }
45 }
46 }
47
48 template void StringBuilderConcatHelper<uint8_t>(String special, uint8_t* sink,
49 FixedArray fixed_array,
50 int array_length);
51
52 template void StringBuilderConcatHelper<base::uc16>(String special,
53 base::uc16* sink,
54 FixedArray fixed_array,
55 int array_length);
56
StringBuilderConcatLength(int special_length,FixedArray fixed_array,int array_length,bool * one_byte)57 int StringBuilderConcatLength(int special_length, FixedArray fixed_array,
58 int array_length, bool* one_byte) {
59 DisallowGarbageCollection no_gc;
60 int position = 0;
61 for (int i = 0; i < array_length; i++) {
62 int increment = 0;
63 Object elt = fixed_array.get(i);
64 if (elt.IsSmi()) {
65 // Smi encoding of position and length.
66 int smi_value = Smi::ToInt(elt);
67 int pos;
68 int len;
69 if (smi_value > 0) {
70 // Position and length encoded in one smi.
71 pos = StringBuilderSubstringPosition::decode(smi_value);
72 len = StringBuilderSubstringLength::decode(smi_value);
73 } else {
74 // Position and length encoded in two smis.
75 len = -smi_value;
76 // Get the position and check that it is a positive smi.
77 i++;
78 if (i >= array_length) return -1;
79 Object next_smi = fixed_array.get(i);
80 if (!next_smi.IsSmi()) return -1;
81 pos = Smi::ToInt(next_smi);
82 if (pos < 0) return -1;
83 }
84 DCHECK_GE(pos, 0);
85 DCHECK_GE(len, 0);
86 if (pos > special_length || len > special_length - pos) return -1;
87 increment = len;
88 } else if (elt.IsString()) {
89 String element = String::cast(elt);
90 int element_length = element.length();
91 increment = element_length;
92 if (*one_byte && !element.IsOneByteRepresentation()) {
93 *one_byte = false;
94 }
95 } else {
96 return -1;
97 }
98 if (increment > String::kMaxLength - position) {
99 return kMaxInt; // Provoke throw on allocation.
100 }
101 position += increment;
102 }
103 return position;
104 }
105
FixedArrayBuilder(Isolate * isolate,int initial_capacity)106 FixedArrayBuilder::FixedArrayBuilder(Isolate* isolate, int initial_capacity)
107 : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
108 length_(0),
109 has_non_smi_elements_(false) {
110 // Require a non-zero initial size. Ensures that doubling the size to
111 // extend the array will work.
112 DCHECK_GT(initial_capacity, 0);
113 }
114
FixedArrayBuilder(Handle<FixedArray> backing_store)115 FixedArrayBuilder::FixedArrayBuilder(Handle<FixedArray> backing_store)
116 : array_(backing_store), length_(0), has_non_smi_elements_(false) {
117 // Require a non-zero initial size. Ensures that doubling the size to
118 // extend the array will work.
119 DCHECK_GT(backing_store->length(), 0);
120 }
121
HasCapacity(int elements)122 bool FixedArrayBuilder::HasCapacity(int elements) {
123 int length = array_->length();
124 int required_length = length_ + elements;
125 return (length >= required_length);
126 }
127
EnsureCapacity(Isolate * isolate,int elements)128 void FixedArrayBuilder::EnsureCapacity(Isolate* isolate, int elements) {
129 int length = array_->length();
130 int required_length = length_ + elements;
131 if (length < required_length) {
132 int new_length = length;
133 do {
134 new_length *= 2;
135 } while (new_length < required_length);
136 Handle<FixedArray> extended_array =
137 isolate->factory()->NewFixedArrayWithHoles(new_length);
138 array_->CopyTo(0, *extended_array, 0, length_);
139 array_ = extended_array;
140 }
141 }
142
Add(Object value)143 void FixedArrayBuilder::Add(Object value) {
144 DCHECK(!value.IsSmi());
145 array_->set(length_, value);
146 length_++;
147 has_non_smi_elements_ = true;
148 }
149
Add(Smi value)150 void FixedArrayBuilder::Add(Smi value) {
151 DCHECK(value.IsSmi());
152 array_->set(length_, value);
153 length_++;
154 }
155
capacity()156 int FixedArrayBuilder::capacity() { return array_->length(); }
157
ToJSArray(Handle<JSArray> target_array)158 Handle<JSArray> FixedArrayBuilder::ToJSArray(Handle<JSArray> target_array) {
159 JSArray::SetContent(target_array, array_);
160 target_array->set_length(Smi::FromInt(length_));
161 return target_array;
162 }
163
ReplacementStringBuilder(Heap * heap,Handle<String> subject,int estimated_part_count)164 ReplacementStringBuilder::ReplacementStringBuilder(Heap* heap,
165 Handle<String> subject,
166 int estimated_part_count)
167 : heap_(heap),
168 array_builder_(Isolate::FromHeap(heap), estimated_part_count),
169 subject_(subject),
170 character_count_(0),
171 is_one_byte_(subject->IsOneByteRepresentation()) {
172 // Require a non-zero initial size. Ensures that doubling the size to
173 // extend the array will work.
174 DCHECK_GT(estimated_part_count, 0);
175 }
176
EnsureCapacity(int elements)177 void ReplacementStringBuilder::EnsureCapacity(int elements) {
178 array_builder_.EnsureCapacity(Isolate::FromHeap(heap_), elements);
179 }
180
AddString(Handle<String> string)181 void ReplacementStringBuilder::AddString(Handle<String> string) {
182 int length = string->length();
183 DCHECK_GT(length, 0);
184 AddElement(string);
185 if (!string->IsOneByteRepresentation()) {
186 is_one_byte_ = false;
187 }
188 IncrementCharacterCount(length);
189 }
190
ToString()191 MaybeHandle<String> ReplacementStringBuilder::ToString() {
192 Isolate* isolate = Isolate::FromHeap(heap_);
193 if (array_builder_.length() == 0) {
194 return isolate->factory()->empty_string();
195 }
196
197 Handle<String> joined_string;
198 if (is_one_byte_) {
199 Handle<SeqOneByteString> seq;
200 ASSIGN_RETURN_ON_EXCEPTION(
201 isolate, seq, isolate->factory()->NewRawOneByteString(character_count_),
202 String);
203
204 DisallowGarbageCollection no_gc;
205 uint8_t* char_buffer = seq->GetChars(no_gc);
206 StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
207 array_builder_.length());
208 joined_string = Handle<String>::cast(seq);
209 } else {
210 // Two-byte.
211 Handle<SeqTwoByteString> seq;
212 ASSIGN_RETURN_ON_EXCEPTION(
213 isolate, seq, isolate->factory()->NewRawTwoByteString(character_count_),
214 String);
215
216 DisallowGarbageCollection no_gc;
217 base::uc16* char_buffer = seq->GetChars(no_gc);
218 StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(),
219 array_builder_.length());
220 joined_string = Handle<String>::cast(seq);
221 }
222 return joined_string;
223 }
224
AddElement(Handle<Object> element)225 void ReplacementStringBuilder::AddElement(Handle<Object> element) {
226 DCHECK(element->IsSmi() || element->IsString());
227 EnsureCapacity(1);
228 DisallowGarbageCollection no_gc;
229 array_builder_.Add(*element);
230 }
231
IncrementalStringBuilder(Isolate * isolate)232 IncrementalStringBuilder::IncrementalStringBuilder(Isolate* isolate)
233 : isolate_(isolate),
234 encoding_(String::ONE_BYTE_ENCODING),
235 overflowed_(false),
236 part_length_(kInitialPartLength),
237 current_index_(0) {
238 // Create an accumulator handle starting with the empty string.
239 accumulator_ =
240 Handle<String>::New(ReadOnlyRoots(isolate).empty_string(), isolate);
241 current_part_ =
242 factory()->NewRawOneByteString(part_length_).ToHandleChecked();
243 }
244
Length() const245 int IncrementalStringBuilder::Length() const {
246 return accumulator_->length() + current_index_;
247 }
248
HasValidCurrentIndex() const249 bool IncrementalStringBuilder::HasValidCurrentIndex() const {
250 return current_index_ < part_length_;
251 }
252
Accumulate(Handle<String> new_part)253 void IncrementalStringBuilder::Accumulate(Handle<String> new_part) {
254 Handle<String> new_accumulator;
255 if (accumulator()->length() + new_part->length() > String::kMaxLength) {
256 // Set the flag and carry on. Delay throwing the exception till the end.
257 new_accumulator = factory()->empty_string();
258 overflowed_ = true;
259 } else {
260 new_accumulator =
261 factory()->NewConsString(accumulator(), new_part).ToHandleChecked();
262 }
263 set_accumulator(new_accumulator);
264 }
265
Extend()266 void IncrementalStringBuilder::Extend() {
267 DCHECK_EQ(current_index_, current_part()->length());
268 Accumulate(current_part());
269 if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
270 part_length_ *= kPartLengthGrowthFactor;
271 }
272 Handle<String> new_part;
273 if (encoding_ == String::ONE_BYTE_ENCODING) {
274 new_part = factory()->NewRawOneByteString(part_length_).ToHandleChecked();
275 } else {
276 new_part = factory()->NewRawTwoByteString(part_length_).ToHandleChecked();
277 }
278 // Reuse the same handle to avoid being invalidated when exiting handle scope.
279 set_current_part(new_part);
280 current_index_ = 0;
281 }
282
Finish()283 MaybeHandle<String> IncrementalStringBuilder::Finish() {
284 ShrinkCurrentPart();
285 Accumulate(current_part());
286 if (overflowed_) {
287 THROW_NEW_ERROR(isolate_, NewInvalidStringLengthError(), String);
288 }
289 return accumulator();
290 }
291
292 // Short strings can be copied directly to {current_part_}.
293 // Requires the IncrementalStringBuilder to either have two byte encoding or
294 // the incoming string to have one byte representation "underneath" (The
295 // one byte check requires the string to be flat).
CanAppendByCopy(Handle<String> string)296 bool IncrementalStringBuilder::CanAppendByCopy(Handle<String> string) {
297 constexpr int kMaxStringLengthForCopy = 16;
298 const bool representation_ok =
299 encoding_ == String::TWO_BYTE_ENCODING ||
300 (string->IsFlat() && String::IsOneByteRepresentationUnderneath(*string));
301
302 return representation_ok && string->length() <= kMaxStringLengthForCopy &&
303 CurrentPartCanFit(string->length());
304 }
305
AppendStringByCopy(Handle<String> string)306 void IncrementalStringBuilder::AppendStringByCopy(Handle<String> string) {
307 DCHECK(CanAppendByCopy(string));
308
309 Handle<SeqOneByteString> part =
310 Handle<SeqOneByteString>::cast(current_part());
311 {
312 DisallowGarbageCollection no_gc;
313 String::WriteToFlat(*string, part->GetChars(no_gc) + current_index_, 0,
314 string->length());
315 }
316 current_index_ += string->length();
317 DCHECK(current_index_ <= part_length_);
318 if (current_index_ == part_length_) Extend();
319 }
320
AppendString(Handle<String> string)321 void IncrementalStringBuilder::AppendString(Handle<String> string) {
322 if (CanAppendByCopy(string)) {
323 AppendStringByCopy(string);
324 return;
325 }
326
327 ShrinkCurrentPart();
328 part_length_ = kInitialPartLength; // Allocate conservatively.
329 Extend(); // Attach current part and allocate new part.
330 Accumulate(string);
331 }
332 } // namespace internal
333 } // namespace v8
334