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