Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "vm/StringBuffer.h"
8 :
9 : #include "mozilla/Range.h"
10 :
11 : #include "jsobjinlines.h"
12 :
13 : #include "vm/String-inl.h"
14 :
15 : using namespace js;
16 :
17 : template <typename CharT, class Buffer>
18 : static CharT*
19 538 : ExtractWellSized(JSContext* cx, Buffer& cb)
20 : {
21 538 : size_t capacity = cb.capacity();
22 538 : size_t length = cb.length();
23 :
24 538 : CharT* buf = cb.extractOrCopyRawBuffer();
25 538 : if (!buf)
26 0 : return nullptr;
27 :
28 : /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
29 538 : MOZ_ASSERT(capacity >= length);
30 538 : if (length > Buffer::sMaxInlineStorage && capacity - length > length / 4) {
31 34 : CharT* tmp = cx->zone()->pod_realloc<CharT>(buf, capacity, length + 1);
32 34 : if (!tmp) {
33 0 : js_free(buf);
34 0 : ReportOutOfMemory(cx);
35 0 : return nullptr;
36 : }
37 34 : buf = tmp;
38 : }
39 :
40 538 : return buf;
41 : }
42 :
43 : char16_t*
44 11 : StringBuffer::stealChars()
45 : {
46 11 : if (isLatin1() && !inflateChars())
47 0 : return nullptr;
48 :
49 11 : return ExtractWellSized<char16_t>(cx, twoByteChars());
50 : }
51 :
52 : bool
53 17 : StringBuffer::inflateChars()
54 : {
55 17 : MOZ_ASSERT(isLatin1());
56 :
57 34 : TwoByteCharBuffer twoByte(cx);
58 :
59 : /*
60 : * Note: we don't use Vector::capacity() because it always returns a
61 : * value >= sInlineCapacity. Since Latin1CharBuffer::sInlineCapacity >
62 : * TwoByteCharBuffer::sInlineCapacitychars, we'd always malloc here.
63 : */
64 17 : size_t capacity = Max(reserved_, latin1Chars().length());
65 17 : if (!twoByte.reserve(capacity))
66 0 : return false;
67 :
68 17 : twoByte.infallibleAppend(latin1Chars().begin(), latin1Chars().length());
69 :
70 17 : cb.destroy();
71 17 : cb.construct<TwoByteCharBuffer>(Move(twoByte));
72 17 : return true;
73 : }
74 :
75 : template <typename CharT, class Buffer>
76 : static JSFlatString*
77 527 : FinishStringFlat(JSContext* cx, StringBuffer& sb, Buffer& cb)
78 : {
79 527 : size_t len = sb.length();
80 527 : if (!sb.append('\0'))
81 0 : return nullptr;
82 :
83 1054 : ScopedJSFreePtr<CharT> buf(ExtractWellSized<CharT>(cx, cb));
84 527 : if (!buf)
85 0 : return nullptr;
86 :
87 527 : JSFlatString* str = NewStringDontDeflate<CanGC>(cx, buf.get(), len);
88 527 : if (!str)
89 0 : return nullptr;
90 :
91 : /*
92 : * The allocation was made on a TempAllocPolicy, so account for the string
93 : * data on the string's zone.
94 : */
95 527 : str->zone()->updateMallocCounter(sizeof(CharT) * len);
96 :
97 527 : buf.forget();
98 527 : return str;
99 : }
100 :
101 : JSFlatString*
102 846 : StringBuffer::finishString()
103 : {
104 846 : size_t len = length();
105 846 : if (len == 0)
106 0 : return cx->names().empty;
107 :
108 846 : if (!JSString::validateLength(cx, len))
109 0 : return nullptr;
110 :
111 : JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_TWO_BYTE < TwoByteCharBuffer::InlineLength);
112 : JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_LATIN1 < Latin1CharBuffer::InlineLength);
113 :
114 846 : if (isLatin1()) {
115 840 : if (JSInlineString::lengthFits<Latin1Char>(len)) {
116 319 : mozilla::Range<const Latin1Char> range(latin1Chars().begin(), len);
117 319 : return NewInlineString<CanGC>(cx, range);
118 : }
119 : } else {
120 6 : if (JSInlineString::lengthFits<char16_t>(len)) {
121 0 : mozilla::Range<const char16_t> range(twoByteChars().begin(), len);
122 0 : return NewInlineString<CanGC>(cx, range);
123 : }
124 : }
125 :
126 527 : return isLatin1()
127 533 : ? FinishStringFlat<Latin1Char>(cx, *this, latin1Chars())
128 533 : : FinishStringFlat<char16_t>(cx, *this, twoByteChars());
129 : }
130 :
131 : JSAtom*
132 17733 : StringBuffer::finishAtom()
133 : {
134 17733 : size_t len = length();
135 17733 : if (len == 0)
136 0 : return cx->names().empty;
137 :
138 17733 : if (isLatin1()) {
139 17733 : JSAtom* atom = AtomizeChars(cx, latin1Chars().begin(), len);
140 17733 : latin1Chars().clear();
141 17733 : return atom;
142 : }
143 :
144 0 : JSAtom* atom = AtomizeChars(cx, twoByteChars().begin(), len);
145 0 : twoByteChars().clear();
146 0 : return atom;
147 : }
148 :
149 : bool
150 0 : js::ValueToStringBufferSlow(JSContext* cx, const Value& arg, StringBuffer& sb)
151 : {
152 0 : RootedValue v(cx, arg);
153 0 : if (!ToPrimitive(cx, JSTYPE_STRING, &v))
154 0 : return false;
155 :
156 0 : if (v.isString())
157 0 : return sb.append(v.toString());
158 0 : if (v.isNumber())
159 0 : return NumberValueToStringBuffer(cx, v, sb);
160 0 : if (v.isBoolean())
161 0 : return BooleanToStringBuffer(v.toBoolean(), sb);
162 0 : if (v.isNull())
163 0 : return sb.append(cx->names().null);
164 0 : if (v.isSymbol()) {
165 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SYMBOL_TO_STRING);
166 0 : return false;
167 : }
168 0 : MOZ_ASSERT(v.isUndefined());
169 0 : return sb.append(cx->names().undefined);
170 : }
|