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.
5 #ifndef V8_STRING_BUILDER_H_
6 #define V8_STRING_BUILDER_H_
8 #include "src/objects-inl.h"
14 const int kStringBuilderConcatHelperLengthBits = 11;
15 const int kStringBuilderConcatHelperPositionBits = 19;
17 typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
18 StringBuilderSubstringLength;
19 typedef BitField<int, kStringBuilderConcatHelperLengthBits,
20 kStringBuilderConcatHelperPositionBits>
21 StringBuilderSubstringPosition;
24 template <typename sinkchar>
25 static inline void StringBuilderConcatHelper(String* special, sinkchar* sink,
26 FixedArray* fixed_array,
28 DisallowHeapAllocation no_gc;
30 for (int i = 0; i < array_length; i++) {
31 Object* element = fixed_array->get(i);
32 if (element->IsSmi()) {
33 // Smi encoding of position and length.
34 int encoded_slice = Smi::cast(element)->value();
37 if (encoded_slice > 0) {
38 // Position and length encoded in one smi.
39 pos = StringBuilderSubstringPosition::decode(encoded_slice);
40 len = StringBuilderSubstringLength::decode(encoded_slice);
42 // Position and length encoded in two smis.
43 Object* obj = fixed_array->get(++i);
45 pos = Smi::cast(obj)->value();
48 String::WriteToFlat(special, sink + position, pos, pos + len);
51 String* string = String::cast(element);
52 int element_length = string->length();
53 String::WriteToFlat(string, sink + position, 0, element_length);
54 position += element_length;
60 // Returns the result length of the concatenation.
61 // On illegal argument, -1 is returned.
62 static inline int StringBuilderConcatLength(int special_length,
63 FixedArray* fixed_array,
64 int array_length, bool* one_byte) {
65 DisallowHeapAllocation no_gc;
67 for (int i = 0; i < array_length; i++) {
69 Object* elt = fixed_array->get(i);
71 // Smi encoding of position and length.
72 int smi_value = Smi::cast(elt)->value();
76 // Position and length encoded in one smi.
77 pos = StringBuilderSubstringPosition::decode(smi_value);
78 len = StringBuilderSubstringLength::decode(smi_value);
80 // Position and length encoded in two smis.
82 // Get the position and check that it is a positive smi.
84 if (i >= array_length) return -1;
85 Object* next_smi = fixed_array->get(i);
86 if (!next_smi->IsSmi()) return -1;
87 pos = Smi::cast(next_smi)->value();
88 if (pos < 0) return -1;
92 if (pos > special_length || len > special_length - pos) return -1;
94 } else if (elt->IsString()) {
95 String* element = String::cast(elt);
96 int element_length = element->length();
97 increment = element_length;
98 if (*one_byte && !element->HasOnlyOneByteChars()) {
104 if (increment > String::kMaxLength - position) {
105 return kMaxInt; // Provoke throw on allocation.
107 position += increment;
113 class FixedArrayBuilder {
115 explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
116 : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
118 has_non_smi_elements_(false) {
119 // Require a non-zero initial size. Ensures that doubling the size to
120 // extend the array will work.
121 DCHECK(initial_capacity > 0);
124 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
125 : array_(backing_store), length_(0), has_non_smi_elements_(false) {
126 // Require a non-zero initial size. Ensures that doubling the size to
127 // extend the array will work.
128 DCHECK(backing_store->length() > 0);
131 bool HasCapacity(int elements) {
132 int length = array_->length();
133 int required_length = length_ + elements;
134 return (length >= required_length);
137 void EnsureCapacity(int elements) {
138 int length = array_->length();
139 int required_length = length_ + elements;
140 if (length < required_length) {
141 int new_length = length;
144 } while (new_length < required_length);
145 Handle<FixedArray> extended_array =
146 array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
147 array_->CopyTo(0, *extended_array, 0, length_);
148 array_ = extended_array;
152 void Add(Object* value) {
153 DCHECK(!value->IsSmi());
154 DCHECK(length_ < capacity());
155 array_->set(length_, value);
157 has_non_smi_elements_ = true;
160 void Add(Smi* value) {
161 DCHECK(value->IsSmi());
162 DCHECK(length_ < capacity());
163 array_->set(length_, value);
167 Handle<FixedArray> array() { return array_; }
169 int length() { return length_; }
171 int capacity() { return array_->length(); }
173 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
174 JSArray::SetContent(target_array, array_);
175 target_array->set_length(Smi::FromInt(length_));
181 Handle<FixedArray> array_;
183 bool has_non_smi_elements_;
187 class ReplacementStringBuilder {
189 ReplacementStringBuilder(Heap* heap, Handle<String> subject,
190 int estimated_part_count)
192 array_builder_(heap->isolate(), estimated_part_count),
195 is_one_byte_(subject->IsOneByteRepresentation()) {
196 // Require a non-zero initial size. Ensures that doubling the size to
197 // extend the array will work.
198 DCHECK(estimated_part_count > 0);
201 static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from,
204 int length = to - from;
206 if (StringBuilderSubstringLength::is_valid(length) &&
207 StringBuilderSubstringPosition::is_valid(from)) {
208 int encoded_slice = StringBuilderSubstringLength::encode(length) |
209 StringBuilderSubstringPosition::encode(from);
210 builder->Add(Smi::FromInt(encoded_slice));
212 // Otherwise encode as two smis.
213 builder->Add(Smi::FromInt(-length));
214 builder->Add(Smi::FromInt(from));
219 void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); }
222 void AddSubjectSlice(int from, int to) {
223 AddSubjectSlice(&array_builder_, from, to);
224 IncrementCharacterCount(to - from);
228 void AddString(Handle<String> string) {
229 int length = string->length();
232 if (!string->IsOneByteRepresentation()) {
233 is_one_byte_ = false;
235 IncrementCharacterCount(length);
239 MaybeHandle<String> ToString();
242 void IncrementCharacterCount(int by) {
243 if (character_count_ > String::kMaxLength - by) {
244 STATIC_ASSERT(String::kMaxLength < kMaxInt);
245 character_count_ = kMaxInt;
247 character_count_ += by;
252 void AddElement(Object* element) {
253 DCHECK(element->IsSmi() || element->IsString());
254 DCHECK(array_builder_.capacity() > array_builder_.length());
255 array_builder_.Add(element);
259 FixedArrayBuilder array_builder_;
260 Handle<String> subject_;
261 int character_count_;
266 class IncrementalStringBuilder {
268 explicit IncrementalStringBuilder(Isolate* isolate);
270 INLINE(String::Encoding CurrentEncoding()) { return encoding_; }
272 template <typename SrcChar, typename DestChar>
273 INLINE(void Append(SrcChar c));
275 INLINE(void AppendCharacter(uint8_t c)) {
276 if (encoding_ == String::ONE_BYTE_ENCODING) {
277 Append<uint8_t, uint8_t>(c);
279 Append<uint8_t, uc16>(c);
283 INLINE(void AppendCString(const char* s)) {
284 const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
285 if (encoding_ == String::ONE_BYTE_ENCODING) {
286 while (*u != '\0') Append<uint8_t, uint8_t>(*(u++));
288 while (*u != '\0') Append<uint8_t, uc16>(*(u++));
292 INLINE(bool CurrentPartCanFit(int length)) {
293 return part_length_ - current_index_ > length;
296 void AppendString(Handle<String> string);
298 MaybeHandle<String> Finish();
300 // Change encoding to two-byte.
301 void ChangeEncoding() {
302 DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
304 encoding_ = String::TWO_BYTE_ENCODING;
308 template <typename DestChar>
311 explicit NoExtend(Handle<String> string, int offset) {
312 DCHECK(string->IsSeqOneByteString() || string->IsSeqTwoByteString());
313 if (sizeof(DestChar) == 1) {
314 start_ = reinterpret_cast<DestChar*>(
315 Handle<SeqOneByteString>::cast(string)->GetChars() + offset);
317 start_ = reinterpret_cast<DestChar*>(
318 Handle<SeqTwoByteString>::cast(string)->GetChars() + offset);
323 INLINE(void Append(DestChar c)) { *(cursor_++) = c; }
324 INLINE(void AppendCString(const char* s)) {
325 const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
326 while (*u != '\0') Append(*(u++));
329 int written() { return static_cast<int>(cursor_ - start_); }
334 DisallowHeapAllocation no_gc_;
337 template <typename DestChar>
338 class NoExtendString : public NoExtend<DestChar> {
340 NoExtendString(Handle<String> string, int required_length)
341 : NoExtend<DestChar>(string, 0), string_(string) {
342 DCHECK(string->length() >= required_length);
346 Handle<SeqString> string = Handle<SeqString>::cast(string_);
347 int length = NoExtend<DestChar>::written();
348 *string_.location() = *SeqString::Truncate(string, length);
352 Handle<String> string_;
355 template <typename DestChar>
356 class NoExtendBuilder : public NoExtend<DestChar> {
358 NoExtendBuilder(IncrementalStringBuilder* builder, int required_length)
359 : NoExtend<DestChar>(builder->current_part(), builder->current_index_),
361 DCHECK(builder->CurrentPartCanFit(required_length));
365 builder_->current_index_ += NoExtend<DestChar>::written();
369 IncrementalStringBuilder* builder_;
373 Factory* factory() { return isolate_->factory(); }
375 INLINE(Handle<String> accumulator()) { return accumulator_; }
377 INLINE(void set_accumulator(Handle<String> string)) {
378 *accumulator_.location() = *string;
381 INLINE(Handle<String> current_part()) { return current_part_; }
383 INLINE(void set_current_part(Handle<String> string)) {
384 *current_part_.location() = *string;
387 // Add the current part to the accumulator.
388 void Accumulate(Handle<String> new_part);
390 // Finish the current part and allocate a new part.
393 // Shrink current part to the right size.
394 void ShrinkCurrentPart() {
395 DCHECK(current_index_ < part_length_);
396 set_current_part(SeqString::Truncate(
397 Handle<SeqString>::cast(current_part()), current_index_));
400 static const int kInitialPartLength = 32;
401 static const int kMaxPartLength = 16 * 1024;
402 static const int kPartLengthGrowthFactor = 2;
405 String::Encoding encoding_;
409 Handle<String> accumulator_;
410 Handle<String> current_part_;
414 template <typename SrcChar, typename DestChar>
415 void IncrementalStringBuilder::Append(SrcChar c) {
416 DCHECK_EQ(encoding_ == String::ONE_BYTE_ENCODING, sizeof(DestChar) == 1);
417 if (sizeof(DestChar) == 1) {
418 DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);
419 SeqOneByteString::cast(*current_part_)
420 ->SeqOneByteStringSet(current_index_++, c);
422 DCHECK_EQ(String::TWO_BYTE_ENCODING, encoding_);
423 SeqTwoByteString::cast(*current_part_)
424 ->SeqTwoByteStringSet(current_index_++, c);
426 if (current_index_ == part_length_) Extend();
429 } // namespace v8::internal
431 #endif // V8_STRING_BUILDER_H_