Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsstrinlines.h
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 #ifndef jsstrinlines_h___
41 #define jsstrinlines_h___
42
43 #include "jsstr.h"
44 #include "jscntxtinlines.h"
45
46 namespace js {
47
48 static inline bool
49 CheckStringLength(JSContext *cx, size_t length)
50 {
51     if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
52         js_ReportAllocationOverflow(cx);
53         return false;
54     }
55
56     return true;
57 }
58
59 /*
60  * String builder that eagerly checks for over-allocation past the maximum
61  * string length.
62  *
63  * Note: over-allocation is not checked for when using the infallible
64  * |replaceRawBuffer|, so the implementation of |finishString| also must check
65  * for over-allocation.
66  */
67 class StringBuffer
68 {
69     typedef Vector<jschar, 32> CharBuffer;
70     CharBuffer cb;
71
72     static inline bool checkLength(JSContext *cx, size_t length);
73     inline bool checkLength(size_t length);
74     JSContext *context() const { return cb.allocPolicy().context(); }
75
76   public:
77     explicit inline StringBuffer(JSContext *cx);
78     bool reserve(size_t len);
79     bool resize(size_t len);
80     bool append(const jschar c);
81     bool append(const jschar *chars, size_t len);
82     bool append(const jschar *begin, const jschar *end);
83     bool append(JSString *str);
84     bool append(JSAtom *atom);
85     bool appendN(const jschar c, size_t n);
86     bool appendInflated(const char *cstr, size_t len);
87     JSAtom *atomize(uintN flags = 0);
88     static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, uintN flags = 0);
89     static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, uintN flags = 0);
90
91     void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); }
92     jschar *begin() { return cb.begin(); }
93     jschar *end() { return cb.end(); }
94     const jschar *begin() const { return cb.begin(); }
95     const jschar *end() const { return cb.end(); }
96     bool empty() const { return cb.empty(); }
97     inline jsint length() const;
98
99     /*
100      * Produces a string, resetting the buffer to an empty state.
101      * This method takes responsibility for adding the terminating '\0'
102      * required by js_NewString.
103      */
104     JSFlatString *finishString();
105
106     template <size_t ArrayLength>
107     bool append(const char (&array)[ArrayLength]) {
108         return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
109     }
110 };
111
112 inline
113 StringBuffer::StringBuffer(JSContext *cx)
114   : cb(cx)
115 {}
116
117 inline bool
118 StringBuffer::reserve(size_t len)
119 {
120     if (!checkLength(len))
121         return false;
122     return cb.reserve(len);
123 }
124
125 inline bool
126 StringBuffer::resize(size_t len)
127 {
128     if (!checkLength(len))
129         return false;
130     return cb.resize(len);
131 }
132
133 inline bool
134 StringBuffer::append(const jschar c)
135 {
136     if (!checkLength(cb.length() + 1))
137         return false;
138     return cb.append(c);
139 }
140
141 inline bool
142 StringBuffer::append(const jschar *chars, size_t len)
143 {
144     if (!checkLength(cb.length() + len))
145         return false;
146     return cb.append(chars, len);
147 }
148
149 inline bool
150 StringBuffer::append(const jschar *begin, const jschar *end)
151 {
152     if (!checkLength(cb.length() + (end - begin)))
153         return false;
154     return cb.append(begin, end);
155 }
156
157 inline bool
158 StringBuffer::append(JSString *str)
159 {
160     JSLinearString *linear = str->ensureLinear(context());
161     size_t strLen = linear->length();
162     if (!checkLength(cb.length() + strLen))
163         return false;
164     return cb.append(linear->chars(), strLen);
165 }
166
167 inline bool
168 StringBuffer::append(JSAtom *atom)
169 {
170     size_t strLen = atom->length();
171     if (!checkLength(cb.length() + strLen))
172         return false;
173     return cb.append(atom->chars(), strLen);
174 }
175
176 inline bool
177 StringBuffer::appendN(const jschar c, size_t n)
178 {
179     if (!checkLength(cb.length() + n))
180         return false;
181     return cb.appendN(c, n);
182 }
183
184 inline bool
185 StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
186 {
187     size_t lengthBefore = length();
188     if (!cb.growByUninitialized(cstrlen))
189         return false;
190 #if DEBUG
191     size_t oldcstrlen = cstrlen;
192     bool ok = 
193 #endif
194     js_InflateStringToBuffer(context(), cstr, cstrlen, begin() + lengthBefore, &cstrlen);
195     JS_ASSERT(ok && oldcstrlen == cstrlen);
196     return true;
197 }
198
199 inline jsint
200 StringBuffer::length() const
201 {
202     JS_STATIC_ASSERT(jsint(JSString::MAX_LENGTH) == JSString::MAX_LENGTH);
203     JS_ASSERT(cb.length() <= JSString::MAX_LENGTH);
204     return jsint(cb.length());
205 }
206
207 inline bool
208 StringBuffer::checkLength(size_t length)
209 {
210     return CheckStringLength(context(), length);
211 }
212
213 } /* namespace js */
214
215 inline JSFlatString *
216 JSString::unitString(jschar c)
217 {
218     JS_ASSERT(c < UNIT_STRING_LIMIT);
219     return const_cast<JSString *>(&unitStringTable[c])->assertIsFlat();
220 }
221
222 inline JSLinearString *
223 JSString::getUnitString(JSContext *cx, JSString *str, size_t index)
224 {
225     JS_ASSERT(index < str->length());
226     const jschar *chars = str->getChars(cx);
227     if (!chars)
228         return NULL;
229     jschar c = chars[index];
230     if (c < UNIT_STRING_LIMIT)
231         return unitString(c);
232     return js_NewDependentString(cx, str, index, 1);
233 }
234
235 inline JSFlatString *
236 JSString::length2String(jschar c1, jschar c2)
237 {
238     JS_ASSERT(fitsInSmallChar(c1));
239     JS_ASSERT(fitsInSmallChar(c2));
240     return const_cast<JSString *> (
241              &length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]]
242            )->assertIsFlat();
243 }
244
245 inline JSFlatString *
246 JSString::length2String(uint32 i)
247 {
248     JS_ASSERT(i < 100);
249     return length2String('0' + i / 10, '0' + i % 10);
250 }
251
252 inline JSFlatString *
253 JSString::intString(jsint i)
254 {
255     jsuint u = jsuint(i);
256     JS_ASSERT(u < INT_STRING_LIMIT);
257     return const_cast<JSString *>(JSString::intStringTable[u])->assertIsFlat();
258 }
259
260 /* Get a static atomized string for chars if possible. */
261 inline JSFlatString *
262 JSString::lookupStaticString(const jschar *chars, size_t length)
263 {
264     if (length == 1) {
265         if (chars[0] < UNIT_STRING_LIMIT)
266             return unitString(chars[0]);
267     }
268
269     if (length == 2) {
270         if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
271             return length2String(chars[0], chars[1]);
272     }
273
274     /*
275      * Here we know that JSString::intStringTable covers only 256 (or at least
276      * not 1000 or more) chars. We rely on order here to resolve the unit vs.
277      * int string/length-2 string atom identity issue by giving priority to unit
278      * strings for "0" through "9" and length-2 strings for "10" through "99".
279      */
280     JS_STATIC_ASSERT(INT_STRING_LIMIT <= 999);
281     if (length == 3) {
282         if ('1' <= chars[0] && chars[0] <= '9' &&
283             '0' <= chars[1] && chars[1] <= '9' &&
284             '0' <= chars[2] && chars[2] <= '9') {
285             jsint i = (chars[0] - '0') * 100 +
286                       (chars[1] - '0') * 10 +
287                       (chars[2] - '0');
288
289             if (jsuint(i) < INT_STRING_LIMIT)
290                 return intString(i);
291         }
292     }
293
294     return NULL;
295 }
296
297 inline void
298 JSString::finalize(JSContext *cx) {
299     JS_ASSERT(!JSString::isStatic(this));
300     JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
301     if (isDependent()) {
302         JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings);
303     } else if (isFlat()) {
304         /*
305          * flatChars for stillborn string is null, but cx->free checks
306          * for a null pointer on its own.
307          */
308         cx->runtime->stringMemoryUsed -= length() * 2;
309         cx->free(const_cast<jschar *>(flatChars()));
310     }
311 }
312
313 inline void
314 JSShortString::finalize(JSContext *cx)
315 {
316     JS_ASSERT(!JSString::isStatic(&mHeader));
317     JS_ASSERT(mHeader.isFlat());
318     JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
319 }
320
321 inline void
322 JSExternalString::finalize(JSContext *cx)
323 {
324     JS_ASSERT(unsigned(externalStringType) < JS_ARRAY_LENGTH(str_finalizers));
325     JS_ASSERT(!isStatic(this));
326     JS_ASSERT(isFlat());
327     JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
328
329     /* A stillborn string has null chars. */
330     jschar *chars = const_cast<jschar *>(flatChars());
331     if (!chars)
332         return;
333     JSStringFinalizeOp finalizer = str_finalizers[externalStringType];
334     if (finalizer)
335         finalizer(cx, this);
336 }
337
338 inline void
339 JSExternalString::finalize()
340 {
341     JS_ASSERT(unsigned(externalStringType) < JS_ARRAY_LENGTH(str_finalizers));
342     JSStringFinalizeOp finalizer = str_finalizers[externalStringType];
343     if (finalizer) {
344         /*
345          * Assume that the finalizer for the permanently interned
346          * string knows how to deal with null context.
347          */
348         finalizer(NULL, this);
349     }
350 }
351
352 namespace js {
353
354 class RopeBuilder {
355     JSContext *cx;
356     JSString *res;
357
358   public:
359     RopeBuilder(JSContext *cx)
360       : cx(cx), res(cx->runtime->emptyString)
361     {}
362
363     inline bool append(JSString *str) {
364         res = js_ConcatStrings(cx, res, str);
365         return !!res;
366     }
367
368     inline JSString *result() {
369         return res;
370     }
371 };
372
373 class StringSegmentRange
374 {
375     /*
376      * If malloc() shows up in any profiles from this vector, we can add a new
377      * StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx.
378      */
379     Vector<JSString *, 32> stack;
380     JSString *cur;
381
382     bool settle(JSString *str) {
383         while (str->isRope()) {
384             if (!stack.append(str->ropeRight()))
385                 return false;
386             str = str->ropeLeft();
387         }
388         cur = str;
389         return true;
390     }
391
392   public:
393     StringSegmentRange(JSContext *cx)
394       : stack(cx), cur(NULL)
395     {}
396
397     JS_WARN_UNUSED_RESULT bool init(JSString *str) {
398         JS_ASSERT(stack.empty());
399         return settle(str);
400     }
401
402     bool empty() const {
403         return cur == NULL;
404     }
405
406     JSString *front() const {
407         JS_ASSERT(!cur->isRope());
408         return cur;
409     }
410
411     JS_WARN_UNUSED_RESULT bool popFront() {
412         JS_ASSERT(!empty());
413         if (stack.empty()) {
414             cur = NULL;
415             return true;
416         }
417         return settle(stack.popCopy());
418     }
419 };
420
421 }  /* namespace js */
422
423 #endif /* jsstrinlines_h___ */