Make paren indexing uniform. (r=dmandelin, a=blocker, b=605754)
[mozilla-central.git] / js / src / jsstr.cpp
blob7f72461c206a21f042a09e6e51deebb64199a1d5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS string type implementation.
44 * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
45 * native methods store strings (possibly newborn) converted from their 'this'
46 * parameter and arguments on the stack: 'this' conversions at argv[-1], arg
47 * conversions at their index (argv[0], argv[1]). This is a legitimate method
48 * of rooting things that might lose their newborn root due to subsequent GC
49 * allocations in the same native method.
51 #include <stdlib.h>
52 #include <string.h>
53 #include "jstypes.h"
54 #include "jsstdint.h"
55 #include "jsutil.h"
56 #include "jshash.h"
57 #include "jsprf.h"
58 #include "jsapi.h"
59 #include "jsarray.h"
60 #include "jsatom.h"
61 #include "jsbool.h"
62 #include "jsbuiltins.h"
63 #include "jscntxt.h"
64 #include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
65 #include "jsgc.h"
66 #include "jsinterp.h"
67 #include "jslock.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsopcode.h"
71 #include "jsregexp.h"
72 #include "jsscope.h"
73 #include "jsstaticcheck.h"
74 #include "jsstr.h"
75 #include "jsbit.h"
76 #include "jsvector.h"
77 #include "jsversion.h"
79 #include "jscntxtinlines.h"
80 #include "jsinterpinlines.h"
81 #include "jsobjinlines.h"
82 #include "jsregexpinlines.h"
83 #include "jsstrinlines.h"
84 #include "jsautooplen.h" // generated headers last
86 using namespace js;
87 using namespace js::gc;
89 JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
90 JS_STATIC_ASSERT(JSString::MAX_LENGTH <= JSVAL_INT_MAX);
92 const jschar *
93 js_GetStringChars(JSContext *cx, JSString *str)
95 if (!js_MakeStringImmutable(cx, str))
96 return NULL;
97 return str->flatChars();
100 void
101 JSString::flatten()
103 JS_ASSERT(isRope());
106 * This can be called from any string in the rope, so first traverse to the
107 * top node.
109 JSString *topNode = this;
110 while (topNode->isInteriorNode())
111 topNode = topNode->interiorNodeParent();
113 const size_t length = topNode->length();
114 const size_t capacity = topNode->topNodeCapacity();
115 jschar *const chars = (jschar *) topNode->topNodeBuffer();
118 * To allow a homogeneous tree traversal, transform the top node into an
119 * internal node with null parent (which will be the stop condition).
121 topNode->e.mParent = NULL;
122 #ifdef DEBUG
123 topNode->mLengthAndFlags = JSString::INTERIOR_NODE;
124 #endif
127 * Perform a depth-first tree traversal, splatting each node's characters
128 * into a contiguous buffer. Visit each node three times:
129 * - the first time, record the position in the linear buffer, and recurse
130 * into the left child.
131 * - the second time, recurse into the right child.
132 * - the third time, transform the node into a dependent string.
133 * To avoid maintaining a stack, tree nodes are mutated to indicate how
134 * many times they have been visited.
136 JSString *str = topNode;
137 jschar *pos = chars;
138 while (true) {
139 /* Visiting the node for the first time. */
140 JS_ASSERT(str->isInteriorNode());
142 JSString *next = str->mLeft;
143 str->mChars = pos; /* N.B. aliases mLeft */
144 if (next->isInteriorNode()) {
145 str->mLengthAndFlags = 0x200; /* N.B. flags invalidated */
146 str = next;
147 continue; /* Visit node for the first time. */
148 } else {
149 size_t len = next->length();
150 PodCopy(pos, next->mChars, len);
151 pos += len;
152 goto visit_right_child;
156 revisit_parent:
157 if (str->mLengthAndFlags == 0x200) {
158 visit_right_child:
159 JSString *next = str->e.mRight;
160 if (next->isInteriorNode()) {
161 str->mLengthAndFlags = 0x300;
162 str = next;
163 continue; /* Visit 'str' for the first time. */
164 } else {
165 size_t len = next->length();
166 PodCopy(pos, next->mChars, len);
167 pos += len;
168 goto finish_node;
170 } else {
171 JS_ASSERT(str->mLengthAndFlags == 0x300);
172 finish_node:
173 JSString *next = str->e.mParent;
174 str->finishTraversalConversion(topNode, pos);
175 if (!next) {
176 JS_ASSERT(pos == chars + length);
177 *pos = 0;
178 topNode->initFlatExtensible(chars, length, capacity);
179 return;
181 str = next;
182 goto revisit_parent; /* Visit 'str' for the second or third time */
187 JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8);
188 JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
189 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
192 #ifdef JS_TRACER
194 int32 JS_FASTCALL
195 js_Flatten(JSString* str)
197 str->flatten();
198 return 0;
200 JS_DEFINE_CALLINFO_1(extern, INT32, js_Flatten, STRING, 0, nanojit::ACCSET_STORE_ANY)
202 #endif /* !JS_TRACER */
204 static JS_ALWAYS_INLINE size_t
205 RopeAllocSize(const size_t length, size_t *capacity)
207 static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
209 size_t size;
210 size_t minCap = (length + 1) * sizeof(jschar);
213 * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
214 * next power of 2. This is similar to what we do with arrays; see
215 * JSObject::ensureDenseArrayElements.
217 if (length > ROPE_DOUBLING_MAX)
218 size = minCap + (minCap / 8);
219 else
220 size = RoundUpPow2(minCap);
221 *capacity = (size / sizeof(jschar)) - 1;
222 JS_ASSERT(size >= sizeof(JSRopeBufferInfo));
223 return size;
226 static JS_ALWAYS_INLINE JSRopeBufferInfo *
227 ObtainRopeBuffer(JSContext *cx, bool usingLeft, bool usingRight,
228 JSRopeBufferInfo *sourceBuffer, size_t length,
229 JSString *left, JSString *right)
231 JSRopeBufferInfo *buf;
232 size_t capacity;
235 * We need to survive a GC upon failure and in case creating a new
236 * string header triggers a GC, but we've broken the invariant that
237 * rope top nodes always point to freeable JSRopeBufferInfo
238 * objects, so make them point to NULL.
240 if (usingLeft)
241 left->nullifyTopNodeBuffer();
242 if (usingRight)
243 right->nullifyTopNodeBuffer();
246 * Try to reuse sourceBuffer. If it's not suitable, free it and create a
247 * suitable buffer.
249 if (length <= sourceBuffer->capacity) {
250 buf = sourceBuffer;
251 } else {
252 size_t allocSize = RopeAllocSize(length, &capacity);
253 cx->free(sourceBuffer);
254 buf = (JSRopeBufferInfo *) cx->malloc(allocSize);
255 if (!buf)
256 return NULL;
257 buf->capacity = capacity;
259 return buf;
262 static JS_ALWAYS_INLINE JSString *
263 FinishConcat(JSContext *cx, bool usingLeft, bool usingRight,
264 JSString *left, JSString *right, size_t length,
265 JSRopeBufferInfo *buf)
267 JSString *res = js_NewGCString(cx);
268 if (!res) {
269 cx->free(buf);
270 return NULL;
272 res->initTopNode(left, right, length, buf);
273 if (usingLeft)
274 left->convertToInteriorNode(res);
275 if (usingRight)
276 right->convertToInteriorNode(res);
277 return res;
280 JSString * JS_FASTCALL
281 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
283 size_t length, leftLen, rightLen;
284 bool leftRopeTop, rightRopeTop;
286 leftLen = left->length();
287 if (leftLen == 0)
288 return right;
289 rightLen = right->length();
290 if (rightLen == 0)
291 return left;
293 length = leftLen + rightLen;
295 if (JSShortString::fitsIntoShortString(length)) {
296 JSShortString *shortStr = js_NewGCShortString(cx);
297 if (!shortStr)
298 return NULL;
300 jschar *buf = shortStr->init(length);
301 js_short_strncpy(buf, left->chars(), leftLen);
302 js_short_strncpy(buf + leftLen, right->chars(), rightLen);
303 buf[length] = 0;
304 return shortStr->header();
308 * We need to enforce a tree structure in ropes: every node needs to have a
309 * unique parent. So, we can't have the left or right child be in the middle
310 * of a rope tree. One potential solution is to traverse the subtree for the
311 * argument string and create a new flat string, but that would add
312 * complexity and is a rare case, so we simply flatten the entire rope that
313 * contains it. The case where left and right are part of the same rope is
314 * handled implicitly.
316 if (left->isInteriorNode())
317 left->flatten();
318 if (right->isInteriorNode())
319 right->flatten();
321 if (left->isExtensible() && !right->isRope() &&
322 left->flatCapacity() >= length) {
323 JS_ASSERT(left->isFlat());
326 * If left has enough unused space at the end of its buffer that we can
327 * fit the entire new string there, just write there.
329 jschar *chars = left->chars();
330 js_strncpy(chars + leftLen, right->chars(), rightLen);
331 chars[length] = 0;
332 JSString *res = js_NewString(cx, chars, length);
333 if (!res)
334 return NULL;
335 res->initFlatExtensible(chars, length, left->flatCapacity());
336 left->initDependent(res, res->flatChars(), leftLen);
337 return res;
340 if (length > JSString::MAX_LENGTH) {
341 if (JS_ON_TRACE(cx)) {
342 if (!CanLeaveTrace(cx))
343 return NULL;
344 LeaveTrace(cx);
346 js_ReportAllocationOverflow(cx);
347 return NULL;
350 leftRopeTop = left->isTopNode();
351 rightRopeTop = right->isTopNode();
354 * To make traversal more manageable, we enforce that, unless the children
355 * are leaves, the two children of a rope node must be distinct.
357 if (left == right && leftRopeTop) {
358 left->flatten();
359 leftRopeTop = false;
360 rightRopeTop = false;
361 JS_ASSERT(leftLen == left->length());
362 JS_ASSERT(rightLen == right->length());
363 JS_ASSERT(!left->isTopNode());
364 JS_ASSERT(!right->isTopNode());
368 * There are 4 cases, based on whether on whether the left or right is a
369 * rope or non-rope string.
371 JSRopeBufferInfo *buf = NULL;
373 if (leftRopeTop) {
374 /* Left child is a rope. */
375 JSRopeBufferInfo *leftBuf = left->topNodeBuffer();
377 /* If both children are ropes, steal the larger buffer. */
378 if (JS_UNLIKELY(rightRopeTop)) {
379 JSRopeBufferInfo *rightBuf = right->topNodeBuffer();
381 /* Put the larger buffer into 'leftBuf'. */
382 if (leftBuf->capacity >= rightBuf->capacity) {
383 cx->free(rightBuf);
384 } else {
385 cx->free(leftBuf);
386 leftBuf = rightBuf;
390 buf = ObtainRopeBuffer(cx, true, rightRopeTop, leftBuf, length, left, right);
391 if (!buf)
392 return NULL;
393 } else if (JS_UNLIKELY(rightRopeTop)) {
394 /* Right child is a rope: steal its buffer if big enough. */
395 JSRopeBufferInfo *rightBuf = right->topNodeBuffer();
397 buf = ObtainRopeBuffer(cx, false, true, rightBuf, length, left, right);
398 if (!buf)
399 return NULL;
400 } else {
401 /* Neither child is a rope: need to make a new buffer. */
402 size_t capacity;
403 size_t allocSize = RopeAllocSize(length, &capacity);
404 buf = (JSRopeBufferInfo *) cx->malloc(allocSize);
405 if (!buf)
406 return NULL;
407 buf->capacity = capacity;
410 return FinishConcat(cx, leftRopeTop, rightRopeTop, left, right, length, buf);
413 const jschar *
414 JSString::undepend(JSContext *cx)
416 size_t n, size;
417 jschar *s;
419 ensureNotRope();
421 if (isDependent()) {
422 n = dependentLength();
423 size = (n + 1) * sizeof(jschar);
424 s = (jschar *) cx->malloc(size);
425 if (!s)
426 return NULL;
428 js_strncpy(s, dependentChars(), n);
429 s[n] = 0;
430 initFlat(s, n);
432 #ifdef DEBUG
434 JSRuntime *rt = cx->runtime;
435 JS_RUNTIME_UNMETER(rt, liveDependentStrings);
436 JS_RUNTIME_UNMETER(rt, totalDependentStrings);
437 JS_LOCK_RUNTIME_VOID(rt,
438 (rt->strdepLengthSum -= (double)n,
439 rt->strdepLengthSquaredSum -= (double)n * (double)n));
441 #endif
444 return flatChars();
447 JSBool
448 js_MakeStringImmutable(JSContext *cx, JSString *str)
451 * Flattening a rope may result in a dependent string, so we need to flatten
452 * before undepending the string.
454 str->ensureNotRope();
455 if (!str->ensureNotDependent(cx)) {
456 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
457 return JS_FALSE;
459 str->flatClearExtensible();
460 return JS_TRUE;
463 static JSString *
464 ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg)
466 if (arg >= argc)
467 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
468 vp += 2 + arg;
470 if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp))
471 return NULL;
473 JSString *str;
474 if (vp->isString()) {
475 str = vp->toString();
476 } else if (vp->isBoolean()) {
477 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
478 (int)vp->toBoolean()]);
479 } else if (vp->isNull()) {
480 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
481 } else if (vp->isUndefined()) {
482 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
484 else {
485 str = js_NumberToString(cx, vp->toNumber());
486 if (str)
487 vp->setString(str);
489 return str;
493 * Forward declarations for URI encode/decode and helper routines
495 static JSBool
496 str_decodeURI(JSContext *cx, uintN argc, Value *vp);
498 static JSBool
499 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp);
501 static JSBool
502 str_encodeURI(JSContext *cx, uintN argc, Value *vp);
504 static JSBool
505 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp);
507 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
509 static uint32
510 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
513 * Contributions from the String class to the set of methods defined for the
514 * global object. escape and unescape used to be defined in the Mocha library,
515 * but as ECMA decided to spec them, they've been moved to the core engine
516 * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
517 * characters by unescape.)
521 * Stuff to emulate the old libmocha escape, which took a second argument
522 * giving the type of escape to perform. Retained for compatibility, and
523 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
526 #define URL_XALPHAS ((uint8) 1)
527 #define URL_XPALPHAS ((uint8) 2)
528 #define URL_PATH ((uint8) 4)
530 static const uint8 urlCharType[256] =
531 /* Bit 0 xalpha -- the alphas
532 * Bit 1 xpalpha -- as xalpha but
533 * converts spaces to plus and plus to %20
534 * Bit 2 ... path -- as xalphas but doesn't escape '/'
536 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
537 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
538 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
539 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
540 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
541 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
542 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
543 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
544 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
545 0, };
547 /* This matches the ECMA escape set when mask is 7 (default.) */
549 #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
551 /* See ECMA-262 Edition 3 B.2.1 */
552 JSBool
553 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
555 JSString *str;
556 size_t i, ni, length, newlength;
557 const jschar *chars;
558 jschar *newchars;
559 jschar ch;
560 jsint mask;
561 jsdouble d;
562 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
563 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
565 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
566 if (argc > 1) {
567 if (!ValueToNumber(cx, argv[1], &d))
568 return JS_FALSE;
569 if (!JSDOUBLE_IS_FINITE(d) ||
570 (mask = (jsint)d) != d ||
571 mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
573 char numBuf[12];
574 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
575 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
576 JSMSG_BAD_STRING_MASK, numBuf);
577 return JS_FALSE;
581 str = ArgToRootedString(cx, argc, argv - 2, 0);
582 if (!str)
583 return JS_FALSE;
585 str->getCharsAndLength(chars, length);
586 newlength = length;
588 /* Take a first pass and see how big the result string will need to be. */
589 for (i = 0; i < length; i++) {
590 if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
591 continue;
592 if (ch < 256) {
593 if (mask == URL_XPALPHAS && ch == ' ')
594 continue; /* The character will be encoded as '+' */
595 newlength += 2; /* The character will be encoded as %XX */
596 } else {
597 newlength += 5; /* The character will be encoded as %uXXXX */
601 * This overflow test works because newlength is incremented by at
602 * most 5 on each iteration.
604 if (newlength < length) {
605 js_ReportAllocationOverflow(cx);
606 return JS_FALSE;
610 if (newlength >= ~(size_t)0 / sizeof(jschar)) {
611 js_ReportAllocationOverflow(cx);
612 return JS_FALSE;
615 newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
616 if (!newchars)
617 return JS_FALSE;
618 for (i = 0, ni = 0; i < length; i++) {
619 if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
620 newchars[ni++] = ch;
621 } else if (ch < 256) {
622 if (mask == URL_XPALPHAS && ch == ' ') {
623 newchars[ni++] = '+'; /* convert spaces to pluses */
624 } else {
625 newchars[ni++] = '%';
626 newchars[ni++] = digits[ch >> 4];
627 newchars[ni++] = digits[ch & 0xF];
629 } else {
630 newchars[ni++] = '%';
631 newchars[ni++] = 'u';
632 newchars[ni++] = digits[ch >> 12];
633 newchars[ni++] = digits[(ch & 0xF00) >> 8];
634 newchars[ni++] = digits[(ch & 0xF0) >> 4];
635 newchars[ni++] = digits[ch & 0xF];
638 JS_ASSERT(ni == newlength);
639 newchars[newlength] = 0;
641 str = js_NewString(cx, newchars, newlength);
642 if (!str) {
643 cx->free(newchars);
644 return JS_FALSE;
646 rval->setString(str);
647 return JS_TRUE;
649 #undef IS_OK
651 static JSBool
652 str_escape(JSContext *cx, uintN argc, Value *vp)
654 JSObject *obj = ComputeThisFromVp(cx, vp);
655 return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
658 /* See ECMA-262 Edition 3 B.2.2 */
659 static JSBool
660 str_unescape(JSContext *cx, uintN argc, Value *vp)
662 JSString *str;
663 size_t i, ni, length;
664 const jschar *chars;
665 jschar *newchars;
666 jschar ch;
668 str = ArgToRootedString(cx, argc, vp, 0);
669 if (!str)
670 return JS_FALSE;
672 str->getCharsAndLength(chars, length);
674 /* Don't bother allocating less space for the new string. */
675 newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
676 if (!newchars)
677 return JS_FALSE;
678 ni = i = 0;
679 while (i < length) {
680 ch = chars[i++];
681 if (ch == '%') {
682 if (i + 1 < length &&
683 JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
685 ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
686 i += 2;
687 } else if (i + 4 < length && chars[i] == 'u' &&
688 JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
689 JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
691 ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
692 + JS7_UNHEX(chars[i + 2])) << 4)
693 + JS7_UNHEX(chars[i + 3])) << 4)
694 + JS7_UNHEX(chars[i + 4]);
695 i += 5;
698 newchars[ni++] = ch;
700 newchars[ni] = 0;
702 str = js_NewString(cx, newchars, ni);
703 if (!str) {
704 cx->free(newchars);
705 return JS_FALSE;
707 vp->setString(str);
708 return JS_TRUE;
711 #if JS_HAS_UNEVAL
712 static JSBool
713 str_uneval(JSContext *cx, uintN argc, Value *vp)
715 JSString *str;
717 str = js_ValueToSource(cx, argc != 0 ? vp[2] : UndefinedValue());
718 if (!str)
719 return JS_FALSE;
720 vp->setString(str);
721 return JS_TRUE;
723 #endif
725 const char js_escape_str[] = "escape";
726 const char js_unescape_str[] = "unescape";
727 #if JS_HAS_UNEVAL
728 const char js_uneval_str[] = "uneval";
729 #endif
730 const char js_decodeURI_str[] = "decodeURI";
731 const char js_encodeURI_str[] = "encodeURI";
732 const char js_decodeURIComponent_str[] = "decodeURIComponent";
733 const char js_encodeURIComponent_str[] = "encodeURIComponent";
735 static JSFunctionSpec string_functions[] = {
736 JS_FN(js_escape_str, str_escape, 1,0),
737 JS_FN(js_unescape_str, str_unescape, 1,0),
738 #if JS_HAS_UNEVAL
739 JS_FN(js_uneval_str, str_uneval, 1,0),
740 #endif
741 JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
742 JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
743 JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
744 JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
746 JS_FS_END
749 jschar js_empty_ucstr[] = {0};
750 JSSubString js_EmptySubString = {0, js_empty_ucstr};
752 static JSBool
753 str_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
755 JSString *str;
757 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
758 if (obj->getClass() == &js_StringClass) {
759 /* Follow ECMA-262 by fetching intrinsic length of our string. */
760 str = obj->getPrimitiveThis().toString();
761 } else {
762 /* Preserve compatibility: convert obj to a string primitive. */
763 str = js_ValueToString(cx, ObjectValue(*obj));
764 if (!str)
765 return JS_FALSE;
768 vp->setInt32(str->length());
771 return JS_TRUE;
774 #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
776 static JSBool
777 str_enumerate(JSContext *cx, JSObject *obj)
779 JSString *str, *str1;
780 size_t i, length;
782 str = obj->getPrimitiveThis().toString();
784 length = str->length();
785 for (i = 0; i < length; i++) {
786 str1 = js_NewDependentString(cx, str, i, 1);
787 if (!str1)
788 return JS_FALSE;
789 if (!obj->defineProperty(cx, INT_TO_JSID(i), StringValue(str1),
790 PropertyStub, PropertyStub,
791 STRING_ELEMENT_ATTRS)) {
792 return JS_FALSE;
796 return obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
797 UndefinedValue(), NULL, NULL,
798 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED);
801 static JSBool
802 str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
803 JSObject **objp)
805 if (!JSID_IS_INT(id))
806 return JS_TRUE;
808 JSString *str = obj->getPrimitiveThis().toString();
810 jsint slot = JSID_TO_INT(id);
811 if ((size_t)slot < str->length()) {
812 JSString *str1 = JSString::getUnitString(cx, str, size_t(slot));
813 if (!str1)
814 return JS_FALSE;
815 if (!obj->defineProperty(cx, id, StringValue(str1), NULL, NULL,
816 STRING_ELEMENT_ATTRS)) {
817 return JS_FALSE;
819 *objp = obj;
821 return JS_TRUE;
824 Class js_StringClass = {
825 js_String_str,
826 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
827 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
828 PropertyStub, /* addProperty */
829 PropertyStub, /* delProperty */
830 str_getProperty,
831 PropertyStub, /* setProperty */
832 str_enumerate,
833 (JSResolveOp)str_resolve,
834 ConvertStub
837 #define NORMALIZE_THIS(cx,vp,str) \
838 JS_BEGIN_MACRO \
839 if (vp[1].isString()) { \
840 str = vp[1].toString(); \
841 } else { \
842 str = NormalizeThis(cx, vp); \
843 if (!str) \
844 return JS_FALSE; \
846 JS_END_MACRO
848 static JSString *
849 NormalizeThis(JSContext *cx, Value *vp)
851 if (vp[1].isNullOrUndefined() && !ComputeThisFromVp(cx, vp))
852 return NULL;
855 * String.prototype.{toString,toSource,valueOf} throw a TypeError if the
856 * this-argument is not a string or a String object. So those methods use
857 * js::GetPrimitiveThis which provides that behavior.
859 * By standard, the rest of the String methods must ToString the
860 * this-argument rather than throw a TypeError. So those methods use
861 * NORMALIZE_THIS (and thus NormalizeThis) instead.
863 if (vp[1].isObject()) {
864 JSObject *obj = &vp[1].toObject();
865 if (obj->getClass() == &js_StringClass) {
866 vp[1] = obj->getPrimitiveThis();
867 return vp[1].toString();
871 JSString *str = js_ValueToString(cx, vp[1]);
872 if (!str)
873 return NULL;
874 vp[1].setString(str);
875 return str;
878 #if JS_HAS_TOSOURCE
881 * String.prototype.quote is generic (as are most string methods), unlike
882 * toSource, toString, and valueOf.
884 static JSBool
885 str_quote(JSContext *cx, uintN argc, Value *vp)
887 JSString *str;
889 NORMALIZE_THIS(cx, vp, str);
890 str = js_QuoteString(cx, str, '"');
891 if (!str)
892 return JS_FALSE;
893 vp->setString(str);
894 return JS_TRUE;
897 static JSBool
898 str_toSource(JSContext *cx, uintN argc, Value *vp)
900 JSString *str;
901 if (!GetPrimitiveThis(cx, vp, &str))
902 return false;
904 str = js_QuoteString(cx, str, '"');
905 if (!str)
906 return false;
908 char buf[16];
909 size_t j = JS_snprintf(buf, sizeof buf, "(new String(");
911 const jschar *s;
912 size_t k;
913 str->getCharsAndLength(s, k);
915 size_t n = j + k + 2;
916 jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
917 if (!t)
918 return false;
920 size_t i;
921 for (i = 0; i < j; i++)
922 t[i] = buf[i];
923 for (j = 0; j < k; i++, j++)
924 t[i] = s[j];
925 t[i++] = ')';
926 t[i++] = ')';
927 t[i] = 0;
929 str = js_NewString(cx, t, n);
930 if (!str) {
931 cx->free(t);
932 return false;
934 vp->setString(str);
935 return true;
938 #endif /* JS_HAS_TOSOURCE */
940 JSBool
941 js_str_toString(JSContext *cx, uintN argc, Value *vp)
943 JSString *str;
944 if (!GetPrimitiveThis(cx, vp, &str))
945 return false;
946 vp->setString(str);
947 return true;
951 * Java-like string native methods.
954 static JSString *
955 SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end)
957 if (begin < 0)
958 begin = 0;
959 else if (begin > length)
960 begin = length;
962 if (end < 0)
963 end = 0;
964 else if (end > length)
965 end = length;
966 if (end < begin) {
967 /* ECMA emulates old JDK1.0 java.lang.String.substring. */
968 jsdouble tmp = begin;
969 begin = end;
970 end = tmp;
973 return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin));
976 static JSBool
977 str_substring(JSContext *cx, uintN argc, Value *vp)
979 JSString *str;
980 jsdouble d;
981 jsdouble length, begin, end;
983 NORMALIZE_THIS(cx, vp, str);
984 if (argc != 0) {
985 if (!ValueToNumber(cx, vp[2], &d))
986 return JS_FALSE;
987 length = str->length();
988 begin = js_DoubleToInteger(d);
989 if (argc == 1 || vp[3].isUndefined()) {
990 end = length;
991 } else {
992 if (!ValueToNumber(cx, vp[3], &d))
993 return JS_FALSE;
994 end = js_DoubleToInteger(d);
997 str = SubstringTail(cx, str, length, begin, end);
998 if (!str)
999 return JS_FALSE;
1001 vp->setString(str);
1002 return JS_TRUE;
1005 JSString* JS_FASTCALL
1006 js_toLowerCase(JSContext *cx, JSString *str)
1008 size_t i, n;
1009 const jschar *s;
1010 jschar *news;
1012 str->getCharsAndLength(s, n);
1013 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
1014 if (!news)
1015 return NULL;
1016 for (i = 0; i < n; i++)
1017 news[i] = JS_TOLOWER(s[i]);
1018 news[n] = 0;
1019 str = js_NewString(cx, news, n);
1020 if (!str) {
1021 cx->free(news);
1022 return NULL;
1024 return str;
1027 static JSBool
1028 str_toLowerCase(JSContext *cx, uintN argc, Value *vp)
1030 JSString *str;
1032 NORMALIZE_THIS(cx, vp, str);
1033 str = js_toLowerCase(cx, str);
1034 if (!str)
1035 return JS_FALSE;
1036 vp->setString(str);
1037 return JS_TRUE;
1040 static JSBool
1041 str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp)
1043 JSString *str;
1046 * Forcefully ignore the first (or any) argument and return toLowerCase(),
1047 * ECMA has reserved that argument, presumably for defining the locale.
1049 if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
1050 NORMALIZE_THIS(cx, vp, str);
1051 return cx->localeCallbacks->localeToLowerCase(cx, str, Jsvalify(vp));
1053 return str_toLowerCase(cx, 0, vp);
1056 JSString* JS_FASTCALL
1057 js_toUpperCase(JSContext *cx, JSString *str)
1059 size_t i, n;
1060 const jschar *s;
1061 jschar *news;
1063 str->getCharsAndLength(s, n);
1064 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
1065 if (!news)
1066 return NULL;
1067 for (i = 0; i < n; i++)
1068 news[i] = JS_TOUPPER(s[i]);
1069 news[n] = 0;
1070 str = js_NewString(cx, news, n);
1071 if (!str) {
1072 cx->free(news);
1073 return NULL;
1075 return str;
1078 static JSBool
1079 str_toUpperCase(JSContext *cx, uintN argc, Value *vp)
1081 JSString *str;
1083 NORMALIZE_THIS(cx, vp, str);
1084 str = js_toUpperCase(cx, str);
1085 if (!str)
1086 return JS_FALSE;
1087 vp->setString(str);
1088 return JS_TRUE;
1091 static JSBool
1092 str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp)
1094 JSString *str;
1097 * Forcefully ignore the first (or any) argument and return toUpperCase(),
1098 * ECMA has reserved that argument, presumably for defining the locale.
1100 if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
1101 NORMALIZE_THIS(cx, vp, str);
1102 return cx->localeCallbacks->localeToUpperCase(cx, str, Jsvalify(vp));
1104 return str_toUpperCase(cx, 0, vp);
1107 static JSBool
1108 str_localeCompare(JSContext *cx, uintN argc, Value *vp)
1110 JSString *str, *thatStr;
1112 NORMALIZE_THIS(cx, vp, str);
1113 if (argc == 0) {
1114 vp->setInt32(0);
1115 } else {
1116 thatStr = js_ValueToString(cx, vp[2]);
1117 if (!thatStr)
1118 return JS_FALSE;
1119 if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
1120 vp[2].setString(thatStr);
1121 return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp));
1123 vp->setInt32(js_CompareStrings(str, thatStr));
1125 return JS_TRUE;
1128 JSBool
1129 js_str_charAt(JSContext *cx, uintN argc, Value *vp)
1131 JSString *str;
1132 jsint i;
1133 jsdouble d;
1135 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1136 str = vp[1].toString();
1137 i = vp[2].toInt32();
1138 if ((size_t)i >= str->length())
1139 goto out_of_range;
1140 } else {
1141 NORMALIZE_THIS(cx, vp, str);
1143 if (argc == 0) {
1144 d = 0.0;
1145 } else {
1146 if (!ValueToNumber(cx, vp[2], &d))
1147 return JS_FALSE;
1148 d = js_DoubleToInteger(d);
1151 if (d < 0 || str->length() <= d)
1152 goto out_of_range;
1153 i = (jsint) d;
1156 str = JSString::getUnitString(cx, str, size_t(i));
1157 if (!str)
1158 return JS_FALSE;
1159 vp->setString(str);
1160 return JS_TRUE;
1162 out_of_range:
1163 vp->setString(cx->runtime->emptyString);
1164 return JS_TRUE;
1167 JSBool
1168 js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp)
1170 JSString *str;
1171 jsint i;
1172 jsdouble d;
1174 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1175 str = vp[1].toString();
1176 i = vp[2].toInt32();
1177 if ((size_t)i >= str->length())
1178 goto out_of_range;
1179 } else {
1180 NORMALIZE_THIS(cx, vp, str);
1182 if (argc == 0) {
1183 d = 0.0;
1184 } else {
1185 if (!ValueToNumber(cx, vp[2], &d))
1186 return JS_FALSE;
1187 d = js_DoubleToInteger(d);
1190 if (d < 0 || str->length() <= d)
1191 goto out_of_range;
1192 i = (jsint) d;
1195 vp->setInt32(str->chars()[i]);
1196 return JS_TRUE;
1198 out_of_range:
1199 vp->setDouble(js_NaN);
1200 return JS_TRUE;
1203 jsint
1204 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1205 const jschar *pat, jsuint patlen)
1207 uint8 skip[sBMHCharSetSize];
1209 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1210 for (jsuint i = 0; i < sBMHCharSetSize; i++)
1211 skip[i] = (uint8)patlen;
1212 jsuint m = patlen - 1;
1213 for (jsuint i = 0; i < m; i++) {
1214 jschar c = pat[i];
1215 if (c >= sBMHCharSetSize)
1216 return sBMHBadPattern;
1217 skip[c] = (uint8)(m - i);
1219 jschar c;
1220 for (jsuint k = m;
1221 k < textlen;
1222 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1223 for (jsuint i = k, j = m; ; i--, j--) {
1224 if (text[i] != pat[j])
1225 break;
1226 if (j == 0)
1227 return static_cast<jsint>(i); /* safe: max string size */
1230 return -1;
1233 struct MemCmp {
1234 typedef jsuint Extent;
1235 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) {
1236 return (patlen - 1) * sizeof(jschar);
1238 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1239 return memcmp(p, t, extent) == 0;
1243 struct ManualCmp {
1244 typedef const jschar *Extent;
1245 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) {
1246 return pat + patlen;
1248 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1249 for (; p != extent; ++p, ++t) {
1250 if (*p != *t)
1251 return false;
1253 return true;
1257 template <class InnerMatch>
1258 static jsint
1259 UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
1261 JS_ASSERT(patlen > 0 && textlen > 0);
1262 const jschar *textend = text + textlen - (patlen - 1);
1263 const jschar p0 = *pat;
1264 const jschar *const patNext = pat + 1;
1265 const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
1266 uint8 fixup;
1268 const jschar *t = text;
1269 switch ((textend - t) & 7) {
1270 case 0: if (*t++ == p0) { fixup = 8; goto match; }
1271 case 7: if (*t++ == p0) { fixup = 7; goto match; }
1272 case 6: if (*t++ == p0) { fixup = 6; goto match; }
1273 case 5: if (*t++ == p0) { fixup = 5; goto match; }
1274 case 4: if (*t++ == p0) { fixup = 4; goto match; }
1275 case 3: if (*t++ == p0) { fixup = 3; goto match; }
1276 case 2: if (*t++ == p0) { fixup = 2; goto match; }
1277 case 1: if (*t++ == p0) { fixup = 1; goto match; }
1279 while (t != textend) {
1280 if (t[0] == p0) { t += 1; fixup = 8; goto match; }
1281 if (t[1] == p0) { t += 2; fixup = 7; goto match; }
1282 if (t[2] == p0) { t += 3; fixup = 6; goto match; }
1283 if (t[3] == p0) { t += 4; fixup = 5; goto match; }
1284 if (t[4] == p0) { t += 5; fixup = 4; goto match; }
1285 if (t[5] == p0) { t += 6; fixup = 3; goto match; }
1286 if (t[6] == p0) { t += 7; fixup = 2; goto match; }
1287 if (t[7] == p0) { t += 8; fixup = 1; goto match; }
1288 t += 8;
1289 continue;
1290 do {
1291 if (*t++ == p0) {
1292 match:
1293 if (!InnerMatch::match(patNext, t, extent))
1294 goto failed_match;
1295 return t - text - 1;
1297 failed_match:;
1298 } while (--fixup > 0);
1300 return -1;
1303 static JS_ALWAYS_INLINE jsint
1304 StringMatch(const jschar *text, jsuint textlen,
1305 const jschar *pat, jsuint patlen)
1307 if (patlen == 0)
1308 return 0;
1309 if (textlen < patlen)
1310 return -1;
1312 #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
1314 * Given enough registers, the unrolled loop below is faster than the
1315 * following loop. 32-bit x86 does not have enough registers.
1317 if (patlen == 1) {
1318 const jschar p0 = *pat;
1319 for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1320 if (*c == p0)
1321 return c - text;
1323 return -1;
1325 #endif
1328 * If the text or pattern string is short, BMH will be more expensive than
1329 * the basic linear scan due to initialization cost and a more complex loop
1330 * body. While the correct threshold is input-dependent, we can make a few
1331 * conservative observations:
1332 * - When |textlen| is "big enough", the initialization time will be
1333 * proportionally small, so the worst-case slowdown is minimized.
1334 * - When |patlen| is "too small", even the best case for BMH will be
1335 * slower than a simple scan for large |textlen| due to the more complex
1336 * loop body of BMH.
1337 * From this, the values for "big enough" and "too small" are determined
1338 * empirically. See bug 526348.
1340 if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
1341 jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1342 if (index != sBMHBadPattern)
1343 return index;
1347 * For big patterns with large potential overlap we want the SIMD-optimized
1348 * speed of memcmp. For small patterns, a simple loop is faster.
1350 * FIXME: Linux memcmp performance is sad and the manual loop is faster.
1352 return
1353 #if !defined(__linux__)
1354 patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
1356 #endif
1357 UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
1360 static const size_t sRopeMatchThresholdRatioLog2 = 5;
1362 static jsint
1363 RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen)
1365 JS_ASSERT(textstr->isTopNode());
1367 if (patlen == 0)
1368 return 0;
1369 if (textstr->length() < patlen)
1370 return -1;
1373 * List of leaf nodes in the rope. If we run out of memory when trying to
1374 * append to this list, we can still fall back to StringMatch, so use the
1375 * system allocator so we don't report OOM in that case.
1377 Vector<JSString *, 16, SystemAllocPolicy> strs;
1380 * We don't want to do rope matching if there is a poor node-to-char ratio,
1381 * since this means spending a lot of time in the match loop below. We also
1382 * need to build the list of leaf nodes. Do both here: iterate over the
1383 * nodes so long as there are not too many.
1385 size_t textstrlen = textstr->length();
1386 size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
1387 JSRopeLeafIterator iter(textstr);
1388 for (JSString *str = iter.init(); str; str = iter.next()) {
1389 if (threshold-- == 0 || !strs.append(str))
1390 return StringMatch(textstr->chars(), textstrlen, pat, patlen);
1393 /* Absolute offset from the beginning of the logical string textstr. */
1394 jsint pos = 0;
1396 // TODO: consider branching to a simple loop if patlen == 1
1398 for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
1399 /* First try to match without spanning two nodes. */
1400 const jschar *chars;
1401 size_t len;
1402 (*outerp)->getCharsAndLength(chars, len);
1403 jsint matchResult = StringMatch(chars, len, pat, patlen);
1404 if (matchResult != -1)
1405 return pos + matchResult;
1407 /* Test the overlap. */
1408 JSString **innerp = outerp;
1411 * Start searching at the first place where StringMatch wouldn't have
1412 * found the match.
1414 const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
1415 const jschar *const textend = chars + len;
1416 const jschar p0 = *pat;
1417 const jschar *const p1 = pat + 1;
1418 const jschar *const patend = pat + patlen;
1419 for (const jschar *t = text; t != textend; ) {
1420 if (*t++ != p0)
1421 continue;
1422 const jschar *ttend = textend;
1423 for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
1424 while (tt == ttend) {
1425 if (++innerp == strs.end())
1426 return -1;
1427 (*innerp)->getCharsAndEnd(tt, ttend);
1429 if (*pp != *tt)
1430 goto break_continue;
1433 /* Matched! */
1434 return pos + (t - chars) - 1; /* -1 because of *t++ above */
1436 break_continue:;
1439 pos += len;
1442 return -1;
1445 static JSBool
1446 str_indexOf(JSContext *cx, uintN argc, Value *vp)
1449 JSString *str;
1450 NORMALIZE_THIS(cx, vp, str);
1452 JSString *patstr = ArgToRootedString(cx, argc, vp, 0);
1453 if (!patstr)
1454 return JS_FALSE;
1456 const jschar *text = str->chars();
1457 jsuint textlen = str->length();
1458 const jschar *pat = patstr->chars();
1459 jsuint patlen = patstr->length();
1461 jsuint start;
1462 if (argc > 1) {
1463 if (vp[3].isInt32()) {
1464 jsint i = vp[3].toInt32();
1465 if (i <= 0) {
1466 start = 0;
1467 } else if (jsuint(i) > textlen) {
1468 start = textlen;
1469 textlen = 0;
1470 } else {
1471 start = i;
1472 text += start;
1473 textlen -= start;
1475 } else {
1476 jsdouble d;
1477 if (!ValueToNumber(cx, vp[3], &d))
1478 return JS_FALSE;
1479 d = js_DoubleToInteger(d);
1480 if (d <= 0) {
1481 start = 0;
1482 } else if (d > textlen) {
1483 start = textlen;
1484 textlen = 0;
1485 } else {
1486 start = (jsint)d;
1487 text += start;
1488 textlen -= start;
1491 } else {
1492 start = 0;
1495 jsint match = StringMatch(text, textlen, pat, patlen);
1496 vp->setInt32((match == -1) ? -1 : start + match);
1497 return true;
1500 static JSBool
1501 str_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
1503 JSString *str, *str2;
1504 const jschar *text, *pat;
1505 jsint i, j, textlen, patlen;
1506 jsdouble d;
1508 NORMALIZE_THIS(cx, vp, str);
1509 text = str->chars();
1510 textlen = (jsint) str->length();
1512 if (argc != 0 && vp[2].isString()) {
1513 str2 = vp[2].toString();
1514 } else {
1515 str2 = ArgToRootedString(cx, argc, vp, 0);
1516 if (!str2)
1517 return JS_FALSE;
1519 pat = str2->chars();
1520 patlen = (jsint) str2->length();
1522 i = textlen - patlen; // Start searching here
1523 if (i < 0) {
1524 vp->setInt32(-1);
1525 return JS_TRUE;
1528 if (argc > 1) {
1529 if (vp[3].isInt32()) {
1530 j = vp[3].toInt32();
1531 if (j <= 0)
1532 i = 0;
1533 else if (j < i)
1534 i = j;
1535 } else {
1536 if (!ValueToNumber(cx, vp[3], &d))
1537 return JS_FALSE;
1538 if (!JSDOUBLE_IS_NaN(d)) {
1539 d = js_DoubleToInteger(d);
1540 if (d <= 0)
1541 i = 0;
1542 else if (d < i)
1543 i = (jsint)d;
1548 if (patlen == 0) {
1549 vp->setInt32(i);
1550 return JS_TRUE;
1553 const jschar *t = text + i;
1554 const jschar *textend = text - 1;
1555 const jschar p0 = *pat;
1556 const jschar *patNext = pat + 1;
1557 const jschar *patEnd = pat + patlen;
1559 for (; t != textend; --t) {
1560 if (*t == p0) {
1561 const jschar *t1 = t + 1;
1562 for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1563 if (*t1 != *p1)
1564 goto break_continue;
1566 vp->setInt32(t - text);
1567 return JS_TRUE;
1569 break_continue:;
1572 vp->setInt32(-1);
1573 return JS_TRUE;
1576 static JSBool
1577 js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
1579 JSString *str;
1580 const jschar *chars;
1581 size_t length, begin, end;
1583 NORMALIZE_THIS(cx, vp, str);
1584 str->getCharsAndLength(chars, length);
1585 begin = 0;
1586 end = length;
1588 if (trimLeft) {
1589 while (begin < length && JS_ISSPACE(chars[begin]))
1590 ++begin;
1593 if (trimRight) {
1594 while (end > begin && JS_ISSPACE(chars[end-1]))
1595 --end;
1598 str = js_NewDependentString(cx, str, begin, end - begin);
1599 if (!str)
1600 return JS_FALSE;
1602 vp->setString(str);
1603 return JS_TRUE;
1606 static JSBool
1607 str_trim(JSContext *cx, uintN argc, Value *vp)
1609 return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
1612 static JSBool
1613 str_trimLeft(JSContext *cx, uintN argc, Value *vp)
1615 return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
1618 static JSBool
1619 str_trimRight(JSContext *cx, uintN argc, Value *vp)
1621 return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
1625 * Perl-inspired string functions.
1628 /* Result of a successfully performed flat match. */
1629 class FlatMatch
1631 JSString *patstr;
1632 const jschar *pat;
1633 size_t patlen;
1634 int32 match_;
1636 friend class RegExpGuard;
1638 public:
1639 FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
1640 JSString *pattern() const { return patstr; }
1641 size_t patternLength() const { return patlen; }
1644 * @note The match is -1 when the match is performed successfully,
1645 * but no match is found.
1647 int32 match() const { return match_; }
1650 /* A regexp and optional associated object. */
1651 class RegExpPair
1653 JSObject *reobj_;
1654 RegExp *re_;
1656 explicit RegExpPair(RegExp *re): re_(re) {}
1657 friend class RegExpGuard;
1659 public:
1660 /* @note May be null. */
1661 JSObject *reobj() const { return reobj_; }
1662 RegExp &re() const { JS_ASSERT(re_); return *re_; }
1666 * RegExpGuard factors logic out of String regexp operations.
1668 * @param optarg Indicates in which argument position RegExp
1669 * flags will be found, if present. This is a Mozilla
1670 * extension and not part of any ECMA spec.
1672 class RegExpGuard
1674 RegExpGuard(const RegExpGuard &);
1675 void operator=(const RegExpGuard &);
1677 JSContext *cx;
1678 RegExpPair rep;
1679 FlatMatch fm;
1682 * Upper bound on the number of characters we are willing to potentially
1683 * waste on searching for RegExp meta-characters.
1685 static const size_t MAX_FLAT_PAT_LEN = 256;
1687 static JSString *flattenPattern(JSContext *cx, JSString *patstr) {
1688 JSCharBuffer cb(cx);
1689 if (!cb.reserve(patstr->length()))
1690 return NULL;
1692 static const jschar ESCAPE_CHAR = '\\';
1693 const jschar *chars = patstr->chars();
1694 size_t len = patstr->length();
1695 for (const jschar *it = chars; it != chars + len; ++it) {
1696 if (RegExp::isMetaChar(*it)) {
1697 if (!cb.append(ESCAPE_CHAR) || !cb.append(*it))
1698 return NULL;
1699 } else {
1700 if (!cb.append(*it))
1701 return NULL;
1704 return js_NewStringFromCharBuffer(cx, cb);
1707 public:
1708 explicit RegExpGuard(JSContext *cx) : cx(cx), rep(NULL) {}
1710 ~RegExpGuard() {
1711 if (rep.re_)
1712 rep.re_->decref(cx);
1715 /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1716 bool
1717 init(uintN argc, Value *vp)
1719 if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
1720 rep.reobj_ = &vp[2].toObject();
1721 rep.re_ = RegExp::extractFrom(rep.reobj_);
1722 rep.re_->incref(cx);
1723 } else {
1724 fm.patstr = ArgToRootedString(cx, argc, vp, 0);
1725 if (!fm.patstr)
1726 return false;
1728 return true;
1732 * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
1733 * pattern string, or a lengthy pattern string can thwart this process.
1735 * @param checkMetaChars Look for regexp metachars in the pattern string.
1736 * @return Whether flat matching could be used.
1738 const FlatMatch *
1739 tryFlatMatch(JSString *textstr, uintN optarg, uintN argc, bool checkMetaChars = true)
1741 if (rep.re_)
1742 return NULL;
1744 fm.patstr->getCharsAndLength(fm.pat, fm.patlen);
1746 if (optarg < argc)
1747 return NULL;
1749 if (checkMetaChars &&
1750 (fm.patlen > MAX_FLAT_PAT_LEN || RegExp::hasMetaChars(fm.pat, fm.patlen))) {
1751 return NULL;
1755 * textstr could be a rope, so we want to avoid flattening it for as
1756 * long as possible.
1758 if (textstr->isTopNode()) {
1759 fm.match_ = RopeMatch(textstr, fm.pat, fm.patlen);
1760 } else {
1761 const jschar *text;
1762 size_t textlen;
1763 textstr->getCharsAndLength(text, textlen);
1764 fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
1766 return &fm;
1769 /* If the pattern is not already a regular expression, make it so. */
1770 const RegExpPair *
1771 normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
1773 /* If we don't have a RegExp, build RegExp from pattern string. */
1774 if (rep.re_)
1775 return &rep;
1777 JSString *opt;
1778 if (optarg < argc) {
1779 opt = js_ValueToString(cx, vp[2 + optarg]);
1780 if (!opt)
1781 return NULL;
1782 } else {
1783 opt = NULL;
1786 JSString *patstr;
1787 if (flat) {
1788 patstr = flattenPattern(cx, fm.patstr);
1789 if (!patstr)
1790 return false;
1791 } else {
1792 patstr = fm.patstr;
1794 JS_ASSERT(patstr);
1796 rep.re_ = RegExp::createFlagged(cx, patstr, opt);
1797 if (!rep.re_)
1798 return NULL;
1799 rep.reobj_ = NULL;
1800 return &rep;
1803 #if DEBUG
1804 bool hasRegExpPair() const { return rep.re_; }
1805 #endif
1808 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1809 static JS_ALWAYS_INLINE bool
1810 Matched(bool test, const Value &v)
1812 return test ? v.isTrue() : !v.isNull();
1815 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
1818 * BitOR-ing these flags allows the DoMatch caller to control when how the
1819 * RegExp engine is called and when callbacks are fired.
1821 enum MatchControlFlags {
1822 TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
1823 TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
1824 CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
1826 MATCH_ARGS = TEST_GLOBAL_BIT,
1827 MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1828 REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1831 /* Factor out looping and matching logic. */
1832 static bool
1833 DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
1834 DoMatchCallback callback, void *data, MatchControlFlags flags)
1836 RegExp &re = rep.re();
1837 if (re.global()) {
1838 /* global matching ('g') */
1839 bool testGlobal = flags & TEST_GLOBAL_BIT;
1840 if (rep.reobj())
1841 rep.reobj()->zeroRegExpLastIndex();
1842 for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1843 if (!re.execute(cx, res, str, &i, testGlobal, vp))
1844 return false;
1845 if (!Matched(testGlobal, *vp))
1846 break;
1847 if (!callback(cx, res, count, data))
1848 return false;
1849 if (!res->matched())
1850 ++i;
1852 } else {
1853 /* single match */
1854 bool testSingle = !!(flags & TEST_SINGLE_BIT),
1855 callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
1856 size_t i = 0;
1857 if (!re.execute(cx, res, str, &i, testSingle, vp))
1858 return false;
1859 if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
1860 return false;
1862 return true;
1865 static bool
1866 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
1868 if (fm.match() < 0) {
1869 vp->setNull();
1870 return true;
1873 /* For this non-global match, produce a RegExp.exec-style array. */
1874 JSObject *obj = js_NewSlowArrayObject(cx);
1875 if (!obj)
1876 return false;
1877 vp->setObject(*obj);
1879 return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) &&
1880 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1881 Int32Value(fm.match())) &&
1882 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1883 StringValue(textstr));
1886 typedef JSObject **MatchArgType;
1889 * DoMatch will only callback on global matches, hence this function builds
1890 * only the "array of matches" returned by match on global regexps.
1892 static bool
1893 MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
1895 JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */
1897 JSObject *&arrayobj = *static_cast<MatchArgType>(p);
1898 if (!arrayobj) {
1899 arrayobj = js_NewArrayObject(cx, 0, NULL);
1900 if (!arrayobj)
1901 return false;
1904 Value v;
1905 if (!res->createLastMatch(cx, &v))
1906 return false;
1908 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1909 return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false);
1912 static JSBool
1913 str_match(JSContext *cx, uintN argc, Value *vp)
1915 JSString *str;
1916 NORMALIZE_THIS(cx, vp, str);
1918 RegExpGuard g(cx);
1919 if (!g.init(argc, vp))
1920 return false;
1921 if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc))
1922 return BuildFlatMatchArray(cx, str, *fm, vp);
1924 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1925 if (!rep)
1926 return false;
1928 AutoObjectRooter array(cx);
1929 MatchArgType arg = array.addr();
1930 RegExpStatics *res = cx->regExpStatics();
1931 if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
1932 return false;
1934 /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
1935 if (rep->re().global())
1936 vp->setObjectOrNull(array.object());
1937 return true;
1940 static JSBool
1941 str_search(JSContext *cx, uintN argc, Value *vp)
1943 JSString *str;
1944 NORMALIZE_THIS(cx, vp, str);
1946 RegExpGuard g(cx);
1947 if (!g.init(argc, vp))
1948 return false;
1949 if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc)) {
1950 vp->setInt32(fm->match());
1951 return true;
1953 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1954 if (!rep)
1955 return false;
1957 RegExpStatics *res = cx->regExpStatics();
1958 size_t i = 0;
1959 if (!rep->re().execute(cx, res, str, &i, true, vp))
1960 return false;
1962 if (vp->isTrue())
1963 vp->setInt32(res->matchStart());
1964 else
1965 vp->setInt32(-1);
1966 return true;
1969 struct ReplaceData
1971 ReplaceData(JSContext *cx)
1972 : g(cx), cb(cx)
1975 JSString *str; /* 'this' parameter object as a string */
1976 RegExpGuard g; /* regexp parameter object and private data */
1977 JSObject *lambda; /* replacement function object or null */
1978 JSObject *elembase; /* object for function(a){return b[a]} replace */
1979 JSString *repstr; /* replacement string */
1980 jschar *dollar; /* null or pointer to first $ in repstr */
1981 jschar *dollarEnd; /* limit pointer for js_strchr_limit */
1982 jsint index; /* index in result of next replacement */
1983 jsint leftIndex; /* left context index in str->chars */
1984 JSSubString dollarStr; /* for "$$" InterpretDollar result */
1985 bool calledBack; /* record whether callback has been called */
1986 InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
1987 InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
1988 JSCharBuffer cb; /* buffer built during DoMatch */
1991 static bool
1992 InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, ReplaceData &rdata,
1993 JSSubString *out, size_t *skip, volatile JSContext::DollarPath *path)
1995 JS_ASSERT(*dp == '$');
1997 /* If there is only a dollar, bail now */
1998 if (dp + 1 >= ep)
1999 return false;
2001 /* Interpret all Perl match-induced dollar variables. */
2002 jschar dc = dp[1];
2003 if (JS7_ISDEC(dc)) {
2004 /* ECMA-262 Edition 3: 1-9 or 01-99 */
2005 uintN num = JS7_UNDEC(dc);
2006 if (num > res->parenCount())
2007 return false;
2009 jschar *cp = dp + 2;
2010 if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
2011 uintN tmp = 10 * num + JS7_UNDEC(dc);
2012 if (tmp <= res->parenCount()) {
2013 cp++;
2014 num = tmp;
2017 if (num == 0)
2018 return false;
2020 *skip = cp - dp;
2022 JS_CRASH_UNLESS(num <= res->parenCount());
2024 * Note: we index to get the paren with the (1-indexed) pair
2025 * number, as opposed to a (0-indexed) paren number.
2027 res->getParen(num, out);
2028 return true;
2031 *skip = 2;
2032 switch (dc) {
2033 case '$':
2034 rdata.dollarStr.chars = dp;
2035 rdata.dollarStr.length = 1;
2036 *out = rdata.dollarStr;
2037 *path = JSContext::DOLLAR_LITERAL;
2038 return true;
2039 case '&':
2040 res->getLastMatch(out);
2041 *path = JSContext::DOLLAR_AMP;
2042 return true;
2043 case '+':
2044 res->getLastParen(out);
2045 *path = JSContext::DOLLAR_PLUS;
2046 return true;
2047 case '`':
2048 res->getLeftContext(out);
2049 *path = JSContext::DOLLAR_TICK;
2050 return true;
2051 case '\'':
2052 res->getRightContext(out);
2053 *path = JSContext::DOLLAR_QUOT;
2054 return true;
2056 return false;
2059 static bool
2060 FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
2062 JSObject *base = rdata.elembase;
2063 if (base) {
2065 * The base object is used when replace was passed a lambda which looks like
2066 * 'function(a) { return b[a]; }' for the base object b. b will not change
2067 * in the course of the replace unless we end up making a scripted call due
2068 * to accessing a scripted getter or a value with a scripted toString.
2070 JS_ASSERT(rdata.lambda);
2071 JS_ASSERT(!base->getOps()->lookupProperty);
2072 JS_ASSERT(!base->getOps()->getProperty);
2074 Value match;
2075 if (!res->createLastMatch(cx, &match))
2076 return false;
2077 JSString *str = match.toString();
2079 JSAtom *atom;
2080 if (str->isAtomized()) {
2081 atom = STRING_TO_ATOM(str);
2082 } else {
2083 atom = js_AtomizeString(cx, str, 0);
2084 if (!atom)
2085 return false;
2087 jsid id = ATOM_TO_JSID(atom);
2089 JSObject *holder;
2090 JSProperty *prop = NULL;
2091 if (js_LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop) < 0)
2092 return false;
2094 /* Only handle the case where the property exists and is on this object. */
2095 if (prop && holder == base) {
2096 Shape *shape = (Shape *) prop;
2097 if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) {
2098 Value value = base->getSlot(shape->slot);
2099 if (value.isString()) {
2100 rdata.repstr = value.toString();
2101 *sizep = rdata.repstr->length();
2102 return true;
2108 * Couldn't handle this property, fall through and despecialize to the
2109 * general lambda case.
2111 rdata.elembase = NULL;
2114 JSObject *lambda = rdata.lambda;
2115 if (lambda) {
2117 * In the lambda case, not only do we find the replacement string's
2118 * length, we compute repstr and return it via rdata for use within
2119 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
2120 * index, input), i.e., all the properties of a regexp match array.
2121 * For $&, etc., we must create string jsvals from cx->regExpStatics.
2122 * We grab up stack space to keep the newborn strings GC-rooted.
2124 uintN p = res->parenCount();
2125 uintN argc = 1 + p + 2;
2127 InvokeSessionGuard &session = rdata.session;
2128 if (!session.started()) {
2129 Value lambdav = ObjectValue(*lambda);
2130 if (!session.start(cx, lambdav, UndefinedValue(), argc))
2131 return false;
2134 PreserveRegExpStatics staticsGuard(res);
2135 if (!staticsGuard.init(cx))
2136 return false;
2138 /* Push $&, $1, $2, ... */
2139 uintN argi = 0;
2140 if (!res->createLastMatch(cx, &session[argi++]))
2141 return false;
2143 for (size_t i = 0; i < res->parenCount(); ++i) {
2144 if (!res->createParen(cx, i + 1, &session[argi++]))
2145 return false;
2148 /* Push match index and input string. */
2149 session[argi++].setInt32(res->matchStart());
2150 session[argi].setString(rdata.str);
2152 if (!session.invoke(cx))
2153 return false;
2155 /* root repstr: rdata is on the stack, so scanned by conservative gc. */
2156 rdata.repstr = ValueToString_TestForStringInline(cx, session.rval());
2157 if (!rdata.repstr)
2158 return false;
2160 *sizep = rdata.repstr->length();
2161 return true;
2164 JSString *repstr = rdata.repstr;
2165 size_t replen = repstr->length();
2166 JSContext::DollarPath path;
2167 for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
2168 JSSubString sub;
2169 size_t skip;
2170 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip, &path)) {
2171 replen += sub.length - skip;
2172 dp += skip;
2173 } else {
2174 dp++;
2177 *sizep = replen;
2178 return true;
2181 static void
2182 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars)
2184 JSString *repstr = rdata.repstr;
2185 jschar *cp;
2186 jschar *bp = cp = repstr->chars();
2187 volatile JSContext::DollarPath path;
2188 cx->dollarPath = &path;
2189 jschar sourceBuf[128];
2190 cx->blackBox = sourceBuf;
2192 for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
2193 size_t len = dp - cp;
2194 js_strncpy(chars, cp, len);
2195 chars += len;
2196 cp = dp;
2198 JSSubString sub;
2199 size_t skip;
2200 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip, &path)) {
2201 if (((size_t(sub.chars) & 0xfffffU) + sub.length) > 0x100000U) {
2202 /* Going to cross a 0xffffe address, so take a gander at the replace value. */
2203 size_t peekLen = JS_MIN(rdata.dollarEnd - rdata.dollar, 128);
2204 js_strncpy(sourceBuf, rdata.dollar, peekLen);
2207 len = sub.length;
2208 js_strncpy(chars, sub.chars, len);
2209 chars += len;
2210 cp += skip;
2211 dp += skip;
2212 } else {
2213 dp++;
2216 js_strncpy(chars, cp, repstr->length() - (cp - bp));
2219 static bool
2220 ReplaceCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
2222 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
2224 rdata.calledBack = true;
2225 JSString *str = rdata.str;
2226 size_t leftoff = rdata.leftIndex;
2227 const jschar *left = str->chars() + leftoff;
2228 size_t leftlen = res->matchStart() - leftoff;
2229 rdata.leftIndex = res->matchLimit();
2231 size_t replen = 0; /* silence 'unused' warning */
2232 if (!FindReplaceLength(cx, res, rdata, &replen))
2233 return false;
2235 size_t growth = leftlen + replen;
2236 if (!rdata.cb.growByUninitialized(growth))
2237 return false;
2239 jschar *chars = rdata.cb.begin() + rdata.index;
2240 rdata.index += growth;
2241 js_strncpy(chars, left, leftlen);
2242 chars += leftlen;
2243 DoReplace(cx, res, rdata, chars);
2244 return true;
2247 static bool
2248 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2249 const FlatMatch &fm, Value *vp)
2251 JSRopeBuilder builder(cx);
2252 size_t match = fm.match(); /* Avoid signed/unsigned warnings. */
2253 size_t matchEnd = match + fm.patternLength();
2255 if (textstr->isTopNode()) {
2257 * If we are replacing over a rope, avoid flattening it by iterating
2258 * through it, building a new rope.
2260 JSRopeLeafIterator iter(textstr);
2261 size_t pos = 0;
2262 for (JSString *str = iter.init(); str; str = iter.next()) {
2263 size_t len = str->length();
2264 size_t strEnd = pos + len;
2265 if (pos < matchEnd && strEnd > match) {
2267 * We need to special-case any part of the rope that overlaps
2268 * with the replacement string.
2270 if (match >= pos) {
2272 * If this part of the rope overlaps with the left side of
2273 * the pattern, then it must be the only one to overlap with
2274 * the first character in the pattern, so we include the
2275 * replacement string here.
2277 JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
2278 if (!leftSide ||
2279 !builder.append(leftSide) ||
2280 !builder.append(repstr)) {
2281 return false;
2286 * If str runs off the end of the matched string, append the
2287 * last part of str.
2289 if (strEnd > matchEnd) {
2290 JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
2291 strEnd - matchEnd);
2292 if (!rightSide || !builder.append(rightSide))
2293 return false;
2295 } else {
2296 if (!builder.append(str))
2297 return false;
2299 pos += str->length();
2301 } else {
2302 JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
2303 if (!leftSide)
2304 return false;
2305 JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
2306 textstr->length() - match - fm.patternLength());
2307 if (!rightSide ||
2308 !builder.append(leftSide) ||
2309 !builder.append(repstr) ||
2310 !builder.append(rightSide)) {
2311 return false;
2315 vp->setString(builder.getStr());
2316 return true;
2320 * Perform a linear-scan dollar substitution on the replacement text,
2321 * constructing a result string that looks like:
2323 * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
2325 static inline bool
2326 BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2327 const jschar *firstDollar, const FlatMatch &fm, Value *vp)
2329 JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
2330 size_t matchStart = fm.match();
2331 size_t matchLimit = matchStart + fm.patternLength();
2332 JSCharBuffer newReplaceChars(cx);
2335 * Most probably:
2337 * len(newstr) >= len(orig) - len(match) + len(replacement)
2339 * Note that dollar vars _could_ make the resulting text smaller than this.
2341 if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
2342 return false;
2344 /* Move the pre-dollar chunk in bulk. */
2345 JS_ALWAYS_TRUE(newReplaceChars.append(repstr->chars(), firstDollar));
2347 /* Move the rest char-by-char, interpreting dollars as we encounter them. */
2348 #define ENSURE(__cond) if (!(__cond)) return false;
2349 const jschar *repstrLimit = repstr->chars() + repstr->length();
2350 for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
2351 if (*it != '$' || it == repstrLimit - 1) {
2352 ENSURE(newReplaceChars.append(*it));
2353 continue;
2356 switch (*(it + 1)) {
2357 case '$': /* Eat one of the dollars. */
2358 ENSURE(newReplaceChars.append(*it));
2359 break;
2360 case '&':
2361 ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
2362 textstr->chars() + matchLimit));
2363 break;
2364 case '`':
2365 ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
2366 break;
2367 case '\'':
2368 ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
2369 textstr->chars() + textstr->length()));
2370 break;
2371 default: /* The dollar we saw was not special (no matter what its mother told it). */
2372 ENSURE(newReplaceChars.append(*it));
2373 continue;
2375 ++it; /* We always eat an extra char in the above switch. */
2378 JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
2379 ENSURE(leftSide);
2381 JSString *newReplace = js_NewStringFromCharBuffer(cx, newReplaceChars);
2382 ENSURE(newReplace);
2384 JS_ASSERT(textstr->length() >= matchLimit);
2385 JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
2386 textstr->length() - matchLimit);
2387 ENSURE(rightSide);
2389 JSRopeBuilder builder(cx);
2390 ENSURE(builder.append(leftSide) &&
2391 builder.append(newReplace) &&
2392 builder.append(rightSide));
2393 #undef ENSURE
2395 vp->setString(builder.getStr());
2396 return true;
2399 static inline bool
2400 str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
2402 const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
2403 if (!rep)
2404 return false;
2406 rdata.index = 0;
2407 rdata.leftIndex = 0;
2408 rdata.calledBack = false;
2410 RegExpStatics *res = cx->regExpStatics();
2411 if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS))
2412 return false;
2414 if (!rdata.calledBack) {
2415 /* Didn't match, so the string is unmodified. */
2416 vp->setString(rdata.str);
2417 return true;
2420 JSSubString sub;
2421 res->getRightContext(&sub);
2422 if (!rdata.cb.append(sub.chars, sub.length))
2423 return false;
2425 JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
2426 if (!retstr)
2427 return false;
2429 vp->setString(retstr);
2430 return true;
2433 static inline bool
2434 str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata,
2435 const FlatMatch &fm)
2437 JS_ASSERT(fm.match() >= 0);
2438 LeaveTrace(cx);
2440 JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
2441 if (!matchStr)
2442 return false;
2444 /* lambda(matchStr, matchStart, textstr) */
2445 static const uint32 lambdaArgc = 3;
2446 if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
2447 return false;
2449 CallArgs &args = rdata.singleShot;
2450 args.callee().setObject(*rdata.lambda);
2451 args.thisv().setUndefined();
2453 Value *sp = args.argv();
2454 sp[0].setString(matchStr);
2455 sp[1].setInt32(fm.match());
2456 sp[2].setString(rdata.str);
2458 if (!Invoke(cx, rdata.singleShot, 0))
2459 return false;
2461 JSString *repstr = js_ValueToString(cx, args.rval());
2462 if (!repstr)
2463 return false;
2465 JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
2466 if (!leftSide)
2467 return false;
2469 size_t matchLimit = fm.match() + fm.patternLength();
2470 JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
2471 rdata.str->length() - matchLimit);
2472 if (!rightSide)
2473 return false;
2475 JSRopeBuilder builder(cx);
2476 if (!(builder.append(leftSide) &&
2477 builder.append(repstr) &&
2478 builder.append(rightSide))) {
2479 return false;
2482 vp->setString(builder.getStr());
2483 return true;
2486 JSBool
2487 js::str_replace(JSContext *cx, uintN argc, Value *vp)
2489 ReplaceData rdata(cx);
2490 NORMALIZE_THIS(cx, vp, rdata.str);
2491 static const uint32 optarg = 2;
2493 /* Extract replacement string/function. */
2494 if (argc >= optarg && js_IsCallable(vp[3])) {
2495 rdata.lambda = &vp[3].toObject();
2496 rdata.elembase = NULL;
2497 rdata.repstr = NULL;
2498 rdata.dollar = rdata.dollarEnd = NULL;
2500 if (rdata.lambda->isFunction()) {
2501 JSFunction *fun = rdata.lambda->getFunctionPrivate();
2502 if (fun->isInterpreted()) {
2504 * Pattern match the script to check if it is is indexing into a
2505 * particular object, e.g. 'function(a) { return b[a]; }'. Avoid
2506 * calling the script in such cases, which are used by javascript
2507 * packers (particularly the popular Dean Edwards packer) to efficiently
2508 * encode large scripts. We only handle the code patterns generated
2509 * by such packers here.
2511 JSScript *script = fun->u.i.script;
2512 jsbytecode *pc = script->code;
2514 Value table = UndefinedValue();
2515 if (JSOp(*pc) == JSOP_GETFCSLOT) {
2516 table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc));
2517 pc += JSOP_GETFCSLOT_LENGTH;
2520 if (table.isObject() &&
2521 JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
2522 JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM &&
2523 JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) {
2524 Class *clasp = table.toObject().getClass();
2525 if (clasp->isNative() &&
2526 !clasp->ops.lookupProperty &&
2527 !clasp->ops.getProperty) {
2528 rdata.elembase = &table.toObject();
2533 } else {
2534 rdata.lambda = NULL;
2535 rdata.elembase = NULL;
2536 rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
2537 if (!rdata.repstr)
2538 return false;
2540 /* We're about to store pointers into the middle of our string. */
2541 if (!js_MakeStringImmutable(cx, rdata.repstr))
2542 return false;
2543 rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
2544 rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
2545 rdata.dollarEnd);
2548 if (!rdata.g.init(argc, vp))
2549 return false;
2552 * Unlike its |String.prototype| brethren, |replace| doesn't convert
2553 * its input to a regular expression. (Even if it contains metachars.)
2555 * However, if the user invokes our (non-standard) |flags| argument
2556 * extension then we revert to creating a regular expression. Note that
2557 * this is observable behavior through the side-effect mutation of the
2558 * |RegExp| statics.
2561 const FlatMatch *fm = rdata.g.tryFlatMatch(rdata.str, optarg, argc, false);
2562 if (!fm) {
2563 JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
2564 return str_replace_regexp(cx, argc, vp, rdata);
2567 if (fm->match() < 0) {
2568 vp->setString(rdata.str);
2569 return true;
2572 if (rdata.lambda)
2573 return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
2576 * Note: we could optimize the text.length == pattern.length case if we wanted,
2577 * even in the presence of dollar metachars.
2579 if (rdata.dollar)
2580 return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
2582 return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
2586 * Subroutine used by str_split to find the next split point in str, starting
2587 * at offset *ip and looking either for the separator substring given by sep, or
2588 * for the next re match. In the re case, return the matched separator in *sep,
2589 * and the possibly updated offset in *ip.
2591 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
2592 * separator occurrence if found, or str->length if no separator is found.
2594 static jsint
2595 find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
2596 JSSubString *sep)
2598 jsint i;
2599 size_t length;
2600 jschar *chars;
2603 * Stop if past end of string. If at end of string, we will compare the
2604 * null char stored there (by js_NewString*) to sep->chars[j] in the while
2605 * loop at the end of this function, so that
2607 * "ab,".split(',') => ["ab", ""]
2609 * and the resulting array converts back to the string "ab," for symmetry.
2610 * However, we ape Perl and do this only if there is a sufficiently large
2611 * limit argument (see str_split).
2613 i = *ip;
2614 length = str->length();
2615 if ((size_t)i > length)
2616 return -1;
2618 chars = str->chars();
2621 * Match a regular expression against the separator at or above index i.
2622 * Call js_ExecuteRegExp with true for the test argument. On successful
2623 * match, get the separator from cx->regExpStatics.lastMatch.
2625 if (re) {
2626 size_t index;
2627 Value rval;
2629 again:
2630 /* JS1.2 deviated from Perl by never matching at end of string. */
2631 index = (size_t)i;
2632 if (!re->execute(cx, res, str, &index, true, &rval))
2633 return -2;
2634 if (!rval.isTrue()) {
2635 /* Mismatch: ensure our caller advances i past end of string. */
2636 sep->length = 1;
2637 return length;
2639 i = (jsint)index;
2640 JS_ASSERT(sep);
2641 res->getLastMatch(sep);
2642 if (sep->length == 0) {
2644 * Empty string match: never split on an empty match at the start
2645 * of a find_split cycle. Same rule as for an empty global match
2646 * in DoMatch.
2648 if (i == *ip) {
2650 * "Bump-along" to avoid sticking at an empty match, but don't
2651 * bump past end of string -- our caller must do that by adding
2652 * sep->length to our return value.
2654 if ((size_t)i == length)
2655 return -1;
2656 i++;
2657 goto again;
2659 if ((size_t)i == length) {
2661 * If there was a trivial zero-length match at the end of the
2662 * split, then we shouldn't output the matched string at the end
2663 * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
2665 sep->chars = NULL;
2668 JS_ASSERT((size_t)i >= sep->length);
2669 return i - sep->length;
2673 * Special case: if sep is the empty string, split str into one character
2674 * substrings. Let our caller worry about whether to split once at end of
2675 * string into an empty substring.
2677 if (sep->length == 0)
2678 return ((size_t)i == length) ? -1 : i + 1;
2681 * Now that we know sep is non-empty, search starting at i in str for an
2682 * occurrence of all of sep's chars. If we find them, return the index of
2683 * the first separator char. Otherwise, return length.
2685 jsint match = StringMatch(chars + i, length - i, sep->chars, sep->length);
2686 return match == -1 ? length : match + i;
2689 static JSBool
2690 str_split(JSContext *cx, uintN argc, Value *vp)
2692 JSString *str;
2693 NORMALIZE_THIS(cx, vp, str);
2695 if (argc == 0) {
2696 Value v = StringValue(str);
2697 JSObject *aobj = js_NewArrayObject(cx, 1, &v);
2698 if (!aobj)
2699 return false;
2700 vp->setObject(*aobj);
2701 return true;
2704 RegExp *re;
2705 JSSubString *sep, tmp;
2706 if (VALUE_IS_REGEXP(cx, vp[2])) {
2707 re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
2708 sep = &tmp;
2710 /* Set a magic value so we can detect a successful re match. */
2711 sep->chars = NULL;
2712 sep->length = 0;
2713 } else {
2714 JSString *str2 = js_ValueToString(cx, vp[2]);
2715 if (!str2)
2716 return false;
2717 vp[2].setString(str2);
2720 * Point sep at a local copy of str2's header because find_split
2721 * will modify sep->length.
2723 str2->getCharsAndLength(tmp.chars, tmp.length);
2724 sep = &tmp;
2725 re = NULL;
2728 /* Use the second argument as the split limit, if given. */
2729 uint32 limit = 0; /* Avoid warning. */
2730 bool limited = (argc > 1) && !vp[3].isUndefined();
2731 if (limited) {
2732 jsdouble d;
2733 if (!ValueToNumber(cx, vp[3], &d))
2734 return false;
2736 /* Clamp limit between 0 and 1 + string length. */
2737 limit = js_DoubleToECMAUint32(d);
2738 if (limit > str->length())
2739 limit = 1 + str->length();
2742 AutoValueVector splits(cx);
2744 RegExpStatics *res = cx->regExpStatics();
2745 jsint i, j;
2746 uint32 len = i = 0;
2747 while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
2748 if (limited && len >= limit)
2749 break;
2751 JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
2752 if (!sub || !splits.append(StringValue(sub)))
2753 return false;
2754 len++;
2757 * Imitate perl's feature of including parenthesized substrings that
2758 * matched part of the delimiter in the new array, after the split
2759 * substring that was delimited.
2761 if (re && sep->chars) {
2762 for (uintN num = 0; num < res->parenCount(); num++) {
2763 if (limited && len >= limit)
2764 break;
2765 JSSubString parsub;
2766 res->getParen(num + 1, &parsub);
2767 sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
2768 if (!sub || !splits.append(StringValue(sub)))
2769 return false;
2770 len++;
2772 sep->chars = NULL;
2774 i = j + sep->length;
2777 if (j == -2)
2778 return false;
2780 JSObject *aobj = js_NewArrayObject(cx, splits.length(), splits.begin());
2781 if (!aobj)
2782 return false;
2783 vp->setObject(*aobj);
2784 return true;
2787 #if JS_HAS_PERL_SUBSTR
2788 static JSBool
2789 str_substr(JSContext *cx, uintN argc, Value *vp)
2791 JSString *str;
2792 jsdouble d;
2793 jsdouble length, begin, end;
2795 NORMALIZE_THIS(cx, vp, str);
2796 if (argc != 0) {
2797 if (!ValueToNumber(cx, vp[2], &d))
2798 return JS_FALSE;
2799 length = str->length();
2800 begin = js_DoubleToInteger(d);
2801 if (begin < 0) {
2802 begin += length;
2803 if (begin < 0)
2804 begin = 0;
2805 } else if (begin > length) {
2806 begin = length;
2809 if (argc == 1 || vp[3].isUndefined()) {
2810 end = length;
2811 } else {
2812 if (!ValueToNumber(cx, vp[3], &d))
2813 return JS_FALSE;
2814 end = js_DoubleToInteger(d);
2815 if (end < 0)
2816 end = 0;
2817 end += begin;
2818 if (end > length)
2819 end = length;
2822 str = js_NewDependentString(cx, str,
2823 (size_t)begin,
2824 (size_t)(end - begin));
2825 if (!str)
2826 return JS_FALSE;
2828 vp->setString(str);
2829 return JS_TRUE;
2831 #endif /* JS_HAS_PERL_SUBSTR */
2834 * Python-esque sequence operations.
2836 static JSBool
2837 str_concat(JSContext *cx, uintN argc, Value *vp)
2839 JSString *str, *str2;
2840 Value *argv;
2841 uintN i;
2843 NORMALIZE_THIS(cx, vp, str);
2845 /* Set vp (aka rval) early to handle the argc == 0 case. */
2846 vp->setString(str);
2848 for (i = 0, argv = vp + 2; i < argc; i++) {
2849 str2 = js_ValueToString(cx, argv[i]);
2850 if (!str2)
2851 return JS_FALSE;
2852 argv[i].setString(str2);
2854 str = js_ConcatStrings(cx, str, str2);
2855 if (!str)
2856 return JS_FALSE;
2857 vp->setString(str);
2860 return JS_TRUE;
2863 static JSBool
2864 str_slice(JSContext *cx, uintN argc, Value *vp)
2866 if (argc == 1 && vp[1].isString() && vp[2].isInt32()) {
2867 size_t begin, end, length;
2869 JSString *str = vp[1].toString();
2870 begin = vp[2].toInt32();
2871 end = str->length();
2872 if (begin <= end) {
2873 length = end - begin;
2874 if (length == 0) {
2875 str = cx->runtime->emptyString;
2876 } else {
2877 str = (length == 1)
2878 ? JSString::getUnitString(cx, str, begin)
2879 : js_NewDependentString(cx, str, begin, length);
2880 if (!str)
2881 return JS_FALSE;
2883 vp->setString(str);
2884 return JS_TRUE;
2888 JSString *str;
2889 NORMALIZE_THIS(cx, vp, str);
2891 if (argc != 0) {
2892 double begin, end, length;
2894 if (!ValueToNumber(cx, vp[2], &begin))
2895 return JS_FALSE;
2896 begin = js_DoubleToInteger(begin);
2897 length = str->length();
2898 if (begin < 0) {
2899 begin += length;
2900 if (begin < 0)
2901 begin = 0;
2902 } else if (begin > length) {
2903 begin = length;
2906 if (argc == 1 || vp[3].isUndefined()) {
2907 end = length;
2908 } else {
2909 if (!ValueToNumber(cx, vp[3], &end))
2910 return JS_FALSE;
2911 end = js_DoubleToInteger(end);
2912 if (end < 0) {
2913 end += length;
2914 if (end < 0)
2915 end = 0;
2916 } else if (end > length) {
2917 end = length;
2919 if (end < begin)
2920 end = begin;
2923 str = js_NewDependentString(cx, str,
2924 (size_t)begin,
2925 (size_t)(end - begin));
2926 if (!str)
2927 return JS_FALSE;
2929 vp->setString(str);
2930 return JS_TRUE;
2933 #if JS_HAS_STR_HTML_HELPERS
2935 * HTML composition aids.
2937 static JSBool
2938 tagify(JSContext *cx, const char *begin, JSString *param, const char *end,
2939 Value *vp)
2941 JSString *str;
2942 jschar *tagbuf;
2943 size_t beglen, endlen, parlen, taglen;
2944 size_t i, j;
2946 NORMALIZE_THIS(cx, vp, str);
2948 if (!end)
2949 end = begin;
2951 beglen = strlen(begin);
2952 taglen = 1 + beglen + 1; /* '<begin' + '>' */
2953 parlen = 0; /* Avoid warning. */
2954 if (param) {
2955 parlen = param->length();
2956 taglen += 2 + parlen + 1; /* '="param"' */
2958 endlen = strlen(end);
2959 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2961 if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2962 js_ReportAllocationOverflow(cx);
2963 return JS_FALSE;
2966 tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2967 if (!tagbuf)
2968 return JS_FALSE;
2970 j = 0;
2971 tagbuf[j++] = '<';
2972 for (i = 0; i < beglen; i++)
2973 tagbuf[j++] = (jschar)begin[i];
2974 if (param) {
2975 tagbuf[j++] = '=';
2976 tagbuf[j++] = '"';
2977 js_strncpy(&tagbuf[j], param->chars(), parlen);
2978 j += parlen;
2979 tagbuf[j++] = '"';
2981 tagbuf[j++] = '>';
2982 js_strncpy(&tagbuf[j], str->chars(), str->length());
2983 j += str->length();
2984 tagbuf[j++] = '<';
2985 tagbuf[j++] = '/';
2986 for (i = 0; i < endlen; i++)
2987 tagbuf[j++] = (jschar)end[i];
2988 tagbuf[j++] = '>';
2989 JS_ASSERT(j == taglen);
2990 tagbuf[j] = 0;
2992 str = js_NewString(cx, tagbuf, taglen);
2993 if (!str) {
2994 js_free((char *)tagbuf);
2995 return JS_FALSE;
2997 vp->setString(str);
2998 return JS_TRUE;
3001 static JSBool
3002 tagify_value(JSContext *cx, uintN argc, Value *vp,
3003 const char *begin, const char *end)
3005 JSString *param;
3007 param = ArgToRootedString(cx, argc, vp, 0);
3008 if (!param)
3009 return JS_FALSE;
3010 return tagify(cx, begin, param, end, vp);
3013 static JSBool
3014 str_bold(JSContext *cx, uintN argc, Value *vp)
3016 return tagify(cx, "b", NULL, NULL, vp);
3019 static JSBool
3020 str_italics(JSContext *cx, uintN argc, Value *vp)
3022 return tagify(cx, "i", NULL, NULL, vp);
3025 static JSBool
3026 str_fixed(JSContext *cx, uintN argc, Value *vp)
3028 return tagify(cx, "tt", NULL, NULL, vp);
3031 static JSBool
3032 str_fontsize(JSContext *cx, uintN argc, Value *vp)
3034 return tagify_value(cx, argc, vp, "font size", "font");
3037 static JSBool
3038 str_fontcolor(JSContext *cx, uintN argc, Value *vp)
3040 return tagify_value(cx, argc, vp, "font color", "font");
3043 static JSBool
3044 str_link(JSContext *cx, uintN argc, Value *vp)
3046 return tagify_value(cx, argc, vp, "a href", "a");
3049 static JSBool
3050 str_anchor(JSContext *cx, uintN argc, Value *vp)
3052 return tagify_value(cx, argc, vp, "a name", "a");
3055 static JSBool
3056 str_strike(JSContext *cx, uintN argc, Value *vp)
3058 return tagify(cx, "strike", NULL, NULL, vp);
3061 static JSBool
3062 str_small(JSContext *cx, uintN argc, Value *vp)
3064 return tagify(cx, "small", NULL, NULL, vp);
3067 static JSBool
3068 str_big(JSContext *cx, uintN argc, Value *vp)
3070 return tagify(cx, "big", NULL, NULL, vp);
3073 static JSBool
3074 str_blink(JSContext *cx, uintN argc, Value *vp)
3076 return tagify(cx, "blink", NULL, NULL, vp);
3079 static JSBool
3080 str_sup(JSContext *cx, uintN argc, Value *vp)
3082 return tagify(cx, "sup", NULL, NULL, vp);
3085 static JSBool
3086 str_sub(JSContext *cx, uintN argc, Value *vp)
3088 return tagify(cx, "sub", NULL, NULL, vp);
3090 #endif /* JS_HAS_STR_HTML_HELPERS */
3092 #ifdef JS_TRACER
3093 JSString* FASTCALL
3094 js_String_getelem(JSContext* cx, JSString* str, int32 i)
3096 if ((size_t)i >= str->length())
3097 return NULL;
3098 return JSString::getUnitString(cx, str, size_t(i));
3100 #endif
3102 JS_DEFINE_TRCINFO_1(str_concat,
3103 (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING,
3104 1, nanojit::ACCSET_NONE)))
3106 static const uint16 GENERIC_PRIMITIVE = JSFUN_GENERIC_NATIVE | JSFUN_PRIMITIVE_THIS;
3108 static JSFunctionSpec string_methods[] = {
3109 #if JS_HAS_TOSOURCE
3110 JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE),
3111 JS_FN(js_toSource_str, str_toSource, 0,JSFUN_PRIMITIVE_THIS),
3112 #endif
3114 /* Java-like methods. */
3115 JS_FN(js_toString_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3116 JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3117 JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3118 JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE),
3119 JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE),
3120 JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE),
3121 JS_FN("charAt", js_str_charAt, 1,GENERIC_PRIMITIVE),
3122 JS_FN("charCodeAt", js_str_charCodeAt, 1,GENERIC_PRIMITIVE),
3123 JS_FN("indexOf", str_indexOf, 1,GENERIC_PRIMITIVE),
3124 JS_FN("lastIndexOf", str_lastIndexOf, 1,GENERIC_PRIMITIVE),
3125 JS_FN("trim", str_trim, 0,GENERIC_PRIMITIVE),
3126 JS_FN("trimLeft", str_trimLeft, 0,GENERIC_PRIMITIVE),
3127 JS_FN("trimRight", str_trimRight, 0,GENERIC_PRIMITIVE),
3128 JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE),
3129 JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE),
3130 JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE),
3132 /* Perl-ish methods (search is actually Python-esque). */
3133 JS_FN("match", str_match, 1,GENERIC_PRIMITIVE),
3134 JS_FN("search", str_search, 1,GENERIC_PRIMITIVE),
3135 JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE),
3136 JS_FN("split", str_split, 2,GENERIC_PRIMITIVE),
3137 #if JS_HAS_PERL_SUBSTR
3138 JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE),
3139 #endif
3141 /* Python-esque sequence methods. */
3142 JS_TN("concat", str_concat, 1,GENERIC_PRIMITIVE, &str_concat_trcinfo),
3143 JS_FN("slice", str_slice, 2,GENERIC_PRIMITIVE),
3145 /* HTML string methods. */
3146 #if JS_HAS_STR_HTML_HELPERS
3147 JS_FN("bold", str_bold, 0,JSFUN_PRIMITIVE_THIS),
3148 JS_FN("italics", str_italics, 0,JSFUN_PRIMITIVE_THIS),
3149 JS_FN("fixed", str_fixed, 0,JSFUN_PRIMITIVE_THIS),
3150 JS_FN("fontsize", str_fontsize, 1,JSFUN_PRIMITIVE_THIS),
3151 JS_FN("fontcolor", str_fontcolor, 1,JSFUN_PRIMITIVE_THIS),
3152 JS_FN("link", str_link, 1,JSFUN_PRIMITIVE_THIS),
3153 JS_FN("anchor", str_anchor, 1,JSFUN_PRIMITIVE_THIS),
3154 JS_FN("strike", str_strike, 0,JSFUN_PRIMITIVE_THIS),
3155 JS_FN("small", str_small, 0,JSFUN_PRIMITIVE_THIS),
3156 JS_FN("big", str_big, 0,JSFUN_PRIMITIVE_THIS),
3157 JS_FN("blink", str_blink, 0,JSFUN_PRIMITIVE_THIS),
3158 JS_FN("sup", str_sup, 0,JSFUN_PRIMITIVE_THIS),
3159 JS_FN("sub", str_sub, 0,JSFUN_PRIMITIVE_THIS),
3160 #endif
3162 JS_FS_END
3166 * Set up some tools to make it easier to generate large tables. After constant
3167 * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
3168 * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
3169 * To use this, define R appropriately, then use Rn(0) (for some value of n), then
3170 * undefine R.
3172 #define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0))
3173 #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
3174 #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
3175 #define R8(n) R6(n), R6((n) + (1 << 6)), R6((n) + (2 << 6)), R6((n) + (3 << 6))
3176 #define R10(n) R8(n), R8((n) + (1 << 8)), R8((n) + (2 << 8)), R8((n) + (3 << 8))
3177 #define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10))
3179 #define R3(n) R2(n), R2((n) + (1 << 2))
3180 #define R7(n) R6(n), R6((n) + (1 << 6))
3183 * Declare unit strings. Pack the string data itself into the mInlineChars
3184 * place in the header.
3186 #define R(c) { \
3187 JSString::FLAT | JSString::ATOMIZED | (1 << JSString::FLAGS_LENGTH_SHIFT),\
3188 { (jschar *)(((char *)(unitStringTable + (c))) + \
3189 offsetof(JSString, mInlineStorage)) }, \
3190 { {(c), 0x00} } }
3192 #ifdef __SUNPRO_CC
3193 #pragma pack(8)
3194 #else
3195 #pragma pack(push, 8)
3196 #endif
3198 const JSString JSString::unitStringTable[]
3199 #ifdef __GNUC__
3200 __attribute__ ((aligned (8)))
3201 #endif
3202 = { R8(0) };
3204 #ifdef __SUNPRO_CC
3205 #pragma pack(0)
3206 #else
3207 #pragma pack(pop)
3208 #endif
3210 #undef R
3213 * Declare length-2 strings. We only store strings where both characters are
3214 * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
3215 * the lowercase letters, and the next 26 are the uppercase letters.
3217 #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
3218 (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \
3219 (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \
3220 JSString::INVALID_SMALL_CHAR)
3222 #define R TO_SMALL_CHAR
3224 const JSString::SmallChar JSString::toSmallChar[] = { R7(0) };
3226 #undef R
3229 * This is used when we generate our table of short strings, so the compiler is
3230 * happier if we use |c| as few times as possible.
3232 #define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' : \
3233 (c) < 36 ? 'a' - 10 : \
3234 'A' - 36))
3235 #define R FROM_SMALL_CHAR
3237 const jschar JSString::fromSmallChar[] = { R6(0) };
3239 #undef R
3242 * For code-generation ease, length-2 strings are encoded as 12-bit int values,
3243 * where the upper 6 bits is the first character and the lower 6 bits is the
3244 * second character.
3246 #define R(c) { \
3247 JSString::FLAT | JSString::ATOMIZED | (2 << JSString::FLAGS_LENGTH_SHIFT),\
3248 { (jschar *)(((char *)(length2StringTable + (c))) + \
3249 offsetof(JSString, mInlineStorage)) }, \
3250 { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
3252 #ifdef __SUNPRO_CC
3253 #pragma pack(8)
3254 #else
3255 #pragma pack(push, 8)
3256 #endif
3258 const JSString JSString::length2StringTable[]
3259 #ifdef __GNUC__
3260 __attribute__ ((aligned (8)))
3261 #endif
3262 = { R12(0) };
3264 #ifdef __SUNPRO_CC
3265 #pragma pack(0)
3266 #else
3267 #pragma pack(pop)
3268 #endif
3270 #undef R
3272 #define R(c) FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3f), 0x00
3274 const char JSString::deflatedLength2StringTable[] = { R12(0) };
3276 #undef R
3279 * Declare int strings. Only int strings from 100 to 255 actually have to be
3280 * generated, since the rest are either unit strings or length-2 strings. To
3281 * avoid the runtime cost of figuring out where to look for the string for a
3282 * particular integer, we precompute a table of JSString*s which refer to the
3283 * correct location of the int string.
3285 #define R(c) { \
3286 JSString::FLAT | JSString::ATOMIZED | (3 << JSString::FLAGS_LENGTH_SHIFT),\
3287 { (jschar *)(((char *)(hundredStringTable + ((c) - 100))) + \
3288 offsetof(JSString, mInlineStorage)) }, \
3289 { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
3292 JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256);
3294 #ifdef __SUNPRO_CC
3295 #pragma pack(8)
3296 #else
3297 #pragma pack(push, 8)
3298 #endif
3300 const JSString JSString::hundredStringTable[]
3301 #ifdef __GNUC__
3302 __attribute__ ((aligned (8)))
3303 #endif
3304 = { R7(100), /* 100 through 227 */
3305 R4(100 + (1 << 7)), /* 228 through 243 */
3306 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3307 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3310 #undef R
3312 #define R(c) ((c) < 10 ? JSString::unitStringTable + ((c) + '0') : \
3313 (c) < 100 ? JSString::length2StringTable + \
3314 ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) + \
3315 TO_SMALL_CHAR(((c) % 10) + '0') : \
3316 JSString::hundredStringTable + ((c) - 100))
3318 const JSString *const JSString::intStringTable[] = { R8(0) };
3320 #undef R
3322 #ifdef __SUNPRO_CC
3323 #pragma pack(0)
3324 #else
3325 #pragma pack(pop)
3326 #endif
3328 #define R(c) ((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00
3330 const char JSString::deflatedIntStringTable[] = {
3331 R7(100), /* 100 through 227 */
3332 R4(100 + (1 << 7)), /* 228 through 243 */
3333 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3334 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3337 #undef R
3338 #undef R2
3339 #undef R4
3340 #undef R6
3341 #undef R8
3342 #undef R10
3343 #undef R12
3345 #undef R3
3346 #undef R7
3348 /* Static table for common UTF8 encoding */
3349 #define U8(c) char(((c) >> 6) | 0xc0), char(((c) & 0x3f) | 0x80), 0
3350 #define U(c) U8(c), U8(c+1), U8(c+2), U8(c+3), U8(c+4), U8(c+5), U8(c+6), U8(c+7)
3352 const char JSString::deflatedUnitStringTable[] = {
3353 U(0x80), U(0x88), U(0x90), U(0x98), U(0xa0), U(0xa8), U(0xb0), U(0xb8),
3354 U(0xc0), U(0xc8), U(0xd0), U(0xd8), U(0xe0), U(0xe8), U(0xf0), U(0xf8)
3357 #undef U
3358 #undef U8
3360 JSBool
3361 js_String(JSContext *cx, uintN argc, Value *vp)
3363 Value *argv = vp + 2;
3365 JSString *str;
3366 if (argc > 0) {
3367 str = js_ValueToString(cx, argv[0]);
3368 if (!str)
3369 return false;
3370 } else {
3371 str = cx->runtime->emptyString;
3374 if (IsConstructing(vp)) {
3375 JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass);
3376 if (!obj)
3377 return false;
3378 obj->setPrimitiveThis(StringValue(str));
3379 vp->setObject(*obj);
3380 } else {
3381 vp->setString(str);
3383 return true;
3386 static JSBool
3387 str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
3389 Value *argv;
3390 uintN i;
3391 jschar *chars;
3392 JSString *str;
3394 argv = vp + 2;
3395 JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
3396 if (argc == 1) {
3397 uint16_t code;
3398 if (!ValueToUint16(cx, argv[0], &code))
3399 return JS_FALSE;
3400 if (code < UNIT_STRING_LIMIT) {
3401 str = JSString::unitString(code);
3402 if (!str)
3403 return JS_FALSE;
3404 vp->setString(str);
3405 return JS_TRUE;
3407 argv[0].setInt32(code);
3409 chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar));
3410 if (!chars)
3411 return JS_FALSE;
3412 for (i = 0; i < argc; i++) {
3413 uint16_t code;
3414 if (!ValueToUint16(cx, argv[i], &code)) {
3415 cx->free(chars);
3416 return JS_FALSE;
3418 chars[i] = (jschar)code;
3420 chars[i] = 0;
3421 str = js_NewString(cx, chars, argc);
3422 if (!str) {
3423 cx->free(chars);
3424 return JS_FALSE;
3426 vp->setString(str);
3427 return JS_TRUE;
3430 #ifdef JS_TRACER
3431 static JSString* FASTCALL
3432 String_fromCharCode(JSContext* cx, int32 i)
3434 JS_ASSERT(JS_ON_TRACE(cx));
3435 jschar c = (jschar)i;
3436 if (c < UNIT_STRING_LIMIT)
3437 return JSString::unitString(c);
3438 return js_NewStringCopyN(cx, &c, 1);
3440 #endif
3442 JS_DEFINE_TRCINFO_1(str_fromCharCode,
3443 (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE)))
3445 static JSFunctionSpec string_static_methods[] = {
3446 JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo),
3447 JS_FS_END
3450 JSObject *
3451 js_InitStringClass(JSContext *cx, JSObject *obj)
3453 JSObject *proto;
3455 /* Define the escape, unescape functions in the global object. */
3456 if (!JS_DefineFunctions(cx, obj, string_functions))
3457 return NULL;
3459 proto = js_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
3460 NULL, string_methods,
3461 NULL, string_static_methods);
3462 if (!proto)
3463 return NULL;
3464 proto->setPrimitiveThis(StringValue(cx->runtime->emptyString));
3465 if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
3466 UndefinedValue(), NULL, NULL,
3467 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
3468 NULL)) {
3469 return JS_FALSE;
3472 return proto;
3475 JSString *
3476 js_NewString(JSContext *cx, jschar *chars, size_t length)
3478 JSString *str;
3480 if (length > JSString::MAX_LENGTH) {
3481 if (JS_ON_TRACE(cx)) {
3483 * If we can't leave the trace, signal OOM condition, otherwise
3484 * exit from trace before throwing.
3486 if (!CanLeaveTrace(cx))
3487 return NULL;
3489 LeaveTrace(cx);
3491 js_ReportAllocationOverflow(cx);
3492 return NULL;
3495 str = js_NewGCString(cx);
3496 if (!str)
3497 return NULL;
3498 str->initFlat(chars, length);
3499 #ifdef DEBUG
3501 JSRuntime *rt = cx->runtime;
3502 JS_RUNTIME_METER(rt, liveStrings);
3503 JS_RUNTIME_METER(rt, totalStrings);
3504 JS_LOCK_RUNTIME_VOID(rt,
3505 (rt->lengthSum += (double)length,
3506 rt->lengthSquaredSum += (double)length * (double)length));
3508 #endif
3509 return str;
3512 static JS_ALWAYS_INLINE JSString *
3513 NewShortString(JSContext *cx, const jschar *chars, size_t length)
3515 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3516 JSShortString *str = js_NewGCShortString(cx);
3517 if (!str)
3518 return NULL;
3519 jschar *storage = str->init(length);
3520 js_short_strncpy(storage, chars, length);
3521 storage[length] = 0;
3522 return str->header();
3525 static JSString *
3526 NewShortString(JSContext *cx, const char *chars, size_t length)
3528 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3529 JSShortString *str = js_NewGCShortString(cx);
3530 if (!str)
3531 return NULL;
3532 jschar *storage = str->init(length);
3534 if (js_CStringsAreUTF8) {
3535 #ifdef DEBUG
3536 size_t oldLength = length;
3537 #endif
3538 if (!js_InflateStringToBuffer(cx, chars, length, storage, &length))
3539 return NULL;
3540 JS_ASSERT(length <= oldLength);
3541 storage[length] = 0;
3542 str->resetLength(length);
3543 } else {
3544 size_t n = length;
3545 jschar *p = storage;
3546 while (n--)
3547 *p++ = jschar(*chars++);
3548 *p = 0;
3550 return str->header();
3553 static const size_t sMinWasteSize = 16;
3555 JSString *
3556 js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb)
3558 if (cb.empty())
3559 return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
3561 size_t length = cb.length();
3563 JS_STATIC_ASSERT(JSShortString::MAX_SHORT_STRING_LENGTH < JSCharBuffer::InlineLength);
3564 if (JSShortString::fitsIntoShortString(length))
3565 return NewShortString(cx, cb.begin(), length);
3567 if (!cb.append('\0'))
3568 return NULL;
3570 size_t capacity = cb.capacity();
3572 jschar *buf = cb.extractRawBuffer();
3573 if (!buf)
3574 return NULL;
3576 /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
3577 JS_ASSERT(capacity >= length);
3578 if (capacity > sMinWasteSize && capacity - length > (length >> 2)) {
3579 size_t bytes = sizeof(jschar) * (length + 1);
3580 jschar *tmp = (jschar *)cx->realloc(buf, bytes);
3581 if (!tmp) {
3582 cx->free(buf);
3583 return NULL;
3585 buf = tmp;
3588 JSString *str = js_NewString(cx, buf, length);
3589 if (!str)
3590 cx->free(buf);
3591 return str;
3594 JSString *
3595 js_NewDependentString(JSContext *cx, JSString *base, size_t start,
3596 size_t length)
3598 JSString *ds;
3600 if (length == 0)
3601 return cx->runtime->emptyString;
3603 if (start == 0 && length == base->length())
3604 return base;
3606 jschar *chars = base->chars() + start;
3608 JSString *staticStr = JSString::lookupStaticString(chars, length);
3609 if (staticStr)
3610 return staticStr;
3612 /* Try to avoid long chains of dependent strings. */
3613 while (base->isDependent())
3614 base = base->dependentBase();
3616 JS_ASSERT(base->isFlat());
3618 ds = js_NewGCString(cx);
3619 if (!ds)
3620 return NULL;
3621 ds->initDependent(base, chars, length);
3622 #ifdef DEBUG
3624 JSRuntime *rt = cx->runtime;
3625 JS_RUNTIME_METER(rt, liveDependentStrings);
3626 JS_RUNTIME_METER(rt, totalDependentStrings);
3627 JS_RUNTIME_METER(rt, liveStrings);
3628 JS_RUNTIME_METER(rt, totalStrings);
3629 JS_LOCK_RUNTIME_VOID(rt,
3630 (rt->strdepLengthSum += (double)length,
3631 rt->strdepLengthSquaredSum += (double)length * (double)length));
3632 JS_LOCK_RUNTIME_VOID(rt,
3633 (rt->lengthSum += (double)length,
3634 rt->lengthSquaredSum += (double)length * (double)length));
3636 #endif
3637 return ds;
3640 #ifdef DEBUG
3641 #include <math.h>
3643 void printJSStringStats(JSRuntime *rt)
3645 double mean, sigma;
3647 mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum,
3648 rt->lengthSquaredSum, &sigma);
3650 fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
3651 (unsigned long)rt->totalStrings, mean, sigma);
3653 mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum,
3654 rt->strdepLengthSquaredSum, &sigma);
3656 fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n",
3657 (unsigned long)rt->totalDependentStrings, mean, sigma);
3659 #endif
3661 JSString *
3662 js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
3664 if (JSShortString::fitsIntoShortString(n))
3665 return NewShortString(cx, s, n);
3667 jschar *news;
3668 JSString *str;
3670 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
3671 if (!news)
3672 return NULL;
3673 js_strncpy(news, s, n);
3674 news[n] = 0;
3675 str = js_NewString(cx, news, n);
3676 if (!str)
3677 cx->free(news);
3678 return str;
3681 JSString *
3682 js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
3684 if (JSShortString::fitsIntoShortString(n))
3685 return NewShortString(cx, s, n);
3686 return JS_NewStringCopyN(cx, s, n);
3689 JSString *
3690 js_NewStringCopyZ(JSContext *cx, const jschar *s)
3692 size_t n, m;
3693 jschar *news;
3694 JSString *str;
3696 n = js_strlen(s);
3698 if (JSShortString::fitsIntoShortString(n))
3699 return NewShortString(cx, s, n);
3701 m = (n + 1) * sizeof(jschar);
3702 news = (jschar *) cx->malloc(m);
3703 if (!news)
3704 return NULL;
3705 memcpy(news, s, m);
3706 str = js_NewString(cx, news, n);
3707 if (!str)
3708 cx->free(news);
3709 return str;
3712 JSString *
3713 js_NewStringCopyZ(JSContext *cx, const char *s)
3715 return js_NewStringCopyN(cx, s, strlen(s));
3718 const char *
3719 js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
3721 JSString *str;
3723 str = (asSource ? js_ValueToSource : js_ValueToString)(cx, v);
3724 if (!str)
3725 return NULL;
3726 str = js_QuoteString(cx, str, 0);
3727 if (!str)
3728 return NULL;
3729 return bytes->encode(cx, str);
3732 JSString *
3733 js_ValueToString(JSContext *cx, const Value &arg)
3735 Value v = arg;
3736 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3737 return NULL;
3739 JSString *str;
3740 if (v.isString()) {
3741 str = v.toString();
3742 } else if (v.isInt32()) {
3743 str = js_IntToString(cx, v.toInt32());
3744 } else if (v.isDouble()) {
3745 str = js_NumberToString(cx, v.toDouble());
3746 } else if (v.isBoolean()) {
3747 str = js_BooleanToString(cx, v.toBoolean());
3748 } else if (v.isNull()) {
3749 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3750 } else {
3751 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3753 return str;
3756 static inline JSBool
3757 AppendAtom(JSAtom *atom, JSCharBuffer &cb)
3759 JSString *str = ATOM_TO_STRING(atom);
3760 const jschar *chars;
3761 size_t length;
3762 str->getCharsAndLength(chars, length);
3763 return cb.append(chars, length);
3766 /* This function implements E-262-3 section 9.8, toString. */
3767 JSBool
3768 js_ValueToCharBuffer(JSContext *cx, const Value &arg, JSCharBuffer &cb)
3770 Value v = arg;
3771 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3772 return JS_FALSE;
3774 if (v.isString()) {
3775 const jschar *chars;
3776 size_t length;
3777 v.toString()->getCharsAndLength(chars, length);
3778 return cb.append(chars, length);
3780 if (v.isNumber())
3781 return js_NumberValueToCharBuffer(cx, v, cb);
3782 if (v.isBoolean())
3783 return js_BooleanToCharBuffer(cx, v.toBoolean(), cb);
3784 if (v.isNull())
3785 return AppendAtom(cx->runtime->atomState.nullAtom, cb);
3786 JS_ASSERT(v.isUndefined());
3787 return AppendAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb);
3790 JS_FRIEND_API(JSString *)
3791 js_ValueToSource(JSContext *cx, const Value &v)
3793 if (v.isUndefined())
3794 return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
3795 if (v.isString())
3796 return js_QuoteString(cx, v.toString(), '"');
3797 if (v.isPrimitive()) {
3798 /* Special case to preserve negative zero, _contra_ toString. */
3799 if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
3800 /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
3801 static const jschar js_negzero_ucNstr[] = {'-', '0'};
3803 return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
3805 return js_ValueToString(cx, v);
3808 JSAtom *atom = cx->runtime->atomState.toSourceAtom;
3809 AutoValueRooter tvr(cx);
3810 if (!js_TryMethod(cx, &v.toObject(), atom, 0, NULL, tvr.addr()))
3811 return NULL;
3812 return js_ValueToString(cx, tvr.value());
3816 * str is not necessarily a GC thing here.
3818 uint32
3819 js_HashString(JSString *str)
3821 const jschar *s;
3822 size_t n;
3823 uint32 h;
3825 str->getCharsAndLength(s, n);
3826 for (h = 0; n; s++, n--)
3827 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
3828 return h;
3832 * str is not necessarily a GC thing here.
3834 JSBool JS_FASTCALL
3835 js_EqualStrings(JSString *str1, JSString *str2)
3837 size_t n;
3838 const jschar *s1, *s2;
3840 JS_ASSERT(str1);
3841 JS_ASSERT(str2);
3843 /* Fast case: pointer equality could be a quick win. */
3844 if (str1 == str2)
3845 return JS_TRUE;
3847 n = str1->length();
3848 if (n != str2->length())
3849 return JS_FALSE;
3851 if (n == 0)
3852 return JS_TRUE;
3854 s1 = str1->chars(), s2 = str2->chars();
3855 do {
3856 if (*s1 != *s2)
3857 return JS_FALSE;
3858 ++s1, ++s2;
3859 } while (--n != 0);
3861 return JS_TRUE;
3863 JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, nanojit::ACCSET_NONE)
3865 int32 JS_FASTCALL
3866 js_CompareStrings(JSString *str1, JSString *str2)
3868 size_t l1, l2, n, i;
3869 const jschar *s1, *s2;
3870 intN cmp;
3872 JS_ASSERT(str1);
3873 JS_ASSERT(str2);
3875 /* Fast case: pointer equality could be a quick win. */
3876 if (str1 == str2)
3877 return 0;
3879 str1->getCharsAndLength(s1, l1);
3880 str2->getCharsAndLength(s2, l2);
3881 n = JS_MIN(l1, l2);
3882 for (i = 0; i < n; i++) {
3883 cmp = s1[i] - s2[i];
3884 if (cmp != 0)
3885 return cmp;
3887 return (intN)(l1 - l2);
3889 JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, nanojit::ACCSET_NONE)
3891 namespace js {
3893 JSBool
3894 MatchStringAndAscii(JSString *str, const char *asciiBytes)
3896 size_t length = strlen(asciiBytes);
3897 #ifdef DEBUG
3898 for (size_t i = 0; i != length; ++i)
3899 JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
3900 #endif
3901 if (length != str->length())
3902 return false;
3903 const jschar *chars = str->chars();
3904 for (size_t i = 0; i != length; ++i) {
3905 if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
3906 return false;
3908 return true;
3911 } /* namespacejs */
3915 size_t
3916 js_strlen(const jschar *s)
3918 const jschar *t;
3920 for (t = s; *t != 0; t++)
3921 continue;
3922 return (size_t)(t - s);
3925 jschar *
3926 js_strchr(const jschar *s, jschar c)
3928 while (*s != 0) {
3929 if (*s == c)
3930 return (jschar *)s;
3931 s++;
3933 return NULL;
3936 jschar *
3937 js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
3939 while (s < limit) {
3940 if (*s == c)
3941 return (jschar *)s;
3942 s++;
3944 return NULL;
3947 jschar *
3948 js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)
3950 size_t nbytes, nchars, i;
3951 jschar *chars;
3952 #ifdef DEBUG
3953 JSBool ok;
3954 #endif
3956 nbytes = *lengthp;
3957 if (js_CStringsAreUTF8) {
3958 if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))
3959 goto bad;
3960 chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar));
3961 if (!chars)
3962 goto bad;
3963 #ifdef DEBUG
3964 ok =
3965 #endif
3966 js_InflateStringToBuffer(cx, bytes, nbytes, chars, &nchars);
3967 JS_ASSERT(ok);
3968 } else {
3969 nchars = nbytes;
3970 chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
3971 if (!chars)
3972 goto bad;
3973 for (i = 0; i < nchars; i++)
3974 chars[i] = (unsigned char) bytes[i];
3976 *lengthp = nchars;
3977 chars[nchars] = 0;
3978 return chars;
3980 bad:
3982 * For compatibility with callers of JS_DecodeBytes we must zero lengthp
3983 * on errors.
3985 *lengthp = 0;
3986 return NULL;
3990 * May be called with null cx by js_GetStringBytes, see below.
3992 char *
3993 js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
3995 size_t nbytes, i;
3996 char *bytes;
3997 #ifdef DEBUG
3998 JSBool ok;
3999 #endif
4001 if (js_CStringsAreUTF8) {
4002 nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
4003 if (nbytes == (size_t) -1)
4004 return NULL;
4005 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
4006 if (!bytes)
4007 return NULL;
4008 #ifdef DEBUG
4009 ok =
4010 #endif
4011 js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes);
4012 JS_ASSERT(ok);
4013 } else {
4014 nbytes = nchars;
4015 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
4016 if (!bytes)
4017 return NULL;
4018 for (i = 0; i < nbytes; i++)
4019 bytes[i] = (char) chars[i];
4021 bytes[nbytes] = 0;
4022 return bytes;
4025 size_t
4026 js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
4028 if (!js_CStringsAreUTF8)
4029 return nchars;
4031 return js_GetDeflatedUTF8StringLength(cx, chars, nchars);
4035 * May be called with null cx through js_GetStringBytes, see below.
4037 size_t
4038 js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, size_t nchars)
4040 size_t nbytes;
4041 const jschar *end;
4042 uintN c, c2;
4043 char buffer[10];
4045 nbytes = nchars;
4046 for (end = chars + nchars; chars != end; chars++) {
4047 c = *chars;
4048 if (c < 0x80)
4049 continue;
4050 if (0xD800 <= c && c <= 0xDFFF) {
4051 /* Surrogate pair. */
4052 chars++;
4054 /* nbytes sets 1 length since this is surrogate pair. */
4055 nbytes--;
4056 if (c >= 0xDC00 || chars == end)
4057 goto bad_surrogate;
4058 c2 = *chars;
4059 if (c2 < 0xDC00 || c2 > 0xDFFF)
4060 goto bad_surrogate;
4061 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4063 c >>= 11;
4064 nbytes++;
4065 while (c) {
4066 c >>= 5;
4067 nbytes++;
4070 return nbytes;
4072 bad_surrogate:
4073 if (cx) {
4074 JS_snprintf(buffer, 10, "0x%x", c);
4075 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
4076 NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
4078 return (size_t) -1;
4081 JSBool
4082 js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
4083 char *dst, size_t *dstlenp)
4085 size_t dstlen, i;
4087 dstlen = *dstlenp;
4088 if (!js_CStringsAreUTF8) {
4089 if (srclen > dstlen) {
4090 for (i = 0; i < dstlen; i++)
4091 dst[i] = (char) src[i];
4092 if (cx) {
4093 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4094 JSMSG_BUFFER_TOO_SMALL);
4096 return JS_FALSE;
4098 for (i = 0; i < srclen; i++)
4099 dst[i] = (char) src[i];
4100 *dstlenp = srclen;
4101 return JS_TRUE;
4104 return js_DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
4107 JSBool
4108 js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
4109 char *dst, size_t *dstlenp)
4111 size_t dstlen, i, origDstlen, utf8Len;
4112 jschar c, c2;
4113 uint32 v;
4114 uint8 utf8buf[6];
4116 dstlen = *dstlenp;
4117 origDstlen = dstlen;
4118 while (srclen) {
4119 c = *src++;
4120 srclen--;
4121 if ((c >= 0xDC00) && (c <= 0xDFFF))
4122 goto badSurrogate;
4123 if (c < 0xD800 || c > 0xDBFF) {
4124 v = c;
4125 } else {
4126 if (srclen < 1)
4127 goto badSurrogate;
4128 c2 = *src;
4129 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
4130 goto badSurrogate;
4131 src++;
4132 srclen--;
4133 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4135 if (v < 0x0080) {
4136 /* no encoding necessary - performance hack */
4137 if (dstlen == 0)
4138 goto bufferTooSmall;
4139 *dst++ = (char) v;
4140 utf8Len = 1;
4141 } else {
4142 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
4143 if (utf8Len > dstlen)
4144 goto bufferTooSmall;
4145 for (i = 0; i < utf8Len; i++)
4146 *dst++ = (char) utf8buf[i];
4148 dstlen -= utf8Len;
4150 *dstlenp = (origDstlen - dstlen);
4151 return JS_TRUE;
4153 badSurrogate:
4154 *dstlenp = (origDstlen - dstlen);
4155 /* Delegate error reporting to the measurement function. */
4156 if (cx)
4157 js_GetDeflatedStringLength(cx, src - 1, srclen + 1);
4158 return JS_FALSE;
4160 bufferTooSmall:
4161 *dstlenp = (origDstlen - dstlen);
4162 if (cx) {
4163 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4164 JSMSG_BUFFER_TOO_SMALL);
4166 return JS_FALSE;
4169 JSBool
4170 js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
4171 jschar *dst, size_t *dstlenp)
4173 size_t dstlen, i;
4175 if (!js_CStringsAreUTF8) {
4176 if (dst) {
4177 dstlen = *dstlenp;
4178 if (srclen > dstlen) {
4179 for (i = 0; i < dstlen; i++)
4180 dst[i] = (unsigned char) src[i];
4181 if (cx) {
4182 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4183 JSMSG_BUFFER_TOO_SMALL);
4185 return JS_FALSE;
4187 for (i = 0; i < srclen; i++)
4188 dst[i] = (unsigned char) src[i];
4190 *dstlenp = srclen;
4191 return JS_TRUE;
4194 return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
4197 JSBool
4198 js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
4199 jschar *dst, size_t *dstlenp)
4201 size_t dstlen, origDstlen, offset, j, n;
4202 uint32 v;
4204 dstlen = dst ? *dstlenp : (size_t) -1;
4205 origDstlen = dstlen;
4206 offset = 0;
4208 while (srclen) {
4209 v = (uint8) *src;
4210 n = 1;
4211 if (v & 0x80) {
4212 while (v & (0x80 >> n))
4213 n++;
4214 if (n > srclen)
4215 goto bufferTooSmall;
4216 if (n == 1 || n > 4)
4217 goto badCharacter;
4218 for (j = 1; j < n; j++) {
4219 if ((src[j] & 0xC0) != 0x80)
4220 goto badCharacter;
4222 v = Utf8ToOneUcs4Char((uint8 *)src, n);
4223 if (v >= 0x10000) {
4224 v -= 0x10000;
4225 if (v > 0xFFFFF || dstlen < 2) {
4226 *dstlenp = (origDstlen - dstlen);
4227 if (cx) {
4228 char buffer[10];
4229 JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
4230 JS_ReportErrorFlagsAndNumber(cx,
4231 JSREPORT_ERROR,
4232 js_GetErrorMessage, NULL,
4233 JSMSG_UTF8_CHAR_TOO_LARGE,
4234 buffer);
4236 return JS_FALSE;
4238 if (dst) {
4239 *dst++ = (jschar)((v >> 10) + 0xD800);
4240 v = (jschar)((v & 0x3FF) + 0xDC00);
4242 dstlen--;
4245 if (!dstlen)
4246 goto bufferTooSmall;
4247 if (dst)
4248 *dst++ = (jschar) v;
4249 dstlen--;
4250 offset += n;
4251 src += n;
4252 srclen -= n;
4254 *dstlenp = (origDstlen - dstlen);
4255 return JS_TRUE;
4257 badCharacter:
4258 *dstlenp = (origDstlen - dstlen);
4259 if (cx) {
4260 char buffer[10];
4261 JS_snprintf(buffer, 10, "%d", offset);
4262 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
4263 js_GetErrorMessage, NULL,
4264 JSMSG_MALFORMED_UTF8_CHAR,
4265 buffer);
4267 return JS_FALSE;
4269 bufferTooSmall:
4270 *dstlenp = (origDstlen - dstlen);
4271 if (cx) {
4272 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4273 JSMSG_BUFFER_TOO_SMALL);
4275 return JS_FALSE;
4278 namespace js {
4280 DeflatedStringCache::DeflatedStringCache()
4282 #ifdef JS_THREADSAFE
4283 lock = NULL;
4284 #endif
4287 bool
4288 DeflatedStringCache::init()
4290 #ifdef JS_THREADSAFE
4291 JS_ASSERT(!lock);
4292 lock = JS_NEW_LOCK();
4293 if (!lock)
4294 return false;
4295 #endif
4298 * Make room for 2K deflated strings that a typical browser session
4299 * creates.
4301 return map.init(2048);
4304 DeflatedStringCache::~DeflatedStringCache()
4306 #ifdef JS_THREADSAFE
4307 if (lock)
4308 JS_DESTROY_LOCK(lock);
4309 #endif
4312 void
4313 DeflatedStringCache::sweep(JSContext *cx)
4316 * We must take a lock even during the GC as JS_GetFunctionName can be
4317 * called outside the request.
4319 JS_ACQUIRE_LOCK(lock);
4321 for (Map::Enum e(map); !e.empty(); e.popFront()) {
4322 JSString *str = e.front().key;
4323 if (IsAboutToBeFinalized(str)) {
4324 char *bytes = e.front().value;
4325 e.removeFront();
4328 * We cannot use cx->free here as bytes may come from the
4329 * embedding that calls JS_NewString(cx, bytes, length). Those
4330 * bytes may not be allocated via js_malloc and may not have
4331 * space for the background free list.
4333 js_free(bytes);
4337 JS_RELEASE_LOCK(lock);
4340 char *
4341 DeflatedStringCache::getBytes(JSString *str)
4343 JS_ACQUIRE_LOCK(lock);
4344 Map::AddPtr p = map.lookupForAdd(str);
4345 char *bytes = p ? p->value : NULL;
4346 JS_RELEASE_LOCK(lock);
4348 if (bytes)
4349 return bytes;
4351 bytes = js_DeflateString(NULL, str->chars(), str->length());
4352 if (!bytes)
4353 return NULL;
4356 * In the single-threaded case we use the add method as js_DeflateString
4357 * cannot mutate the map. In particular, it cannot run the GC that may
4358 * delete entries from the map. But the JS_THREADSAFE version requires to
4359 * deal with other threads adding the entries to the map.
4361 char *bytesToFree = NULL;
4362 JSBool ok;
4363 #ifdef JS_THREADSAFE
4364 JS_ACQUIRE_LOCK(lock);
4365 ok = map.relookupOrAdd(p, str, bytes);
4366 if (ok && p->value != bytes) {
4367 /* Some other thread has asked for str bytes .*/
4368 JS_ASSERT(!strcmp(p->value, bytes));
4369 bytesToFree = bytes;
4370 bytes = p->value;
4372 JS_RELEASE_LOCK(lock);
4373 #else /* !JS_THREADSAFE */
4374 ok = map.add(p, str, bytes);
4375 #endif
4376 if (!ok) {
4377 bytesToFree = bytes;
4378 bytes = NULL;
4381 if (bytesToFree)
4382 js_free(bytesToFree);
4383 return bytes;
4386 } /* namespace js */
4388 const char *
4389 js_GetStringBytes(JSAtom *atom)
4391 JSString *str = ATOM_TO_STRING(atom);
4392 if (JSString::isUnitString(str)) {
4393 char *bytes;
4394 #ifdef IS_LITTLE_ENDIAN
4395 /* Unit string data is {c, 0, 0, 0} so we can just cast. */
4396 bytes = (char *)str->chars();
4397 #else
4398 /* Unit string data is {0, c, 0, 0} so we can point into the middle. */
4399 bytes = (char *)str->chars() + 1;
4400 #endif
4401 return ((*bytes & 0x80) && js_CStringsAreUTF8)
4402 ? JSString::deflatedUnitStringTable + ((*bytes & 0x7f) * 3)
4403 : bytes;
4407 * We must burn some space on deflated int strings and length-2 strings
4408 * to preserve static allocation (which is to say, JSRuntime independence).
4410 if (JSString::isLength2String(str))
4411 return JSString::deflatedLength2StringTable + ((str - JSString::length2StringTable) * 3);
4413 if (JSString::isHundredString(str)) {
4415 * We handled the 1 and 2-digit number cases already, so we know that
4416 * str is between 100 and 255.
4418 return JSString::deflatedIntStringTable + ((str - JSString::hundredStringTable) * 4);
4421 return GetGCThingRuntime(str)->deflatedStringCache->getBytes(str);
4425 * From java.lang.Character.java:
4427 * The character properties are currently encoded into 32 bits in the
4428 * following manner:
4430 * 10 bits signed offset used for converting case
4431 * 1 bit if 1, adding the signed offset converts the character to
4432 * lowercase
4433 * 1 bit if 1, subtracting the signed offset converts the character to
4434 * uppercase
4435 * 1 bit if 1, character has a titlecase equivalent (possibly itself)
4436 * 3 bits 0 may not be part of an identifier
4437 * 1 ignorable control; may continue a Unicode identifier or JS
4438 * identifier
4439 * 2 may continue a JS identifier but not a Unicode identifier
4440 * (unused)
4441 * 3 may continue a Unicode identifier or JS identifier
4442 * 4 is a JS whitespace character
4443 * 5 may start or continue a JS identifier;
4444 * may continue but not start a Unicode identifier (_)
4445 * 6 may start or continue a JS identifier but not a Unicode
4446 * identifier ($)
4447 * 7 may start or continue a Unicode identifier or JS identifier
4448 * Thus:
4449 * 5, 6, 7 may start a JS identifier
4450 * 1, 2, 3, 5, 6, 7 may continue a JS identifier
4451 * 7 may start a Unicode identifier
4452 * 1, 3, 5, 7 may continue a Unicode identifier
4453 * 1 is ignorable within an identifier
4454 * 4 is JS whitespace
4455 * 2 bits 0 this character has no numeric property
4456 * 1 adding the digit offset to the character code and then
4457 * masking with 0x1F will produce the desired numeric value
4458 * 2 this character has a "strange" numeric value
4459 * 3 a JS supradecimal digit: adding the digit offset to the
4460 * character code, then masking with 0x1F, then adding 10
4461 * will produce the desired numeric value
4462 * 5 bits digit offset
4463 * 1 bit XML 1.0 name start character
4464 * 1 bit XML 1.0 name character
4465 * 2 bits reserved for future use
4466 * 5 bits character type
4469 /* The X table has 1024 entries for a total of 1024 bytes. */
4471 const uint8 js_X[] = {
4472 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */
4473 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */
4474 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */
4475 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */
4476 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */
4477 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */
4478 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */
4479 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */
4480 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */
4481 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */
4482 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */
4483 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */
4484 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */
4485 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */
4486 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */
4487 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */
4488 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */
4489 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */
4490 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */
4491 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */
4492 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */
4493 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */
4494 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */
4495 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */
4496 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */
4497 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */
4498 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */
4499 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */
4500 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */
4501 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */
4502 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */
4503 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */
4504 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */
4505 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */
4506 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */
4507 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */
4508 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */
4509 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */
4510 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */
4511 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */
4512 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */
4513 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */
4514 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */
4515 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */
4516 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */
4517 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */
4518 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */
4519 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */
4520 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */
4521 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */
4522 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */
4523 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */
4524 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */
4525 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */
4526 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */
4527 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */
4528 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */
4529 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */
4530 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */
4531 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */
4532 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */
4533 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */
4534 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */
4535 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */
4536 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */
4537 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */
4538 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */
4539 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */
4540 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */
4541 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */
4542 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */
4543 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */
4544 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */
4545 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */
4546 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */
4547 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */
4548 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */
4549 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */
4550 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */
4551 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */
4552 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */
4553 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */
4554 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */
4555 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */
4556 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */
4557 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */
4558 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */
4559 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */
4560 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */
4561 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */
4562 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */
4563 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */
4564 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */
4565 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */
4566 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */
4567 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */
4568 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */
4569 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */
4570 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */
4571 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */
4572 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */
4573 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */
4574 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */
4575 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */
4576 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */
4577 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */
4578 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */
4579 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */
4580 104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */
4581 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */
4582 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */
4583 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */
4584 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */
4585 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */
4586 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */
4587 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */
4588 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */
4589 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */
4590 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */
4591 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */
4592 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */
4593 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */
4594 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */
4595 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */
4596 105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */
4597 106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */
4598 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */
4599 115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */
4602 /* The Y table has 7808 entries for a total of 7808 bytes. */
4604 const uint8 js_Y[] = {
4605 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4606 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */
4607 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4608 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4609 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */
4610 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */
4611 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */
4612 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */
4613 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4614 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4615 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4616 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */
4617 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4618 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4619 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4620 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */
4621 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4622 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4623 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4624 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4625 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */
4626 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */
4627 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */
4628 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */
4629 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4630 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4631 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */
4632 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */
4633 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4634 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4635 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */
4636 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */
4637 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4638 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4639 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4640 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4641 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4642 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4643 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */
4644 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */
4645 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */
4646 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */
4647 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4648 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4649 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4650 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4651 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4652 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */
4653 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */
4654 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */
4655 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */
4656 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */
4657 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */
4658 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */
4659 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */
4660 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */
4661 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */
4662 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */
4663 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */
4664 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */
4665 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4666 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4667 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */
4668 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */
4669 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4670 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4671 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4672 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4673 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4674 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4675 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4676 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4677 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4678 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4679 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */
4680 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */
4681 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */
4682 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */
4683 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */
4684 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */
4685 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */
4686 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */
4687 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */
4688 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4689 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4690 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */
4691 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */
4692 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */
4693 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4694 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */
4695 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4696 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */
4697 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */
4698 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */
4699 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4700 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4701 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4702 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4703 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4704 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4705 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4706 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4707 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4708 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4709 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */
4710 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4711 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4712 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4713 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */
4714 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4715 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */
4716 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */
4717 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */
4718 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */
4719 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4720 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4721 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */
4722 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */
4723 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4724 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4725 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */
4726 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */
4727 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */
4728 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */
4729 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */
4730 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */
4731 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */
4732 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */
4733 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */
4734 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */
4735 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4736 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4737 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4738 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4739 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4740 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4741 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4742 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4743 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */
4744 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */
4745 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4746 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4747 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4748 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4749 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */
4750 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */
4751 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4752 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4753 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4754 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4755 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4756 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4757 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */
4758 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */
4759 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4760 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4761 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4762 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */
4763 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */
4764 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */
4765 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4766 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4767 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4768 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4769 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4770 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4771 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4772 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4773 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4774 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4775 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */
4776 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */
4777 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4778 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4779 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4780 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4781 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */
4782 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */
4783 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4784 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4785 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */
4786 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4787 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4788 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */
4789 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */
4790 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4791 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4792 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4793 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4794 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */
4795 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */
4796 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4797 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4798 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */
4799 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4800 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */
4801 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4802 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4803 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4804 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */
4805 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4806 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */
4807 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */
4808 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */
4809 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */
4810 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */
4811 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4812 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4813 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4814 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4815 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4816 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4817 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4818 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4819 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4820 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */
4821 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */
4822 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */
4823 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */
4824 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */
4825 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */
4826 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */
4827 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */
4828 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */
4829 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4830 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4831 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4832 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4833 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4834 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4835 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4836 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4837 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */
4838 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4839 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4840 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4841 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4842 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4843 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4844 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */
4845 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */
4846 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */
4847 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */
4848 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */
4849 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */
4850 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */
4851 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4852 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4853 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */
4854 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */
4855 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */
4856 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4857 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4858 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */
4859 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */
4860 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */
4861 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */
4862 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */
4863 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */
4864 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */
4865 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */
4866 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */
4867 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */
4868 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */
4869 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */
4870 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */
4871 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */
4872 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4873 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4874 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */
4875 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */
4876 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */
4877 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */
4878 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */
4879 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4880 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */
4881 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */
4882 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */
4883 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */
4884 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4885 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */
4886 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */
4887 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */
4888 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4889 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4890 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */
4891 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */
4892 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */
4893 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */
4894 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */
4895 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4896 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4897 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */
4898 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */
4899 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4900 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4901 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */
4902 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */
4903 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */
4904 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4905 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4906 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */
4907 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */
4908 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */
4909 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */
4910 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */
4911 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */
4912 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */
4913 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */
4914 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */
4915 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4916 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4917 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */
4918 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4919 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */
4920 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */
4921 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */
4922 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4923 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */
4924 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */
4925 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */
4926 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */
4927 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */
4928 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4929 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */
4930 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */
4931 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */
4932 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4933 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */
4934 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */
4935 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4936 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4937 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4938 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4939 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */
4940 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */
4941 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */
4942 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */
4943 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */
4944 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4945 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */
4946 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */
4947 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4948 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4949 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */
4950 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */
4951 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4952 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4953 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4954 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4955 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */
4956 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */
4957 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */
4958 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */
4959 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */
4960 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */
4961 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */
4962 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */
4963 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4964 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4965 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */
4966 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */
4967 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4968 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4969 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4970 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4971 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4972 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */
4973 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */
4974 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */
4975 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */
4976 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4977 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */
4978 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */
4979 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4980 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4981 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4982 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4983 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4984 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4985 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4986 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */
4987 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */
4988 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */
4989 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */
4990 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */
4991 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */
4992 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */
4993 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4994 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4995 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4996 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4997 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */
4998 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */
4999 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */
5000 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */
5001 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */
5002 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */
5003 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */
5004 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */
5005 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */
5006 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */
5007 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */
5008 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */
5009 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5010 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5011 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5012 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5013 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */
5014 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */
5015 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */
5016 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */
5017 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */
5018 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */
5019 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */
5020 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */
5021 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5022 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5023 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5024 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5025 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5026 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */
5027 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */
5028 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */
5029 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */
5030 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */
5031 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */
5032 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5033 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5034 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */
5035 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5036 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */
5037 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5038 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5039 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5040 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5041 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5042 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5043 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5044 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5045 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */
5046 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */
5047 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5048 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5049 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5050 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5051 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */
5052 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */
5053 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5054 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5055 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5056 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5057 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5058 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5059 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5060 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5061 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5062 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5063 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5064 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */
5065 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5066 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5067 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5068 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5069 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5070 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5071 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5072 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5073 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */
5074 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5075 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5076 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5077 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5078 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5079 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5080 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5081 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5082 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5083 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5084 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */
5085 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5086 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5087 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5088 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5089 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5090 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5091 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5092 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5093 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5094 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5095 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */
5096 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */
5097 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5098 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5099 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5100 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5101 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5102 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5103 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5104 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5105 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5106 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5107 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5108 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */
5109 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5110 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5111 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */
5112 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */
5113 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5114 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5115 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5116 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5117 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */
5118 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */
5119 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */
5120 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */
5121 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */
5122 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */
5123 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */
5124 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */
5125 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5126 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5127 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5128 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5129 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5130 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5131 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */
5132 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */
5133 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */
5134 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */
5135 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */
5136 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */
5137 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */
5138 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */
5139 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */
5140 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */
5141 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */
5142 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */
5143 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */
5144 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */
5145 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
5146 105, 106, 104, 104, 104, 104, 104, 46, /* 67 */
5147 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
5148 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */
5149 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */
5150 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5151 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5152 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5153 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5154 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */
5155 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */
5156 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */
5157 107, 107, 107, 107, 107, 107, 107, 107, /* 69 */
5158 107, 107, 7, 7, 7, 5, 6, 46, /* 69 */
5159 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5160 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5161 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */
5162 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */
5163 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5164 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5165 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5166 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5167 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */
5168 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */
5169 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */
5170 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5171 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5172 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5173 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */
5174 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */
5175 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */
5176 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */
5177 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */
5178 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */
5179 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */
5180 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */
5181 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5182 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5183 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */
5184 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */
5185 109, 109, 109, 109, 109, 109, 109, 109, /* 72 */
5186 109, 109, 109, 109, 110, 110, 110, 110, /* 72 */
5187 111, 111, 111, 111, 111, 111, 111, 111, /* 72 */
5188 111, 111, 111, 111, 112, 112, 112, 112, /* 72 */
5189 113, 113, 113, 46, 46, 46, 46, 46, /* 73 */
5190 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */
5191 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */
5192 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5193 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5194 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5195 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5196 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5197 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5198 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5199 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */
5200 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5201 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5202 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */
5203 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5204 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5205 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5206 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5207 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5208 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5209 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5210 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5211 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5212 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5213 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5214 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5215 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5216 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5217 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5218 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5219 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */
5220 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */
5221 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */
5222 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */
5223 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5224 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5225 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */
5226 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */
5227 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5228 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5229 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5230 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5231 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5232 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5233 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5234 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5235 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5236 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */
5237 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5238 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5239 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5240 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5241 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */
5242 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5243 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5244 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5245 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */
5246 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */
5247 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5248 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5249 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5250 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5251 114, 114, 114, 114, 82, 82, 82, 82, /* 80 */
5252 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */
5253 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */
5254 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5255 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5256 115, 115, 115, 115, 15, 15, 15, 15, /* 81 */
5257 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5258 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5259 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */
5260 116, 116, 116, 116, 116, 116, 116, 116, /* 81 */
5261 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5262 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5263 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5264 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5265 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5266 117, 117, 118, 46, 46, 46, 46, 46, /* 82 */
5267 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5268 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5269 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5270 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5271 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5272 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5273 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5274 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5275 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5276 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5277 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5278 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5279 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */
5280 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */
5281 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5282 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5283 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5284 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5285 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5286 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5287 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5288 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5289 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5290 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5291 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5292 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5293 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5294 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5295 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */
5296 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */
5297 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5298 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5299 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5300 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5301 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */
5302 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */
5303 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5304 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5305 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5306 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5307 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5308 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5309 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5310 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */
5311 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */
5312 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */
5313 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5314 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */
5315 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */
5316 119, 119, 119, 119, 119, 119, 119, 119, /* 88 */
5317 114, 114, 114, 114, 114, 114, 114, 114, /* 89 */
5318 114, 114, 83, 83, 83, 83, 83, 83, /* 89 */
5319 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */
5320 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5321 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5322 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5323 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5324 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */
5325 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */
5326 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */
5327 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */
5328 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */
5329 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */
5330 121, 121, 60, 60, 60, 60, 60, 60, /* 90 */
5331 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */
5332 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */
5333 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5334 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5335 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5336 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5337 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5338 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5339 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5340 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5341 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5342 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5343 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */
5344 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */
5345 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5346 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5347 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5348 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5349 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5350 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5351 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5352 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5353 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5354 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5355 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5356 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */
5357 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */
5358 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5359 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5360 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5361 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5362 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */
5363 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5364 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5365 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */
5366 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */
5367 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */
5368 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */
5369 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5370 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5371 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5372 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5373 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5374 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5375 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5376 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */
5377 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */
5378 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */
5379 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5380 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5381 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */
5382 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5383 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5384 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5385 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5386 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5387 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5388 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */
5389 114, 114, 114, 114, 114, 114, 114, 114, /* 98 */
5390 114, 114, 15, 15, 15, 15, 15, 15, /* 98 */
5391 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5392 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5393 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5394 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5395 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5396 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5397 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5398 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */
5399 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5400 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5401 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5402 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5403 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5404 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */
5405 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5406 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5407 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5408 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5409 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5410 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5411 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */
5412 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */
5413 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5414 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5415 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5416 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */
5417 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5418 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5419 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5420 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */
5421 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5422 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5423 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5424 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5425 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */
5426 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5427 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5428 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5429 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5430 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5431 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5432 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5433 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */
5434 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5435 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5436 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5437 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5438 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5439 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5440 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5441 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5442 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5443 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5444 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5445 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5446 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5447 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5448 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5449 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5450 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5451 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5452 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5453 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5454 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5455 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5456 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5457 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5458 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */
5459 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5460 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5461 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */
5462 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */
5463 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */
5464 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */
5465 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */
5466 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */
5467 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */
5468 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */
5469 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */
5470 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5471 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5472 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5473 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5474 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5475 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5476 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5477 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5478 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5479 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5480 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5481 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5482 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5483 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */
5484 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */
5485 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5486 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5487 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */
5488 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5489 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5490 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5491 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5492 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5493 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5494 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5495 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5496 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5497 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5498 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5499 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5500 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */
5501 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5502 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5503 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5504 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5505 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5506 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5507 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5508 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5509 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5510 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5511 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */
5512 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5513 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5514 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5515 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5516 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5517 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5518 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5519 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5520 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5521 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5522 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5523 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5524 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */
5525 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5526 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5527 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5528 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5529 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */
5530 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5531 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */
5532 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */
5533 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */
5534 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */
5535 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */
5536 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */
5537 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */
5538 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */
5539 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */
5540 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */
5541 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5542 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5543 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5544 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5545 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5546 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5547 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5548 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */
5549 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */
5550 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */
5551 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */
5552 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */
5553 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5554 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5555 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5556 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */
5557 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5558 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5559 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5560 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */
5561 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */
5562 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5563 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5564 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5565 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5566 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5567 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5568 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */
5569 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5570 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5571 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5572 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */
5573 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5574 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5575 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5576 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */
5577 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */
5578 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */
5579 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */
5580 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */
5583 /* The A table has 124 entries for a total of 496 bytes. */
5585 const uint32 js_A[] = {
5586 0x0001000F, /* 0 Cc, ignorable */
5587 0x0004000F, /* 1 Cc, whitespace */
5588 0x0004000C, /* 2 Zs, whitespace */
5589 0x00000018, /* 3 Po */
5590 0x0006001A, /* 4 Sc, currency */
5591 0x00000015, /* 5 Ps */
5592 0x00000016, /* 6 Pe */
5593 0x00000019, /* 7 Sm */
5594 0x00000014, /* 8 Pd */
5595 0x00036089, /* 9 Nd, identifier part, decimal 16 */
5596 0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */
5597 0x0000001B, /* 11 Sk */
5598 0x00050017, /* 12 Pc, underscore */
5599 0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */
5600 0x0000000C, /* 14 Zs */
5601 0x0000001C, /* 15 So */
5602 0x00070182, /* 16 Ll, identifier start */
5603 0x0000600B, /* 17 No, decimal 16 */
5604 0x0000500B, /* 18 No, decimal 8 */
5605 0x0000800B, /* 19 No, strange */
5606 0x08270181, /* 20 Lu, hasLower (add 32), identifier start */
5607 0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */
5608 0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */
5609 0x00670181, /* 23 Lu, hasLower (add 1), identifier start */
5610 0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */
5611 0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */
5612 0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */
5613 0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */
5614 0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */
5615 0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */
5616 0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */
5617 0x33670181, /* 31 Lu, hasLower (add 205), identifier start */
5618 0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */
5619 0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */
5620 0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */
5621 0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */
5622 0x34670181, /* 36 Lu, hasLower (add 209), identifier start */
5623 0x35670181, /* 37 Lu, hasLower (add 213), identifier start */
5624 0x00070181, /* 38 Lu, identifier start */
5625 0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */
5626 0x00070185, /* 40 Lo, identifier start */
5627 0x36670181, /* 41 Lu, hasLower (add 217), identifier start */
5628 0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */
5629 0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */
5630 0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */
5631 0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */
5632 0x00000000, /* 46 unassigned */
5633 0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */
5634 0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */
5635 0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */
5636 0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */
5637 0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */
5638 0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */
5639 0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */
5640 0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */
5641 0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */
5642 0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */
5643 0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */
5644 0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */
5645 0x00070084, /* 59 Lm, identifier start */
5646 0x00030086, /* 60 Mn, identifier part */
5647 0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */
5648 0x09670181, /* 62 Lu, hasLower (add 37), identifier start */
5649 0x10270181, /* 63 Lu, hasLower (add 64), identifier start */
5650 0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */
5651 0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */
5652 0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */
5653 0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */
5654 0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */
5655 0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */
5656 0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */
5657 0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */
5658 0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */
5659 0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */
5660 0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */
5661 0x14270181, /* 75 Lu, hasLower (add 80), identifier start */
5662 0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */
5663 0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */
5664 0x00034089, /* 78 Nd, identifier part, decimal 0 */
5665 0x00000087, /* 79 Me */
5666 0x00030088, /* 80 Mc, identifier part */
5667 0x00037489, /* 81 Nd, identifier part, decimal 26 */
5668 0x00005A0B, /* 82 No, decimal 13 */
5669 0x00006E0B, /* 83 No, decimal 23 */
5670 0x0000740B, /* 84 No, decimal 26 */
5671 0x0000000B, /* 85 No */
5672 0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */
5673 0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */
5674 0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */
5675 0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */
5676 0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */
5677 0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */
5678 0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */
5679 0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */
5680 0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */
5681 0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */
5682 0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */
5683 0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */
5684 0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */
5685 0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */
5686 0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */
5687 0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */
5688 0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */
5689 0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */
5690 0x00010010, /* 104 Cf, ignorable */
5691 0x0004000D, /* 105 Zl, whitespace */
5692 0x0004000E, /* 106 Zp, whitespace */
5693 0x0000400B, /* 107 No, decimal 0 */
5694 0x0000440B, /* 108 No, decimal 2 */
5695 0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */
5696 0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */
5697 0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */
5698 0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */
5699 0x0007818A, /* 113 Nl, identifier start, strange */
5700 0x0000420B, /* 114 No, decimal 1 */
5701 0x0000720B, /* 115 No, decimal 25 */
5702 0x06A0001C, /* 116 So, hasLower (add 26) */
5703 0x0690001C, /* 117 So, hasUpper (subtract 26) */
5704 0x00006C0B, /* 118 No, decimal 22 */
5705 0x0000560B, /* 119 No, decimal 11 */
5706 0x0007738A, /* 120 Nl, identifier start, decimal 25 */
5707 0x0007418A, /* 121 Nl, identifier start, decimal 0 */
5708 0x00000013, /* 122 Cs */
5709 0x00000012 /* 123 Co */
5712 const jschar js_uriReservedPlusPound_ucstr[] =
5713 {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
5714 const jschar js_uriUnescaped_ucstr[] =
5715 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
5716 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
5717 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
5718 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
5719 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
5720 '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
5723 * This table allows efficient testing for the regular expression \w which is
5724 * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z].
5726 const bool js_alnum[] = {
5727 /* 0 1 2 3 4 5 5 7 8 9 */
5728 /* 0 */ false, false, false, false, false, false, false, false, false, false,
5729 /* 1 */ false, false, false, false, false, false, false, false, false, false,
5730 /* 2 */ false, false, false, false, false, false, false, false, false, false,
5731 /* 3 */ false, false, false, false, false, false, false, false, false, false,
5732 /* 4 */ false, false, false, false, false, false, false, false, true, true,
5733 /* 5 */ true, true, true, true, true, true, true, true, false, false,
5734 /* 6 */ false, false, false, false, false, true, true, true, true, true,
5735 /* 7 */ true, true, true, true, true, true, true, true, true, true,
5736 /* 8 */ true, true, true, true, true, true, true, true, true, true,
5737 /* 9 */ true, false, false, false, false, true, false, true, true, true,
5738 /* 10 */ true, true, true, true, true, true, true, true, true, true,
5739 /* 11 */ true, true, true, true, true, true, true, true, true, true,
5740 /* 12 */ true, true, true, false, false, false, false, false
5743 #define URI_CHUNK 64U
5745 static inline bool
5746 TransferBufferToString(JSContext *cx, JSCharBuffer &cb, Value *rval)
5748 JSString *str = js_NewStringFromCharBuffer(cx, cb);
5749 if (!str)
5750 return false;
5751 rval->setString(str);
5752 return true;;
5756 * ECMA 3, 15.1.3 URI Handling Function Properties
5758 * The following are implementations of the algorithms
5759 * given in the ECMA specification for the hidden functions
5760 * 'Encode' and 'Decode'.
5762 static JSBool
5763 Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
5764 const jschar *unescapedSet2, Value *rval)
5766 size_t length, j, k, L;
5767 JSCharBuffer cb(cx);
5768 const jschar *chars;
5769 jschar c, c2;
5770 uint32 v;
5771 uint8 utf8buf[4];
5772 jschar hexBuf[4];
5773 static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
5775 str->getCharsAndLength(chars, length);
5776 if (length == 0) {
5777 rval->setString(cx->runtime->emptyString);
5778 return JS_TRUE;
5781 /* From this point the control must goto bad on failures. */
5782 hexBuf[0] = '%';
5783 hexBuf[3] = 0;
5784 for (k = 0; k < length; k++) {
5785 c = chars[k];
5786 if (js_strchr(unescapedSet, c) ||
5787 (unescapedSet2 && js_strchr(unescapedSet2, c))) {
5788 if (!cb.append(c))
5789 return JS_FALSE;
5790 } else {
5791 if ((c >= 0xDC00) && (c <= 0xDFFF)) {
5792 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5793 JSMSG_BAD_URI, NULL);
5794 return JS_FALSE;
5796 if (c < 0xD800 || c > 0xDBFF) {
5797 v = c;
5798 } else {
5799 k++;
5800 if (k == length) {
5801 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5802 JSMSG_BAD_URI, NULL);
5803 return JS_FALSE;
5805 c2 = chars[k];
5806 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
5807 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5808 JSMSG_BAD_URI, NULL);
5809 return JS_FALSE;
5811 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
5813 L = js_OneUcs4ToUtf8Char(utf8buf, v);
5814 for (j = 0; j < L; j++) {
5815 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
5816 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
5817 if (!cb.append(hexBuf, 3))
5818 return JS_FALSE;
5823 return TransferBufferToString(cx, cb, rval);
5826 static JSBool
5827 Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
5829 size_t length, start, k;
5830 JSCharBuffer cb(cx);
5831 const jschar *chars;
5832 jschar c, H;
5833 uint32 v;
5834 jsuint B;
5835 uint8 octets[4];
5836 intN j, n;
5838 str->getCharsAndLength(chars, length);
5839 if (length == 0) {
5840 rval->setString(cx->runtime->emptyString);
5841 return JS_TRUE;
5844 /* From this point the control must goto bad on failures. */
5845 for (k = 0; k < length; k++) {
5846 c = chars[k];
5847 if (c == '%') {
5848 start = k;
5849 if ((k + 2) >= length)
5850 goto report_bad_uri;
5851 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5852 goto report_bad_uri;
5853 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5854 k += 2;
5855 if (!(B & 0x80)) {
5856 c = (jschar)B;
5857 } else {
5858 n = 1;
5859 while (B & (0x80 >> n))
5860 n++;
5861 if (n == 1 || n > 4)
5862 goto report_bad_uri;
5863 octets[0] = (uint8)B;
5864 if (k + 3 * (n - 1) >= length)
5865 goto report_bad_uri;
5866 for (j = 1; j < n; j++) {
5867 k++;
5868 if (chars[k] != '%')
5869 goto report_bad_uri;
5870 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5871 goto report_bad_uri;
5872 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5873 if ((B & 0xC0) != 0x80)
5874 goto report_bad_uri;
5875 k += 2;
5876 octets[j] = (char)B;
5878 v = Utf8ToOneUcs4Char(octets, n);
5879 if (v >= 0x10000) {
5880 v -= 0x10000;
5881 if (v > 0xFFFFF)
5882 goto report_bad_uri;
5883 c = (jschar)((v & 0x3FF) + 0xDC00);
5884 H = (jschar)((v >> 10) + 0xD800);
5885 if (!cb.append(H))
5886 return JS_FALSE;
5887 } else {
5888 c = (jschar)v;
5891 if (js_strchr(reservedSet, c)) {
5892 if (!cb.append(chars + start, k - start + 1))
5893 return JS_FALSE;
5894 } else {
5895 if (!cb.append(c))
5896 return JS_FALSE;
5898 } else {
5899 if (!cb.append(c))
5900 return JS_FALSE;
5904 return TransferBufferToString(cx, cb, rval);
5906 report_bad_uri:
5907 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
5908 /* FALL THROUGH */
5910 return JS_FALSE;
5913 static JSBool
5914 str_decodeURI(JSContext *cx, uintN argc, Value *vp)
5916 JSString *str;
5918 str = ArgToRootedString(cx, argc, vp, 0);
5919 if (!str)
5920 return JS_FALSE;
5921 return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp);
5924 static JSBool
5925 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5927 JSString *str;
5929 str = ArgToRootedString(cx, argc, vp, 0);
5930 if (!str)
5931 return JS_FALSE;
5932 return Decode(cx, str, js_empty_ucstr, vp);
5935 static JSBool
5936 str_encodeURI(JSContext *cx, uintN argc, Value *vp)
5938 JSString *str;
5940 str = ArgToRootedString(cx, argc, vp, 0);
5941 if (!str)
5942 return JS_FALSE;
5943 return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr,
5944 vp);
5947 static JSBool
5948 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5950 JSString *str;
5952 str = ArgToRootedString(cx, argc, vp, 0);
5953 if (!str)
5954 return JS_FALSE;
5955 return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp);
5959 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
5960 * least 4 bytes long. Return the number of UTF-8 bytes of data written.
5963 js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)
5965 int utf8Length = 1;
5967 JS_ASSERT(ucs4Char <= 0x10FFFF);
5968 if (ucs4Char < 0x80) {
5969 *utf8Buffer = (uint8)ucs4Char;
5970 } else {
5971 int i;
5972 uint32 a = ucs4Char >> 11;
5973 utf8Length = 2;
5974 while (a) {
5975 a >>= 5;
5976 utf8Length++;
5978 i = utf8Length;
5979 while (--i) {
5980 utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
5981 ucs4Char >>= 6;
5983 *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
5985 return utf8Length;
5989 * Convert a utf8 character sequence into a UCS-4 character and return that
5990 * character. It is assumed that the caller already checked that the sequence
5991 * is valid.
5993 static uint32
5994 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
5996 uint32 ucs4Char;
5997 uint32 minucs4Char;
5998 /* from Unicode 3.1, non-shortest form is illegal */
5999 static const uint32 minucs4Table[] = {
6000 0x00000080, 0x00000800, 0x00010000
6003 JS_ASSERT(utf8Length >= 1 && utf8Length <= 4);
6004 if (utf8Length == 1) {
6005 ucs4Char = *utf8Buffer;
6006 JS_ASSERT(!(ucs4Char & 0x80));
6007 } else {
6008 JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) ==
6009 (0x100 - (1 << (8-utf8Length))));
6010 ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
6011 minucs4Char = minucs4Table[utf8Length-2];
6012 while (--utf8Length) {
6013 JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
6014 ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
6016 if (JS_UNLIKELY(ucs4Char < minucs4Char)) {
6017 ucs4Char = OVERLONG_UTF8;
6018 } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
6019 ucs4Char = 0xFFFD;
6022 return ucs4Char;
6025 namespace js {
6027 size_t
6028 PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, uint32 quote)
6030 const jschar *chars, *charsEnd;
6031 size_t n;
6032 const char *escape;
6033 char c;
6034 uintN u, hex, shift;
6035 enum {
6036 STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
6037 } state;
6039 JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
6040 JS_ASSERT_IF(!buffer, bufferSize == 0);
6041 JS_ASSERT_IF(fp, !buffer);
6043 if (bufferSize == 0)
6044 buffer = NULL;
6045 else
6046 bufferSize--;
6048 str->getCharsAndEnd(chars, charsEnd);
6049 n = 0;
6050 state = FIRST_QUOTE;
6051 shift = 0;
6052 hex = 0;
6053 u = 0;
6054 c = 0; /* to quell GCC warnings */
6056 for (;;) {
6057 switch (state) {
6058 case STOP:
6059 goto stop;
6060 case FIRST_QUOTE:
6061 state = CHARS;
6062 goto do_quote;
6063 case LAST_QUOTE:
6064 state = STOP;
6065 do_quote:
6066 if (quote == 0)
6067 continue;
6068 c = (char)quote;
6069 break;
6070 case CHARS:
6071 if (chars == charsEnd) {
6072 state = LAST_QUOTE;
6073 continue;
6075 u = *chars++;
6076 if (u < ' ') {
6077 if (u != 0) {
6078 escape = strchr(js_EscapeMap, (int)u);
6079 if (escape) {
6080 u = escape[1];
6081 goto do_escape;
6084 goto do_hex_escape;
6086 if (u < 127) {
6087 if (u == quote || u == '\\')
6088 goto do_escape;
6089 c = (char)u;
6090 } else if (u < 0x100) {
6091 goto do_hex_escape;
6092 } else {
6093 shift = 16;
6094 hex = u;
6095 u = 'u';
6096 goto do_escape;
6098 break;
6099 do_hex_escape:
6100 shift = 8;
6101 hex = u;
6102 u = 'x';
6103 do_escape:
6104 c = '\\';
6105 state = ESCAPE_START;
6106 break;
6107 case ESCAPE_START:
6108 JS_ASSERT(' ' <= u && u < 127);
6109 c = (char)u;
6110 state = ESCAPE_MORE;
6111 break;
6112 case ESCAPE_MORE:
6113 if (shift == 0) {
6114 state = CHARS;
6115 continue;
6117 shift -= 4;
6118 u = 0xF & (hex >> shift);
6119 c = (char)(u + (u < 10 ? '0' : 'A' - 10));
6120 break;
6122 if (buffer) {
6123 JS_ASSERT(n <= bufferSize);
6124 if (n != bufferSize) {
6125 buffer[n] = c;
6126 } else {
6127 buffer[n] = '\0';
6128 buffer = NULL;
6130 } else if (fp) {
6131 if (fputc(c, fp) < 0)
6132 return size_t(-1);
6134 n++;
6136 stop:
6137 if (buffer)
6138 buffer[n] = '\0';
6139 return n;
6142 } /* namespace js */