Encapsulate RegExpStatics more. (r=gal, b=610223)
[mozilla-central.git] / js / src / jsstr.cpp
blob54b6cd92d04fa8f471ec4dc65f483b6dc22cd305
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 JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
93 JSStringFinalizeOp str_finalizers[JS_EXTERNAL_STRING_LIMIT] = {
94 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
97 const jschar *
98 js_GetStringChars(JSContext *cx, JSString *str)
100 if (!js_MakeStringImmutable(cx, str))
101 return NULL;
102 return str->flatChars();
105 void
106 JSString::flatten()
108 JSString *topNode;
109 jschar *chars;
110 size_t capacity;
111 JS_ASSERT(isRope());
114 * This can be called from any string in the rope, so first traverse to the
115 * top node.
117 topNode = this;
118 while (topNode->isInteriorNode())
119 topNode = topNode->interiorNodeParent();
121 #ifdef DEBUG
122 size_t length = topNode->length();
123 #endif
125 capacity = topNode->topNodeCapacity();
126 chars = (jschar *) topNode->topNodeBuffer();
129 * To make the traversal simpler, convert the top node to be marked as an
130 * interior node with a NULL parent, so that we end up at NULL when we are
131 * done processing it.
133 topNode->convertToInteriorNode(NULL);
134 JSString *str = topNode, *next;
135 size_t pos = 0;
138 * Traverse the tree, making each interior string dependent on the resulting
139 * string.
141 while (str) {
142 switch (str->ropeTraversalCount()) {
143 case 0:
144 next = str->ropeLeft();
147 * We know the "offset" field for the new dependent string now, but
148 * not later, so store it early. We have to be careful with this:
149 * mLeft is replaced by mOffset.
151 str->startTraversalConversion(chars, pos);
152 str->ropeIncrementTraversalCount();
153 if (next->isInteriorNode()) {
154 str = next;
155 } else {
156 js_strncpy(chars + pos, next->chars(), next->length());
157 pos += next->length();
159 break;
160 case 1:
161 next = str->ropeRight();
162 str->ropeIncrementTraversalCount();
163 if (next->isInteriorNode()) {
164 str = next;
165 } else {
166 js_strncpy(chars + pos, next->chars(), next->length());
167 pos += next->length();
169 break;
170 case 2:
171 next = str->interiorNodeParent();
172 /* Make the string a dependent string dependent with the right fields. */
173 str->finishTraversalConversion(topNode, chars, pos);
174 str = next;
175 break;
176 default:
177 JS_NOT_REACHED("bad traversal count");
181 JS_ASSERT(pos == length);
182 /* Set null terminator. */
183 chars[pos] = 0;
184 topNode->initFlatMutable(chars, pos, capacity);
187 #ifdef JS_TRACER
189 int32 JS_FASTCALL
190 js_Flatten(JSString* str)
192 str->flatten();
193 return 0;
195 JS_DEFINE_CALLINFO_1(extern, INT32, js_Flatten, STRING, 0, nanojit::ACCSET_STORE_ANY)
197 #endif /* !JS_TRACER */
199 static JS_ALWAYS_INLINE size_t
200 RopeAllocSize(const size_t length, size_t *capacity)
202 static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
204 size_t size;
205 size_t minCap = (length + 1) * sizeof(jschar);
208 * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
209 * next power of 2. This is similar to what we do with arrays; see
210 * JSObject::ensureDenseArrayElements.
212 if (length > ROPE_DOUBLING_MAX)
213 size = minCap + (minCap / 8);
214 else
215 size = 1 << (JS_CeilingLog2(minCap));
216 *capacity = (size / sizeof(jschar)) - 1;
217 JS_ASSERT(size >= sizeof(JSRopeBufferInfo));
218 return size;
221 static JS_ALWAYS_INLINE JSRopeBufferInfo *
222 ObtainRopeBuffer(JSContext *cx, bool usingLeft, bool usingRight,
223 JSRopeBufferInfo *sourceBuffer, size_t length,
224 JSString *left, JSString *right)
226 JSRopeBufferInfo *buf;
227 size_t capacity;
230 * We need to survive a GC upon failure and in case creating a new
231 * string header triggers a GC, but we've broken the invariant that
232 * rope top nodes always point to freeable JSRopeBufferInfo
233 * objects, so make them point to NULL.
235 if (usingLeft)
236 left->nullifyTopNodeBuffer();
237 if (usingRight)
238 right->nullifyTopNodeBuffer();
241 * Try to reuse sourceBuffer. If it's not suitable, free it and create a
242 * suitable buffer.
244 if (length <= sourceBuffer->capacity) {
245 buf = sourceBuffer;
246 } else {
247 size_t allocSize = RopeAllocSize(length, &capacity);
248 cx->free(sourceBuffer);
249 buf = (JSRopeBufferInfo *) cx->malloc(allocSize);
250 if (!buf)
251 return NULL;
252 buf->capacity = capacity;
254 return buf;
257 static JS_ALWAYS_INLINE JSString *
258 FinishConcat(JSContext *cx, bool usingLeft, bool usingRight,
259 JSString *left, JSString *right, size_t length,
260 JSRopeBufferInfo *buf)
262 JSString *res = js_NewGCString(cx);
263 if (!res) {
264 cx->free(buf);
265 return NULL;
267 res->initTopNode(left, right, length, buf);
268 if (usingLeft)
269 left->convertToInteriorNode(res);
270 if (usingRight)
271 right->convertToInteriorNode(res);
272 return res;
275 JSString * JS_FASTCALL
276 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
278 size_t length, leftLen, rightLen;
279 bool leftRopeTop, rightRopeTop;
281 leftLen = left->length();
282 if (leftLen == 0)
283 return right;
284 rightLen = right->length();
285 if (rightLen == 0)
286 return left;
288 length = leftLen + rightLen;
290 if (JSShortString::fitsIntoShortString(length)) {
291 JSShortString *shortStr = js_NewGCShortString(cx);
292 if (!shortStr)
293 return NULL;
295 jschar *buf = shortStr->init(length);
296 js_short_strncpy(buf, left->chars(), leftLen);
297 js_short_strncpy(buf + leftLen, right->chars(), rightLen);
298 buf[length] = 0;
299 return shortStr->header();
303 * We need to enforce a tree structure in ropes: every node needs to have a
304 * unique parent. So, we can't have the left or right child be in the middle
305 * of a rope tree. One potential solution is to traverse the subtree for the
306 * argument string and create a new flat string, but that would add
307 * complexity and is a rare case, so we simply flatten the entire rope that
308 * contains it. The case where left and right are part of the same rope is
309 * handled implicitly.
311 if (left->isInteriorNode())
312 left->flatten();
313 if (right->isInteriorNode())
314 right->flatten();
316 if (left->isMutable() && !right->isRope() &&
317 left->flatCapacity() >= length) {
318 JS_ASSERT(left->isFlat());
321 * If left has enough unused space at the end of its buffer that we can
322 * fit the entire new string there, just write there.
324 jschar *chars = left->chars();
325 js_strncpy(chars + leftLen, right->chars(), rightLen);
326 chars[length] = 0;
327 JSString *res = js_NewString(cx, chars, length);
328 if (!res)
329 return NULL;
330 res->initFlatMutable(chars, length, left->flatCapacity());
331 left->initDependent(res, res->flatChars(), leftLen);
332 return res;
335 if (length > JSString::MAX_LENGTH) {
336 if (JS_ON_TRACE(cx)) {
337 if (!CanLeaveTrace(cx))
338 return NULL;
339 LeaveTrace(cx);
341 js_ReportAllocationOverflow(cx);
342 return NULL;
345 leftRopeTop = left->isTopNode();
346 rightRopeTop = right->isTopNode();
349 * To make traversal more manageable, we enforce that, unless the children
350 * are leaves, the two children of a rope node must be distinct.
352 if (left == right && leftRopeTop) {
353 left->flatten();
354 leftRopeTop = false;
355 rightRopeTop = false;
356 JS_ASSERT(leftLen = left->length());
357 JS_ASSERT(rightLen = right->length());
358 JS_ASSERT(!left->isTopNode());
359 JS_ASSERT(!right->isTopNode());
363 * There are 4 cases, based on whether on whether the left or right is a
364 * rope or non-rope string.
366 JSRopeBufferInfo *buf = NULL;
368 if (leftRopeTop) {
369 /* Left child is a rope. */
370 JSRopeBufferInfo *leftBuf = left->topNodeBuffer();
372 /* If both children are ropes, steal the larger buffer. */
373 if (JS_UNLIKELY(rightRopeTop)) {
374 JSRopeBufferInfo *rightBuf = right->topNodeBuffer();
376 /* Put the larger buffer into 'leftBuf'. */
377 if (leftBuf->capacity >= rightBuf->capacity) {
378 cx->free(rightBuf);
379 } else {
380 cx->free(leftBuf);
381 leftBuf = rightBuf;
385 buf = ObtainRopeBuffer(cx, true, rightRopeTop, leftBuf, length, left, right);
386 if (!buf)
387 return NULL;
388 } else if (JS_UNLIKELY(rightRopeTop)) {
389 /* Right child is a rope: steal its buffer if big enough. */
390 JSRopeBufferInfo *rightBuf = right->topNodeBuffer();
392 buf = ObtainRopeBuffer(cx, false, true, rightBuf, length, left, right);
393 if (!buf)
394 return NULL;
395 } else {
396 /* Neither child is a rope: need to make a new buffer. */
397 size_t capacity;
398 size_t allocSize = RopeAllocSize(length, &capacity);
399 buf = (JSRopeBufferInfo *) cx->malloc(allocSize);
400 if (!buf)
401 return NULL;
402 buf->capacity = capacity;
405 return FinishConcat(cx, leftRopeTop, rightRopeTop, left, right, length, buf);
408 const jschar *
409 JSString::undepend(JSContext *cx)
411 size_t n, size;
412 jschar *s;
414 ensureNotRope();
416 if (isDependent()) {
417 n = dependentLength();
418 size = (n + 1) * sizeof(jschar);
419 s = (jschar *) cx->malloc(size);
420 if (!s)
421 return NULL;
423 js_strncpy(s, dependentChars(), n);
424 s[n] = 0;
425 initFlat(s, n);
427 #ifdef DEBUG
429 JSRuntime *rt = cx->runtime;
430 JS_RUNTIME_UNMETER(rt, liveDependentStrings);
431 JS_RUNTIME_UNMETER(rt, totalDependentStrings);
432 JS_LOCK_RUNTIME_VOID(rt,
433 (rt->strdepLengthSum -= (double)n,
434 rt->strdepLengthSquaredSum -= (double)n * (double)n));
436 #endif
439 return flatChars();
442 JSBool
443 js_MakeStringImmutable(JSContext *cx, JSString *str)
446 * Flattening a rope may result in a dependent string, so we need to flatten
447 * before undepending the string.
449 str->ensureNotRope();
450 if (!str->ensureNotDependent(cx)) {
451 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
452 return JS_FALSE;
454 str->flatClearMutable();
455 return JS_TRUE;
458 static JSString *
459 ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg)
461 if (arg >= argc)
462 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
463 vp += 2 + arg;
465 if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp))
466 return NULL;
468 JSString *str;
469 if (vp->isString()) {
470 str = vp->toString();
471 } else if (vp->isBoolean()) {
472 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
473 (int)vp->toBoolean()]);
474 } else if (vp->isNull()) {
475 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
476 } else if (vp->isUndefined()) {
477 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
479 else {
480 str = js_NumberToString(cx, vp->toNumber());
481 if (str)
482 vp->setString(str);
484 return str;
488 * Forward declarations for URI encode/decode and helper routines
490 static JSBool
491 str_decodeURI(JSContext *cx, uintN argc, Value *vp);
493 static JSBool
494 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp);
496 static JSBool
497 str_encodeURI(JSContext *cx, uintN argc, Value *vp);
499 static JSBool
500 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp);
502 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
504 static uint32
505 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
508 * Contributions from the String class to the set of methods defined for the
509 * global object. escape and unescape used to be defined in the Mocha library,
510 * but as ECMA decided to spec them, they've been moved to the core engine
511 * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
512 * characters by unescape.)
516 * Stuff to emulate the old libmocha escape, which took a second argument
517 * giving the type of escape to perform. Retained for compatibility, and
518 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
521 #define URL_XALPHAS ((uint8) 1)
522 #define URL_XPALPHAS ((uint8) 2)
523 #define URL_PATH ((uint8) 4)
525 static const uint8 urlCharType[256] =
526 /* Bit 0 xalpha -- the alphas
527 * Bit 1 xpalpha -- as xalpha but
528 * converts spaces to plus and plus to %20
529 * Bit 2 ... path -- as xalphas but doesn't escape '/'
531 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
532 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
533 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
534 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
535 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
536 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
537 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
538 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
539 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
540 0, };
542 /* This matches the ECMA escape set when mask is 7 (default.) */
544 #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
546 /* See ECMA-262 Edition 3 B.2.1 */
547 JSBool
548 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
550 JSString *str;
551 size_t i, ni, length, newlength;
552 const jschar *chars;
553 jschar *newchars;
554 jschar ch;
555 jsint mask;
556 jsdouble d;
557 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
558 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
560 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
561 if (argc > 1) {
562 if (!ValueToNumber(cx, argv[1], &d))
563 return JS_FALSE;
564 if (!JSDOUBLE_IS_FINITE(d) ||
565 (mask = (jsint)d) != d ||
566 mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
568 char numBuf[12];
569 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
570 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
571 JSMSG_BAD_STRING_MASK, numBuf);
572 return JS_FALSE;
576 str = ArgToRootedString(cx, argc, argv - 2, 0);
577 if (!str)
578 return JS_FALSE;
580 str->getCharsAndLength(chars, length);
581 newlength = length;
583 /* Take a first pass and see how big the result string will need to be. */
584 for (i = 0; i < length; i++) {
585 if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
586 continue;
587 if (ch < 256) {
588 if (mask == URL_XPALPHAS && ch == ' ')
589 continue; /* The character will be encoded as '+' */
590 newlength += 2; /* The character will be encoded as %XX */
591 } else {
592 newlength += 5; /* The character will be encoded as %uXXXX */
596 * This overflow test works because newlength is incremented by at
597 * most 5 on each iteration.
599 if (newlength < length) {
600 js_ReportAllocationOverflow(cx);
601 return JS_FALSE;
605 if (newlength >= ~(size_t)0 / sizeof(jschar)) {
606 js_ReportAllocationOverflow(cx);
607 return JS_FALSE;
610 newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
611 if (!newchars)
612 return JS_FALSE;
613 for (i = 0, ni = 0; i < length; i++) {
614 if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
615 newchars[ni++] = ch;
616 } else if (ch < 256) {
617 if (mask == URL_XPALPHAS && ch == ' ') {
618 newchars[ni++] = '+'; /* convert spaces to pluses */
619 } else {
620 newchars[ni++] = '%';
621 newchars[ni++] = digits[ch >> 4];
622 newchars[ni++] = digits[ch & 0xF];
624 } else {
625 newchars[ni++] = '%';
626 newchars[ni++] = 'u';
627 newchars[ni++] = digits[ch >> 12];
628 newchars[ni++] = digits[(ch & 0xF00) >> 8];
629 newchars[ni++] = digits[(ch & 0xF0) >> 4];
630 newchars[ni++] = digits[ch & 0xF];
633 JS_ASSERT(ni == newlength);
634 newchars[newlength] = 0;
636 str = js_NewString(cx, newchars, newlength);
637 if (!str) {
638 cx->free(newchars);
639 return JS_FALSE;
641 rval->setString(str);
642 return JS_TRUE;
644 #undef IS_OK
646 static JSBool
647 str_escape(JSContext *cx, uintN argc, Value *vp)
649 JSObject *obj = ComputeThisFromVp(cx, vp);
650 return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
653 /* See ECMA-262 Edition 3 B.2.2 */
654 static JSBool
655 str_unescape(JSContext *cx, uintN argc, Value *vp)
657 JSString *str;
658 size_t i, ni, length;
659 const jschar *chars;
660 jschar *newchars;
661 jschar ch;
663 str = ArgToRootedString(cx, argc, vp, 0);
664 if (!str)
665 return JS_FALSE;
667 str->getCharsAndLength(chars, length);
669 /* Don't bother allocating less space for the new string. */
670 newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
671 if (!newchars)
672 return JS_FALSE;
673 ni = i = 0;
674 while (i < length) {
675 ch = chars[i++];
676 if (ch == '%') {
677 if (i + 1 < length &&
678 JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
680 ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
681 i += 2;
682 } else if (i + 4 < length && chars[i] == 'u' &&
683 JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
684 JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
686 ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
687 + JS7_UNHEX(chars[i + 2])) << 4)
688 + JS7_UNHEX(chars[i + 3])) << 4)
689 + JS7_UNHEX(chars[i + 4]);
690 i += 5;
693 newchars[ni++] = ch;
695 newchars[ni] = 0;
697 str = js_NewString(cx, newchars, ni);
698 if (!str) {
699 cx->free(newchars);
700 return JS_FALSE;
702 vp->setString(str);
703 return JS_TRUE;
706 #if JS_HAS_UNEVAL
707 static JSBool
708 str_uneval(JSContext *cx, uintN argc, Value *vp)
710 JSString *str;
712 str = js_ValueToSource(cx, argc != 0 ? vp[2] : UndefinedValue());
713 if (!str)
714 return JS_FALSE;
715 vp->setString(str);
716 return JS_TRUE;
718 #endif
720 const char js_escape_str[] = "escape";
721 const char js_unescape_str[] = "unescape";
722 #if JS_HAS_UNEVAL
723 const char js_uneval_str[] = "uneval";
724 #endif
725 const char js_decodeURI_str[] = "decodeURI";
726 const char js_encodeURI_str[] = "encodeURI";
727 const char js_decodeURIComponent_str[] = "decodeURIComponent";
728 const char js_encodeURIComponent_str[] = "encodeURIComponent";
730 static JSFunctionSpec string_functions[] = {
731 JS_FN(js_escape_str, str_escape, 1,0),
732 JS_FN(js_unescape_str, str_unescape, 1,0),
733 #if JS_HAS_UNEVAL
734 JS_FN(js_uneval_str, str_uneval, 1,0),
735 #endif
736 JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
737 JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
738 JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
739 JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
741 JS_FS_END
744 jschar js_empty_ucstr[] = {0};
745 JSSubString js_EmptySubString = {0, js_empty_ucstr};
747 static JSBool
748 str_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
750 JSString *str;
752 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
753 if (obj->getClass() == &js_StringClass) {
754 /* Follow ECMA-262 by fetching intrinsic length of our string. */
755 str = obj->getPrimitiveThis().toString();
756 } else {
757 /* Preserve compatibility: convert obj to a string primitive. */
758 str = js_ValueToString(cx, ObjectValue(*obj));
759 if (!str)
760 return JS_FALSE;
763 vp->setInt32(str->length());
766 return JS_TRUE;
769 #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
771 static JSBool
772 str_enumerate(JSContext *cx, JSObject *obj)
774 JSString *str, *str1;
775 size_t i, length;
777 str = obj->getPrimitiveThis().toString();
779 length = str->length();
780 for (i = 0; i < length; i++) {
781 str1 = js_NewDependentString(cx, str, i, 1);
782 if (!str1)
783 return JS_FALSE;
784 if (!obj->defineProperty(cx, INT_TO_JSID(i), StringValue(str1),
785 PropertyStub, PropertyStub,
786 STRING_ELEMENT_ATTRS)) {
787 return JS_FALSE;
791 return obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
792 UndefinedValue(), NULL, NULL,
793 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED);
796 static JSBool
797 str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
798 JSObject **objp)
800 if (!JSID_IS_INT(id))
801 return JS_TRUE;
803 JSString *str = obj->getPrimitiveThis().toString();
805 jsint slot = JSID_TO_INT(id);
806 if ((size_t)slot < str->length()) {
807 JSString *str1 = JSString::getUnitString(cx, str, size_t(slot));
808 if (!str1)
809 return JS_FALSE;
810 if (!obj->defineProperty(cx, id, StringValue(str1), NULL, NULL,
811 STRING_ELEMENT_ATTRS)) {
812 return JS_FALSE;
814 *objp = obj;
816 return JS_TRUE;
819 Class js_StringClass = {
820 js_String_str,
821 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
822 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
823 PropertyStub, /* addProperty */
824 PropertyStub, /* delProperty */
825 str_getProperty,
826 PropertyStub, /* setProperty */
827 str_enumerate,
828 (JSResolveOp)str_resolve,
829 ConvertStub
832 #define NORMALIZE_THIS(cx,vp,str) \
833 JS_BEGIN_MACRO \
834 if (vp[1].isString()) { \
835 str = vp[1].toString(); \
836 } else { \
837 str = NormalizeThis(cx, vp); \
838 if (!str) \
839 return JS_FALSE; \
841 JS_END_MACRO
843 static JSString *
844 NormalizeThis(JSContext *cx, Value *vp)
846 if (vp[1].isNullOrUndefined() && !ComputeThisFromVp(cx, vp))
847 return NULL;
850 * String.prototype.{toString,toSource,valueOf} throw a TypeError if the
851 * this-argument is not a string or a String object. So those methods use
852 * js::GetPrimitiveThis which provides that behavior.
854 * By standard, the rest of the String methods must ToString the
855 * this-argument rather than throw a TypeError. So those methods use
856 * NORMALIZE_THIS (and thus NormalizeThis) instead.
858 if (vp[1].isObject()) {
859 JSObject *obj = &vp[1].toObject();
860 if (obj->getClass() == &js_StringClass) {
861 vp[1] = obj->getPrimitiveThis();
862 return vp[1].toString();
866 JSString *str = js_ValueToString(cx, vp[1]);
867 if (!str)
868 return NULL;
869 vp[1].setString(str);
870 return str;
873 #if JS_HAS_TOSOURCE
876 * String.prototype.quote is generic (as are most string methods), unlike
877 * toSource, toString, and valueOf.
879 static JSBool
880 str_quote(JSContext *cx, uintN argc, Value *vp)
882 JSString *str;
884 NORMALIZE_THIS(cx, vp, str);
885 str = js_QuoteString(cx, str, '"');
886 if (!str)
887 return JS_FALSE;
888 vp->setString(str);
889 return JS_TRUE;
892 static JSBool
893 str_toSource(JSContext *cx, uintN argc, Value *vp)
895 JSString *str;
896 if (!GetPrimitiveThis(cx, vp, &str))
897 return false;
899 str = js_QuoteString(cx, str, '"');
900 if (!str)
901 return false;
903 char buf[16];
904 size_t j = JS_snprintf(buf, sizeof buf, "(new String(");
906 const jschar *s;
907 size_t k;
908 str->getCharsAndLength(s, k);
910 size_t n = j + k + 2;
911 jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
912 if (!t)
913 return false;
915 size_t i;
916 for (i = 0; i < j; i++)
917 t[i] = buf[i];
918 for (j = 0; j < k; i++, j++)
919 t[i] = s[j];
920 t[i++] = ')';
921 t[i++] = ')';
922 t[i] = 0;
924 str = js_NewString(cx, t, n);
925 if (!str) {
926 cx->free(t);
927 return false;
929 vp->setString(str);
930 return true;
933 #endif /* JS_HAS_TOSOURCE */
935 JSBool
936 js_str_toString(JSContext *cx, uintN argc, Value *vp)
938 JSString *str;
939 if (!GetPrimitiveThis(cx, vp, &str))
940 return false;
941 vp->setString(str);
942 return true;
946 * Java-like string native methods.
949 static JSString *
950 SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end)
952 if (begin < 0)
953 begin = 0;
954 else if (begin > length)
955 begin = length;
957 if (end < 0)
958 end = 0;
959 else if (end > length)
960 end = length;
961 if (end < begin) {
962 /* ECMA emulates old JDK1.0 java.lang.String.substring. */
963 jsdouble tmp = begin;
964 begin = end;
965 end = tmp;
968 return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin));
971 static JSBool
972 str_substring(JSContext *cx, uintN argc, Value *vp)
974 JSString *str;
975 jsdouble d;
976 jsdouble length, begin, end;
978 NORMALIZE_THIS(cx, vp, str);
979 if (argc != 0) {
980 if (!ValueToNumber(cx, vp[2], &d))
981 return JS_FALSE;
982 length = str->length();
983 begin = js_DoubleToInteger(d);
984 if (argc == 1 || vp[3].isUndefined()) {
985 end = length;
986 } else {
987 if (!ValueToNumber(cx, vp[3], &d))
988 return JS_FALSE;
989 end = js_DoubleToInteger(d);
992 str = SubstringTail(cx, str, length, begin, end);
993 if (!str)
994 return JS_FALSE;
996 vp->setString(str);
997 return JS_TRUE;
1000 JSString* JS_FASTCALL
1001 js_toLowerCase(JSContext *cx, JSString *str)
1003 size_t i, n;
1004 const jschar *s;
1005 jschar *news;
1007 str->getCharsAndLength(s, n);
1008 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
1009 if (!news)
1010 return NULL;
1011 for (i = 0; i < n; i++)
1012 news[i] = JS_TOLOWER(s[i]);
1013 news[n] = 0;
1014 str = js_NewString(cx, news, n);
1015 if (!str) {
1016 cx->free(news);
1017 return NULL;
1019 return str;
1022 static JSBool
1023 str_toLowerCase(JSContext *cx, uintN argc, Value *vp)
1025 JSString *str;
1027 NORMALIZE_THIS(cx, vp, str);
1028 str = js_toLowerCase(cx, str);
1029 if (!str)
1030 return JS_FALSE;
1031 vp->setString(str);
1032 return JS_TRUE;
1035 static JSBool
1036 str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp)
1038 JSString *str;
1041 * Forcefully ignore the first (or any) argument and return toLowerCase(),
1042 * ECMA has reserved that argument, presumably for defining the locale.
1044 if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
1045 NORMALIZE_THIS(cx, vp, str);
1046 return cx->localeCallbacks->localeToLowerCase(cx, str, Jsvalify(vp));
1048 return str_toLowerCase(cx, 0, vp);
1051 JSString* JS_FASTCALL
1052 js_toUpperCase(JSContext *cx, JSString *str)
1054 size_t i, n;
1055 const jschar *s;
1056 jschar *news;
1058 str->getCharsAndLength(s, n);
1059 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
1060 if (!news)
1061 return NULL;
1062 for (i = 0; i < n; i++)
1063 news[i] = JS_TOUPPER(s[i]);
1064 news[n] = 0;
1065 str = js_NewString(cx, news, n);
1066 if (!str) {
1067 cx->free(news);
1068 return NULL;
1070 return str;
1073 static JSBool
1074 str_toUpperCase(JSContext *cx, uintN argc, Value *vp)
1076 JSString *str;
1078 NORMALIZE_THIS(cx, vp, str);
1079 str = js_toUpperCase(cx, str);
1080 if (!str)
1081 return JS_FALSE;
1082 vp->setString(str);
1083 return JS_TRUE;
1086 static JSBool
1087 str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp)
1089 JSString *str;
1092 * Forcefully ignore the first (or any) argument and return toUpperCase(),
1093 * ECMA has reserved that argument, presumably for defining the locale.
1095 if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
1096 NORMALIZE_THIS(cx, vp, str);
1097 return cx->localeCallbacks->localeToUpperCase(cx, str, Jsvalify(vp));
1099 return str_toUpperCase(cx, 0, vp);
1102 static JSBool
1103 str_localeCompare(JSContext *cx, uintN argc, Value *vp)
1105 JSString *str, *thatStr;
1107 NORMALIZE_THIS(cx, vp, str);
1108 if (argc == 0) {
1109 vp->setInt32(0);
1110 } else {
1111 thatStr = js_ValueToString(cx, vp[2]);
1112 if (!thatStr)
1113 return JS_FALSE;
1114 if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
1115 vp[2].setString(thatStr);
1116 return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp));
1118 vp->setInt32(js_CompareStrings(str, thatStr));
1120 return JS_TRUE;
1123 JSBool
1124 js_str_charAt(JSContext *cx, uintN argc, Value *vp)
1126 JSString *str;
1127 jsint i;
1128 jsdouble d;
1130 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1131 str = vp[1].toString();
1132 i = vp[2].toInt32();
1133 if ((size_t)i >= str->length())
1134 goto out_of_range;
1135 } else {
1136 NORMALIZE_THIS(cx, vp, str);
1138 if (argc == 0) {
1139 d = 0.0;
1140 } else {
1141 if (!ValueToNumber(cx, vp[2], &d))
1142 return JS_FALSE;
1143 d = js_DoubleToInteger(d);
1146 if (d < 0 || str->length() <= d)
1147 goto out_of_range;
1148 i = (jsint) d;
1151 str = JSString::getUnitString(cx, str, size_t(i));
1152 if (!str)
1153 return JS_FALSE;
1154 vp->setString(str);
1155 return JS_TRUE;
1157 out_of_range:
1158 vp->setString(cx->runtime->emptyString);
1159 return JS_TRUE;
1162 JSBool
1163 js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp)
1165 JSString *str;
1166 jsint i;
1167 jsdouble d;
1169 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1170 str = vp[1].toString();
1171 i = vp[2].toInt32();
1172 if ((size_t)i >= str->length())
1173 goto out_of_range;
1174 } else {
1175 NORMALIZE_THIS(cx, vp, str);
1177 if (argc == 0) {
1178 d = 0.0;
1179 } else {
1180 if (!ValueToNumber(cx, vp[2], &d))
1181 return JS_FALSE;
1182 d = js_DoubleToInteger(d);
1185 if (d < 0 || str->length() <= d)
1186 goto out_of_range;
1187 i = (jsint) d;
1190 vp->setInt32(str->chars()[i]);
1191 return JS_TRUE;
1193 out_of_range:
1194 vp->setDouble(js_NaN);
1195 return JS_TRUE;
1198 jsint
1199 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1200 const jschar *pat, jsuint patlen)
1202 uint8 skip[sBMHCharSetSize];
1204 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1205 for (jsuint i = 0; i < sBMHCharSetSize; i++)
1206 skip[i] = (uint8)patlen;
1207 jsuint m = patlen - 1;
1208 for (jsuint i = 0; i < m; i++) {
1209 jschar c = pat[i];
1210 if (c >= sBMHCharSetSize)
1211 return sBMHBadPattern;
1212 skip[c] = (uint8)(m - i);
1214 jschar c;
1215 for (jsuint k = m;
1216 k < textlen;
1217 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1218 for (jsuint i = k, j = m; ; i--, j--) {
1219 if (text[i] != pat[j])
1220 break;
1221 if (j == 0)
1222 return static_cast<jsint>(i); /* safe: max string size */
1225 return -1;
1228 struct MemCmp {
1229 typedef jsuint Extent;
1230 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) {
1231 return (patlen - 1) * sizeof(jschar);
1233 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1234 return memcmp(p, t, extent) == 0;
1238 struct ManualCmp {
1239 typedef const jschar *Extent;
1240 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) {
1241 return pat + patlen;
1243 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1244 for (; p != extent; ++p, ++t) {
1245 if (*p != *t)
1246 return false;
1248 return true;
1252 template <class InnerMatch>
1253 static jsint
1254 UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
1256 JS_ASSERT(patlen > 0 && textlen > 0);
1257 const jschar *textend = text + textlen - (patlen - 1);
1258 const jschar p0 = *pat;
1259 const jschar *const patNext = pat + 1;
1260 const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
1261 uint8 fixup;
1263 const jschar *t = text;
1264 switch ((textend - t) & 7) {
1265 case 0: if (*t++ == p0) { fixup = 8; goto match; }
1266 case 7: if (*t++ == p0) { fixup = 7; goto match; }
1267 case 6: if (*t++ == p0) { fixup = 6; goto match; }
1268 case 5: if (*t++ == p0) { fixup = 5; goto match; }
1269 case 4: if (*t++ == p0) { fixup = 4; goto match; }
1270 case 3: if (*t++ == p0) { fixup = 3; goto match; }
1271 case 2: if (*t++ == p0) { fixup = 2; goto match; }
1272 case 1: if (*t++ == p0) { fixup = 1; goto match; }
1274 while (t != textend) {
1275 if (t[0] == p0) { t += 1; fixup = 8; goto match; }
1276 if (t[1] == p0) { t += 2; fixup = 7; goto match; }
1277 if (t[2] == p0) { t += 3; fixup = 6; goto match; }
1278 if (t[3] == p0) { t += 4; fixup = 5; goto match; }
1279 if (t[4] == p0) { t += 5; fixup = 4; goto match; }
1280 if (t[5] == p0) { t += 6; fixup = 3; goto match; }
1281 if (t[6] == p0) { t += 7; fixup = 2; goto match; }
1282 if (t[7] == p0) { t += 8; fixup = 1; goto match; }
1283 t += 8;
1284 continue;
1285 do {
1286 if (*t++ == p0) {
1287 match:
1288 if (!InnerMatch::match(patNext, t, extent))
1289 goto failed_match;
1290 return t - text - 1;
1292 failed_match:;
1293 } while (--fixup > 0);
1295 return -1;
1298 static JS_ALWAYS_INLINE jsint
1299 StringMatch(const jschar *text, jsuint textlen,
1300 const jschar *pat, jsuint patlen)
1302 if (patlen == 0)
1303 return 0;
1304 if (textlen < patlen)
1305 return -1;
1307 #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
1309 * Given enough registers, the unrolled loop below is faster than the
1310 * following loop. 32-bit x86 does not have enough registers.
1312 if (patlen == 1) {
1313 const jschar p0 = *pat;
1314 for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1315 if (*c == p0)
1316 return c - text;
1318 return -1;
1320 #endif
1323 * If the text or pattern string is short, BMH will be more expensive than
1324 * the basic linear scan due to initialization cost and a more complex loop
1325 * body. While the correct threshold is input-dependent, we can make a few
1326 * conservative observations:
1327 * - When |textlen| is "big enough", the initialization time will be
1328 * proportionally small, so the worst-case slowdown is minimized.
1329 * - When |patlen| is "too small", even the best case for BMH will be
1330 * slower than a simple scan for large |textlen| due to the more complex
1331 * loop body of BMH.
1332 * From this, the values for "big enough" and "too small" are determined
1333 * empirically. See bug 526348.
1335 if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
1336 jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1337 if (index != sBMHBadPattern)
1338 return index;
1342 * For big patterns with large potential overlap we want the SIMD-optimized
1343 * speed of memcmp. For small patterns, a simple loop is faster.
1345 * FIXME: Linux memcmp performance is sad and the manual loop is faster.
1347 return
1348 #if !defined(__linux__)
1349 patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
1351 #endif
1352 UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
1355 static const size_t sRopeMatchThresholdRatioLog2 = 5;
1357 static jsint
1358 RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen)
1360 JS_ASSERT(textstr->isTopNode());
1362 if (patlen == 0)
1363 return 0;
1364 if (textstr->length() < patlen)
1365 return -1;
1368 * List of leaf nodes in the rope. If we run out of memory when trying to
1369 * append to this list, we can still fall back to StringMatch, so use the
1370 * system allocator so we don't report OOM in that case.
1372 Vector<JSString *, 16, SystemAllocPolicy> strs;
1375 * We don't want to do rope matching if there is a poor node-to-char ratio,
1376 * since this means spending a lot of time in the match loop below. We also
1377 * need to build the list of leaf nodes. Do both here: iterate over the
1378 * nodes so long as there are not too many.
1380 size_t textstrlen = textstr->length();
1381 size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
1382 JSRopeLeafIterator iter(textstr);
1383 for (JSString *str = iter.init(); str; str = iter.next()) {
1384 if (threshold-- == 0 || !strs.append(str))
1385 return StringMatch(textstr->chars(), textstrlen, pat, patlen);
1388 /* Absolute offset from the beginning of the logical string textstr. */
1389 jsint pos = 0;
1391 // TODO: consider branching to a simple loop if patlen == 1
1393 for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
1394 /* First try to match without spanning two nodes. */
1395 const jschar *chars;
1396 size_t len;
1397 (*outerp)->getCharsAndLength(chars, len);
1398 jsint matchResult = StringMatch(chars, len, pat, patlen);
1399 if (matchResult != -1)
1400 return pos + matchResult;
1402 /* Test the overlap. */
1403 JSString **innerp = outerp;
1406 * Start searching at the first place where StringMatch wouldn't have
1407 * found the match.
1409 const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
1410 const jschar *const textend = chars + len;
1411 const jschar p0 = *pat;
1412 const jschar *const p1 = pat + 1;
1413 const jschar *const patend = pat + patlen;
1414 for (const jschar *t = text; t != textend; ) {
1415 if (*t++ != p0)
1416 continue;
1417 const jschar *ttend = textend;
1418 for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
1419 while (tt == ttend) {
1420 if (++innerp == strs.end())
1421 return -1;
1422 (*innerp)->getCharsAndEnd(tt, ttend);
1424 if (*pp != *tt)
1425 goto break_continue;
1428 /* Matched! */
1429 return pos + (t - chars) - 1; /* -1 because of *t++ above */
1431 break_continue:;
1434 pos += len;
1437 return -1;
1440 static JSBool
1441 str_indexOf(JSContext *cx, uintN argc, Value *vp)
1444 JSString *str;
1445 NORMALIZE_THIS(cx, vp, str);
1447 JSString *patstr = ArgToRootedString(cx, argc, vp, 0);
1448 if (!patstr)
1449 return JS_FALSE;
1451 const jschar *text = str->chars();
1452 jsuint textlen = str->length();
1453 const jschar *pat = patstr->chars();
1454 jsuint patlen = patstr->length();
1456 jsuint start;
1457 if (argc > 1) {
1458 if (vp[3].isInt32()) {
1459 jsint i = vp[3].toInt32();
1460 if (i <= 0) {
1461 start = 0;
1462 } else if (jsuint(i) > textlen) {
1463 start = 0;
1464 textlen = 0;
1465 } else {
1466 start = i;
1467 text += start;
1468 textlen -= start;
1470 } else {
1471 jsdouble d;
1472 if (!ValueToNumber(cx, vp[3], &d))
1473 return JS_FALSE;
1474 d = js_DoubleToInteger(d);
1475 if (d <= 0) {
1476 start = 0;
1477 } else if (d > textlen) {
1478 start = 0;
1479 textlen = 0;
1480 } else {
1481 start = (jsint)d;
1482 text += start;
1483 textlen -= start;
1486 } else {
1487 start = 0;
1490 jsint match = StringMatch(text, textlen, pat, patlen);
1491 vp->setInt32((match == -1) ? -1 : start + match);
1492 return true;
1495 static JSBool
1496 str_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
1498 JSString *str, *str2;
1499 const jschar *text, *pat;
1500 jsint i, j, textlen, patlen;
1501 jsdouble d;
1503 NORMALIZE_THIS(cx, vp, str);
1504 text = str->chars();
1505 textlen = (jsint) str->length();
1507 if (argc != 0 && vp[2].isString()) {
1508 str2 = vp[2].toString();
1509 } else {
1510 str2 = ArgToRootedString(cx, argc, vp, 0);
1511 if (!str2)
1512 return JS_FALSE;
1514 pat = str2->chars();
1515 patlen = (jsint) str2->length();
1517 i = textlen - patlen; // Start searching here
1518 if (i < 0) {
1519 vp->setInt32(-1);
1520 return JS_TRUE;
1523 if (argc > 1) {
1524 if (vp[3].isInt32()) {
1525 j = vp[3].toInt32();
1526 if (j <= 0)
1527 i = 0;
1528 else if (j < i)
1529 i = j;
1530 } else {
1531 if (!ValueToNumber(cx, vp[3], &d))
1532 return JS_FALSE;
1533 if (!JSDOUBLE_IS_NaN(d)) {
1534 d = js_DoubleToInteger(d);
1535 if (d <= 0)
1536 i = 0;
1537 else if (d < i)
1538 i = (jsint)d;
1543 if (patlen == 0) {
1544 vp->setInt32(i);
1545 return JS_TRUE;
1548 const jschar *t = text + i;
1549 const jschar *textend = text - 1;
1550 const jschar p0 = *pat;
1551 const jschar *patNext = pat + 1;
1552 const jschar *patEnd = pat + patlen;
1554 for (; t != textend; --t) {
1555 if (*t == p0) {
1556 const jschar *t1 = t + 1;
1557 for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1558 if (*t1 != *p1)
1559 goto break_continue;
1561 vp->setInt32(t - text);
1562 return JS_TRUE;
1564 break_continue:;
1567 vp->setInt32(-1);
1568 return JS_TRUE;
1571 static JSBool
1572 js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
1574 JSString *str;
1575 const jschar *chars;
1576 size_t length, begin, end;
1578 NORMALIZE_THIS(cx, vp, str);
1579 str->getCharsAndLength(chars, length);
1580 begin = 0;
1581 end = length;
1583 if (trimLeft) {
1584 while (begin < length && JS_ISSPACE(chars[begin]))
1585 ++begin;
1588 if (trimRight) {
1589 while (end > begin && JS_ISSPACE(chars[end-1]))
1590 --end;
1593 str = js_NewDependentString(cx, str, begin, end - begin);
1594 if (!str)
1595 return JS_FALSE;
1597 vp->setString(str);
1598 return JS_TRUE;
1601 static JSBool
1602 str_trim(JSContext *cx, uintN argc, Value *vp)
1604 return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
1607 static JSBool
1608 str_trimLeft(JSContext *cx, uintN argc, Value *vp)
1610 return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
1613 static JSBool
1614 str_trimRight(JSContext *cx, uintN argc, Value *vp)
1616 return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
1620 * Perl-inspired string functions.
1623 /* Result of a successfully performed flat match. */
1624 class FlatMatch
1626 JSString *patstr;
1627 const jschar *pat;
1628 size_t patlen;
1629 int32 match_;
1631 friend class RegExpGuard;
1633 public:
1634 FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
1635 JSString *pattern() const { return patstr; }
1636 size_t patternLength() const { return patlen; }
1639 * @note The match is -1 when the match is performed successfully,
1640 * but no match is found.
1642 int32 match() const { return match_; }
1645 /* A regexp and optional associated object. */
1646 class RegExpPair
1648 JSObject *reobj_;
1649 RegExp *re_;
1651 explicit RegExpPair(RegExp *re): re_(re) {}
1652 friend class RegExpGuard;
1654 public:
1655 /* @note May be null. */
1656 JSObject *reobj() const { return reobj_; }
1657 RegExp &re() const { JS_ASSERT(re_); return *re_; }
1661 * RegExpGuard factors logic out of String regexp operations.
1663 * @param optarg Indicates in which argument position RegExp
1664 * flags will be found, if present. This is a Mozilla
1665 * extension and not part of any ECMA spec.
1667 class RegExpGuard
1669 RegExpGuard(const RegExpGuard &);
1670 void operator=(const RegExpGuard &);
1672 JSContext *cx;
1673 RegExpPair rep;
1674 FlatMatch fm;
1677 * Upper bound on the number of characters we are willing to potentially
1678 * waste on searching for RegExp meta-characters.
1680 static const size_t MAX_FLAT_PAT_LEN = 256;
1682 static JSString *flattenPattern(JSContext *cx, JSString *patstr) {
1683 JSCharBuffer cb(cx);
1684 if (!cb.reserve(patstr->length()))
1685 return NULL;
1687 static const jschar ESCAPE_CHAR = '\\';
1688 const jschar *chars = patstr->chars();
1689 size_t len = patstr->length();
1690 for (const jschar *it = chars; it != chars + len; ++it) {
1691 if (RegExp::isMetaChar(*it)) {
1692 if (!cb.append(ESCAPE_CHAR) || !cb.append(*it))
1693 return NULL;
1694 } else {
1695 if (!cb.append(*it))
1696 return NULL;
1699 return js_NewStringFromCharBuffer(cx, cb);
1702 public:
1703 explicit RegExpGuard(JSContext *cx) : cx(cx), rep(NULL) {}
1705 ~RegExpGuard() {
1706 if (rep.re_)
1707 rep.re_->decref(cx);
1710 /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1711 bool
1712 init(uintN argc, Value *vp)
1714 if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
1715 rep.reobj_ = &vp[2].toObject();
1716 rep.re_ = RegExp::extractFrom(rep.reobj_);
1717 rep.re_->incref(cx);
1718 } else {
1719 fm.patstr = ArgToRootedString(cx, argc, vp, 0);
1720 if (!fm.patstr)
1721 return false;
1723 return true;
1727 * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
1728 * pattern string, or a lengthy pattern string can thwart this process.
1730 * @param checkMetaChars Look for regexp metachars in the pattern string.
1731 * @return Whether flat matching could be used.
1733 const FlatMatch *
1734 tryFlatMatch(JSString *textstr, uintN optarg, uintN argc, bool checkMetaChars = true)
1736 if (rep.re_)
1737 return NULL;
1739 fm.patstr->getCharsAndLength(fm.pat, fm.patlen);
1741 if (optarg < argc)
1742 return NULL;
1744 if (checkMetaChars &&
1745 (fm.patlen > MAX_FLAT_PAT_LEN || RegExp::hasMetaChars(fm.pat, fm.patlen))) {
1746 return NULL;
1750 * textstr could be a rope, so we want to avoid flattening it for as
1751 * long as possible.
1753 if (textstr->isTopNode()) {
1754 fm.match_ = RopeMatch(textstr, fm.pat, fm.patlen);
1755 } else {
1756 const jschar *text;
1757 size_t textlen;
1758 textstr->getCharsAndLength(text, textlen);
1759 fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
1761 return &fm;
1764 /* If the pattern is not already a regular expression, make it so. */
1765 const RegExpPair *
1766 normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
1768 /* If we don't have a RegExp, build RegExp from pattern string. */
1769 if (rep.re_)
1770 return &rep;
1772 JSString *opt;
1773 if (optarg < argc) {
1774 opt = js_ValueToString(cx, vp[2 + optarg]);
1775 if (!opt)
1776 return NULL;
1777 } else {
1778 opt = NULL;
1781 JSString *patstr;
1782 if (flat) {
1783 patstr = flattenPattern(cx, fm.patstr);
1784 if (!patstr)
1785 return false;
1786 } else {
1787 patstr = fm.patstr;
1789 JS_ASSERT(patstr);
1791 rep.re_ = RegExp::createFlagged(cx, patstr, opt);
1792 if (!rep.re_)
1793 return NULL;
1794 rep.reobj_ = NULL;
1795 return &rep;
1798 #if DEBUG
1799 bool hasRegExpPair() const { return rep.re_; }
1800 #endif
1803 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1804 static JS_ALWAYS_INLINE bool
1805 Matched(bool test, const Value &v)
1807 return test ? v.isTrue() : !v.isNull();
1810 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
1813 * BitOR-ing these flags allows the DoMatch caller to control when how the
1814 * RegExp engine is called and when callbacks are fired.
1816 enum MatchControlFlags {
1817 TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
1818 TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
1819 CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
1821 MATCH_ARGS = TEST_GLOBAL_BIT,
1822 MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1823 REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1826 /* Factor out looping and matching logic. */
1827 static bool
1828 DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
1829 DoMatchCallback callback, void *data, MatchControlFlags flags)
1831 RegExp &re = rep.re();
1832 if (re.global()) {
1833 /* global matching ('g') */
1834 bool testGlobal = flags & TEST_GLOBAL_BIT;
1835 if (rep.reobj())
1836 rep.reobj()->zeroRegExpLastIndex();
1837 for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1838 if (!re.execute(cx, res, str, &i, testGlobal, vp))
1839 return false;
1840 if (!Matched(testGlobal, *vp))
1841 break;
1842 if (!callback(cx, res, count, data))
1843 return false;
1844 if (!res->matched())
1845 ++i;
1847 } else {
1848 /* single match */
1849 bool testSingle = !!(flags & TEST_SINGLE_BIT),
1850 callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
1851 size_t i = 0;
1852 if (!re.execute(cx, res, str, &i, testSingle, vp))
1853 return false;
1854 if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
1855 return false;
1857 return true;
1860 static bool
1861 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
1863 if (fm.match() < 0) {
1864 vp->setNull();
1865 return true;
1868 /* For this non-global match, produce a RegExp.exec-style array. */
1869 JSObject *obj = js_NewSlowArrayObject(cx);
1870 if (!obj)
1871 return false;
1872 vp->setObject(*obj);
1874 return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) &&
1875 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1876 Int32Value(fm.match())) &&
1877 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1878 StringValue(textstr));
1881 typedef JSObject **MatchArgType;
1884 * DoMatch will only callback on global matches, hence this function builds
1885 * only the "array of matches" returned by match on global regexps.
1887 static bool
1888 MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
1890 JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */
1892 JSObject *&arrayobj = *static_cast<MatchArgType>(p);
1893 if (!arrayobj) {
1894 arrayobj = js_NewArrayObject(cx, 0, NULL);
1895 if (!arrayobj)
1896 return false;
1899 Value v;
1900 if (!res->createLastMatch(cx, &v))
1901 return false;
1903 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1904 return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false);
1907 static JSBool
1908 str_match(JSContext *cx, uintN argc, Value *vp)
1910 JSString *str;
1911 NORMALIZE_THIS(cx, vp, str);
1913 RegExpGuard g(cx);
1914 if (!g.init(argc, vp))
1915 return false;
1916 if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc))
1917 return BuildFlatMatchArray(cx, str, *fm, vp);
1919 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1920 if (!rep)
1921 return false;
1923 AutoObjectRooter array(cx);
1924 MatchArgType arg = array.addr();
1925 RegExpStatics *res = cx->regExpStatics();
1926 if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
1927 return false;
1929 /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
1930 if (rep->re().global())
1931 vp->setObjectOrNull(array.object());
1932 return true;
1935 static JSBool
1936 str_search(JSContext *cx, uintN argc, Value *vp)
1938 JSString *str;
1939 NORMALIZE_THIS(cx, vp, str);
1941 RegExpGuard g(cx);
1942 if (!g.init(argc, vp))
1943 return false;
1944 if (const FlatMatch *fm = g.tryFlatMatch(str, 1, argc)) {
1945 vp->setInt32(fm->match());
1946 return true;
1948 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1949 if (!rep)
1950 return false;
1952 RegExpStatics *res = cx->regExpStatics();
1953 size_t i = 0;
1954 if (!rep->re().execute(cx, res, str, &i, true, vp))
1955 return false;
1957 if (vp->isTrue())
1958 vp->setInt32(res->matchStart());
1959 else
1960 vp->setInt32(-1);
1961 return true;
1964 struct ReplaceData
1966 ReplaceData(JSContext *cx)
1967 : g(cx), cb(cx)
1970 JSString *str; /* 'this' parameter object as a string */
1971 RegExpGuard g; /* regexp parameter object and private data */
1972 JSObject *lambda; /* replacement function object or null */
1973 JSObject *elembase; /* object for function(a){return b[a]} replace */
1974 JSString *repstr; /* replacement string */
1975 jschar *dollar; /* null or pointer to first $ in repstr */
1976 jschar *dollarEnd; /* limit pointer for js_strchr_limit */
1977 jsint index; /* index in result of next replacement */
1978 jsint leftIndex; /* left context index in str->chars */
1979 JSSubString dollarStr; /* for "$$" InterpretDollar result */
1980 bool calledBack; /* record whether callback has been called */
1981 InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
1982 InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
1983 JSCharBuffer cb; /* buffer built during DoMatch */
1986 static bool
1987 InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, ReplaceData &rdata,
1988 JSSubString *out, size_t *skip)
1990 JS_ASSERT(*dp == '$');
1992 /* If there is only a dollar, bail now */
1993 if (dp + 1 >= ep)
1994 return false;
1996 /* Interpret all Perl match-induced dollar variables. */
1997 jschar dc = dp[1];
1998 if (JS7_ISDEC(dc)) {
1999 /* ECMA-262 Edition 3: 1-9 or 01-99 */
2000 uintN num = JS7_UNDEC(dc);
2001 if (num > res->getParenCount())
2002 return false;
2004 jschar *cp = dp + 2;
2005 if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
2006 uintN tmp = 10 * num + JS7_UNDEC(dc);
2007 if (tmp <= res->getParenCount()) {
2008 cp++;
2009 num = tmp;
2012 if (num == 0)
2013 return false;
2015 /* Adjust num from 1 $n-origin to 0 array-index-origin. */
2016 num--;
2017 *skip = cp - dp;
2018 if (num < res->getParenCount())
2019 res->getParen(num, out);
2020 else
2021 *out = js_EmptySubString;
2022 return true;
2025 *skip = 2;
2026 switch (dc) {
2027 case '$':
2028 rdata.dollarStr.chars = dp;
2029 rdata.dollarStr.length = 1;
2030 *out = rdata.dollarStr;
2031 return true;
2032 case '&':
2033 res->getLastMatch(out);
2034 return true;
2035 case '+':
2036 res->getLastParen(out);
2037 return true;
2038 case '`':
2039 res->getLeftContext(out);
2040 return true;
2041 case '\'':
2042 res->getRightContext(out);
2043 return true;
2045 return false;
2048 static bool
2049 FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
2051 JSObject *base = rdata.elembase;
2052 if (base) {
2054 * The base object is used when replace was passed a lambda which looks like
2055 * 'function(a) { return b[a]; }' for the base object b. b will not change
2056 * in the course of the replace unless we end up making a scripted call due
2057 * to accessing a scripted getter or a value with a scripted toString.
2059 JS_ASSERT(rdata.lambda);
2060 JS_ASSERT(!base->getOps()->lookupProperty);
2061 JS_ASSERT(!base->getOps()->getProperty);
2063 Value match;
2064 if (!res->createLastMatch(cx, &match))
2065 return false;
2066 JSString *str = match.toString();
2068 JSAtom *atom;
2069 if (str->isAtomized()) {
2070 atom = STRING_TO_ATOM(str);
2071 } else {
2072 atom = js_AtomizeString(cx, str, 0);
2073 if (!atom)
2074 return false;
2076 jsid id = ATOM_TO_JSID(atom);
2078 JSObject *holder;
2079 JSProperty *prop = NULL;
2080 if (js_LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop) < 0)
2081 return false;
2083 /* Only handle the case where the property exists and is on this object. */
2084 if (prop && holder == base) {
2085 Shape *shape = (Shape *) prop;
2086 if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) {
2087 Value value = base->getSlot(shape->slot);
2088 if (value.isString()) {
2089 rdata.repstr = value.toString();
2090 *sizep = rdata.repstr->length();
2091 return true;
2097 * Couldn't handle this property, fall through and despecialize to the
2098 * general lambda case.
2100 rdata.elembase = NULL;
2103 JSObject *lambda = rdata.lambda;
2104 if (lambda) {
2106 * In the lambda case, not only do we find the replacement string's
2107 * length, we compute repstr and return it via rdata for use within
2108 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
2109 * index, input), i.e., all the properties of a regexp match array.
2110 * For $&, etc., we must create string jsvals from cx->regExpStatics.
2111 * We grab up stack space to keep the newborn strings GC-rooted.
2113 uintN p = res->getParenCount();
2114 uintN argc = 1 + p + 2;
2116 InvokeSessionGuard &session = rdata.session;
2117 if (!session.started()) {
2118 Value lambdav = ObjectValue(*lambda);
2119 if (!session.start(cx, lambdav, UndefinedValue(), argc))
2120 return false;
2123 PreserveRegExpStatics staticsGuard(res);
2124 if (!staticsGuard.init(cx))
2125 return false;
2127 /* Push $&, $1, $2, ... */
2128 uintN argi = 0;
2129 if (!res->createLastMatch(cx, &session[argi++]))
2130 return false;
2132 for (size_t i = 0; i < res->getParenCount(); ++i) {
2133 if (!res->createParen(cx, i, &session[argi++]))
2134 return false;
2137 /* Push match index and input string. */
2138 session[argi++].setInt32(res->matchStart());
2139 session[argi].setString(rdata.str);
2141 if (!session.invoke(cx))
2142 return false;
2144 /* root repstr: rdata is on the stack, so scanned by conservative gc. */
2145 rdata.repstr = ValueToString_TestForStringInline(cx, session.rval());
2146 if (!rdata.repstr)
2147 return false;
2149 *sizep = rdata.repstr->length();
2150 return true;
2153 JSString *repstr = rdata.repstr;
2154 size_t replen = repstr->length();
2155 for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
2156 JSSubString sub;
2157 size_t skip;
2158 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2159 replen += sub.length - skip;
2160 dp += skip;
2161 } else {
2162 dp++;
2165 *sizep = replen;
2166 return true;
2169 static void
2170 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars)
2172 JSString *repstr = rdata.repstr;
2173 jschar *cp;
2174 jschar *bp = cp = repstr->chars();
2175 for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) {
2176 size_t len = dp - cp;
2177 js_strncpy(chars, cp, len);
2178 chars += len;
2179 cp = dp;
2181 JSSubString sub;
2182 size_t skip;
2183 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2184 len = sub.length;
2185 js_strncpy(chars, sub.chars, len);
2186 chars += len;
2187 cp += skip;
2188 dp += skip;
2189 } else {
2190 dp++;
2193 js_strncpy(chars, cp, repstr->length() - (cp - bp));
2196 static bool
2197 ReplaceCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
2199 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
2201 rdata.calledBack = true;
2202 JSString *str = rdata.str;
2203 size_t leftoff = rdata.leftIndex;
2204 const jschar *left = str->chars() + leftoff;
2205 size_t leftlen = res->matchStart() - leftoff;
2206 rdata.leftIndex = res->matchLimit();
2208 size_t replen = 0; /* silence 'unused' warning */
2209 if (!FindReplaceLength(cx, res, rdata, &replen))
2210 return false;
2212 size_t growth = leftlen + replen;
2213 if (!rdata.cb.growByUninitialized(growth))
2214 return false;
2216 jschar *chars = rdata.cb.begin() + rdata.index;
2217 rdata.index += growth;
2218 js_strncpy(chars, left, leftlen);
2219 chars += leftlen;
2220 DoReplace(cx, res, rdata, chars);
2221 return true;
2224 static bool
2225 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2226 const FlatMatch &fm, Value *vp)
2228 JSRopeBuilder builder(cx);
2229 size_t match = fm.match(); /* Avoid signed/unsigned warnings. */
2230 size_t matchEnd = match + fm.patternLength();
2232 if (textstr->isTopNode()) {
2234 * If we are replacing over a rope, avoid flattening it by iterating
2235 * through it, building a new rope.
2237 JSRopeLeafIterator iter(textstr);
2238 size_t pos = 0;
2239 for (JSString *str = iter.init(); str; str = iter.next()) {
2240 size_t len = str->length();
2241 size_t strEnd = pos + len;
2242 if (pos < matchEnd && strEnd > match) {
2244 * We need to special-case any part of the rope that overlaps
2245 * with the replacement string.
2247 if (match >= pos) {
2249 * If this part of the rope overlaps with the left side of
2250 * the pattern, then it must be the only one to overlap with
2251 * the first character in the pattern, so we include the
2252 * replacement string here.
2254 JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
2255 if (!leftSide ||
2256 !builder.append(leftSide) ||
2257 !builder.append(repstr)) {
2258 return false;
2263 * If str runs off the end of the matched string, append the
2264 * last part of str.
2266 if (strEnd > matchEnd) {
2267 JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
2268 strEnd - matchEnd);
2269 if (!rightSide || !builder.append(rightSide))
2270 return false;
2272 } else {
2273 if (!builder.append(str))
2274 return false;
2276 pos += str->length();
2278 } else {
2279 JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
2280 if (!leftSide)
2281 return false;
2282 JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
2283 textstr->length() - match - fm.patternLength());
2284 if (!rightSide ||
2285 !builder.append(leftSide) ||
2286 !builder.append(repstr) ||
2287 !builder.append(rightSide)) {
2288 return false;
2292 vp->setString(builder.getStr());
2293 return true;
2297 * Perform a linear-scan dollar substitution on the replacement text,
2298 * constructing a result string that looks like:
2300 * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
2302 static inline bool
2303 BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2304 const jschar *firstDollar, const FlatMatch &fm, Value *vp)
2306 JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
2307 size_t matchStart = fm.match();
2308 size_t matchLimit = matchStart + fm.patternLength();
2309 JSCharBuffer newReplaceChars(cx);
2312 * Most probably:
2314 * len(newstr) >= len(orig) - len(match) + len(replacement)
2316 * Note that dollar vars _could_ make the resulting text smaller than this.
2318 if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
2319 return false;
2321 /* Move the pre-dollar chunk in bulk. */
2322 JS_ALWAYS_TRUE(newReplaceChars.append(repstr->chars(), firstDollar));
2324 /* Move the rest char-by-char, interpreting dollars as we encounter them. */
2325 #define ENSURE(__cond) if (!(__cond)) return false;
2326 const jschar *repstrLimit = repstr->chars() + repstr->length();
2327 for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
2328 if (*it != '$' || it == repstrLimit - 1) {
2329 ENSURE(newReplaceChars.append(*it));
2330 continue;
2333 switch (*(it + 1)) {
2334 case '$': /* Eat one of the dollars. */
2335 ENSURE(newReplaceChars.append(*it));
2336 break;
2337 case '&':
2338 ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
2339 textstr->chars() + matchLimit));
2340 break;
2341 case '`':
2342 ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
2343 break;
2344 case '\'':
2345 ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
2346 textstr->chars() + textstr->length()));
2347 break;
2348 default: /* The dollar we saw was not special (no matter what its mother told it). */
2349 ENSURE(newReplaceChars.append(*it));
2350 continue;
2352 ++it; /* We always eat an extra char in the above switch. */
2355 JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
2356 ENSURE(leftSide);
2358 JSString *newReplace = js_NewStringFromCharBuffer(cx, newReplaceChars);
2359 ENSURE(newReplace);
2361 JS_ASSERT(textstr->length() >= matchLimit);
2362 JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
2363 textstr->length() - matchLimit);
2364 ENSURE(rightSide);
2366 JSRopeBuilder builder(cx);
2367 ENSURE(builder.append(leftSide) &&
2368 builder.append(newReplace) &&
2369 builder.append(rightSide));
2370 #undef ENSURE
2372 vp->setString(builder.getStr());
2373 return true;
2376 static inline bool
2377 str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
2379 const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
2380 if (!rep)
2381 return false;
2383 rdata.index = 0;
2384 rdata.leftIndex = 0;
2385 rdata.calledBack = false;
2387 RegExpStatics *res = cx->regExpStatics();
2388 if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS))
2389 return false;
2391 if (!rdata.calledBack) {
2392 /* Didn't match, so the string is unmodified. */
2393 vp->setString(rdata.str);
2394 return true;
2397 JSSubString sub;
2398 res->getRightContext(&sub);
2399 if (!rdata.cb.append(sub.chars, sub.length))
2400 return false;
2402 JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
2403 if (!retstr)
2404 return false;
2406 vp->setString(retstr);
2407 return true;
2410 static inline bool
2411 str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata,
2412 const FlatMatch &fm)
2414 JS_ASSERT(fm.match() >= 0);
2415 LeaveTrace(cx);
2417 JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
2418 if (!matchStr)
2419 return false;
2421 /* lambda(matchStr, matchStart, textstr) */
2422 static const uint32 lambdaArgc = 3;
2423 if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
2424 return false;
2426 CallArgs &args = rdata.singleShot;
2427 args.callee().setObject(*rdata.lambda);
2428 args.thisv().setUndefined();
2430 Value *sp = args.argv();
2431 sp[0].setString(matchStr);
2432 sp[1].setInt32(fm.match());
2433 sp[2].setString(rdata.str);
2435 if (!Invoke(cx, rdata.singleShot, 0))
2436 return false;
2438 JSString *repstr = js_ValueToString(cx, args.rval());
2439 if (!repstr)
2440 return false;
2442 JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
2443 if (!leftSide)
2444 return false;
2446 size_t matchLimit = fm.match() + fm.patternLength();
2447 JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
2448 rdata.str->length() - matchLimit);
2449 if (!rightSide)
2450 return false;
2452 JSRopeBuilder builder(cx);
2453 if (!(builder.append(leftSide) &&
2454 builder.append(repstr) &&
2455 builder.append(rightSide))) {
2456 return false;
2459 vp->setString(builder.getStr());
2460 return true;
2463 JSBool
2464 js::str_replace(JSContext *cx, uintN argc, Value *vp)
2466 ReplaceData rdata(cx);
2467 NORMALIZE_THIS(cx, vp, rdata.str);
2468 static const uint32 optarg = 2;
2470 /* Extract replacement string/function. */
2471 if (argc >= optarg && js_IsCallable(vp[3])) {
2472 rdata.lambda = &vp[3].toObject();
2473 rdata.elembase = NULL;
2474 rdata.repstr = NULL;
2475 rdata.dollar = rdata.dollarEnd = NULL;
2477 if (rdata.lambda->isFunction()) {
2478 JSFunction *fun = rdata.lambda->getFunctionPrivate();
2479 if (fun->isInterpreted()) {
2481 * Pattern match the script to check if it is is indexing into a
2482 * particular object, e.g. 'function(a) { return b[a]; }'. Avoid
2483 * calling the script in such cases, which are used by javascript
2484 * packers (particularly the popular Dean Edwards packer) to efficiently
2485 * encode large scripts. We only handle the code patterns generated
2486 * by such packers here.
2488 JSScript *script = fun->u.i.script;
2489 jsbytecode *pc = script->code;
2491 Value table = UndefinedValue();
2492 if (JSOp(*pc) == JSOP_GETFCSLOT) {
2493 table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc));
2494 pc += JSOP_GETFCSLOT_LENGTH;
2497 if (table.isObject() &&
2498 JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
2499 JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM &&
2500 JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) {
2501 Class *clasp = table.toObject().getClass();
2502 if (clasp->isNative() &&
2503 !clasp->ops.lookupProperty &&
2504 !clasp->ops.getProperty) {
2505 rdata.elembase = &table.toObject();
2510 } else {
2511 rdata.lambda = NULL;
2512 rdata.elembase = NULL;
2513 rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
2514 if (!rdata.repstr)
2515 return false;
2517 /* We're about to store pointers into the middle of our string. */
2518 if (!js_MakeStringImmutable(cx, rdata.repstr))
2519 return false;
2520 rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
2521 rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
2522 rdata.dollarEnd);
2525 if (!rdata.g.init(argc, vp))
2526 return false;
2529 * Unlike its |String.prototype| brethren, |replace| doesn't convert
2530 * its input to a regular expression. (Even if it contains metachars.)
2532 * However, if the user invokes our (non-standard) |flags| argument
2533 * extension then we revert to creating a regular expression. Note that
2534 * this is observable behavior through the side-effect mutation of the
2535 * |RegExp| statics.
2538 const FlatMatch *fm = rdata.g.tryFlatMatch(rdata.str, optarg, argc, false);
2539 if (!fm) {
2540 JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
2541 return str_replace_regexp(cx, argc, vp, rdata);
2544 if (fm->match() < 0) {
2545 vp->setString(rdata.str);
2546 return true;
2549 if (rdata.lambda)
2550 return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
2553 * Note: we could optimize the text.length == pattern.length case if we wanted,
2554 * even in the presence of dollar metachars.
2556 if (rdata.dollar)
2557 return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
2559 return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
2563 * Subroutine used by str_split to find the next split point in str, starting
2564 * at offset *ip and looking either for the separator substring given by sep, or
2565 * for the next re match. In the re case, return the matched separator in *sep,
2566 * and the possibly updated offset in *ip.
2568 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
2569 * separator occurrence if found, or str->length if no separator is found.
2571 static jsint
2572 find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
2573 JSSubString *sep)
2575 jsint i;
2576 size_t length;
2577 jschar *chars;
2580 * Stop if past end of string. If at end of string, we will compare the
2581 * null char stored there (by js_NewString*) to sep->chars[j] in the while
2582 * loop at the end of this function, so that
2584 * "ab,".split(',') => ["ab", ""]
2586 * and the resulting array converts back to the string "ab," for symmetry.
2587 * However, we ape Perl and do this only if there is a sufficiently large
2588 * limit argument (see str_split).
2590 i = *ip;
2591 length = str->length();
2592 if ((size_t)i > length)
2593 return -1;
2595 chars = str->chars();
2598 * Match a regular expression against the separator at or above index i.
2599 * Call js_ExecuteRegExp with true for the test argument. On successful
2600 * match, get the separator from cx->regExpStatics.lastMatch.
2602 if (re) {
2603 size_t index;
2604 Value rval;
2606 again:
2607 /* JS1.2 deviated from Perl by never matching at end of string. */
2608 index = (size_t)i;
2609 if (!re->execute(cx, res, str, &index, true, &rval))
2610 return -2;
2611 if (!rval.isTrue()) {
2612 /* Mismatch: ensure our caller advances i past end of string. */
2613 sep->length = 1;
2614 return length;
2616 i = (jsint)index;
2617 JS_ASSERT(sep);
2618 res->getLastMatch(sep);
2619 if (sep->length == 0) {
2621 * Empty string match: never split on an empty match at the start
2622 * of a find_split cycle. Same rule as for an empty global match
2623 * in DoMatch.
2625 if (i == *ip) {
2627 * "Bump-along" to avoid sticking at an empty match, but don't
2628 * bump past end of string -- our caller must do that by adding
2629 * sep->length to our return value.
2631 if ((size_t)i == length)
2632 return -1;
2633 i++;
2634 goto again;
2636 if ((size_t)i == length) {
2638 * If there was a trivial zero-length match at the end of the
2639 * split, then we shouldn't output the matched string at the end
2640 * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
2642 sep->chars = NULL;
2645 JS_ASSERT((size_t)i >= sep->length);
2646 return i - sep->length;
2650 * Special case: if sep is the empty string, split str into one character
2651 * substrings. Let our caller worry about whether to split once at end of
2652 * string into an empty substring.
2654 if (sep->length == 0)
2655 return ((size_t)i == length) ? -1 : i + 1;
2658 * Now that we know sep is non-empty, search starting at i in str for an
2659 * occurrence of all of sep's chars. If we find them, return the index of
2660 * the first separator char. Otherwise, return length.
2662 jsint match = StringMatch(chars + i, length - i, sep->chars, sep->length);
2663 return match == -1 ? length : match + i;
2666 static JSBool
2667 str_split(JSContext *cx, uintN argc, Value *vp)
2669 JSString *str;
2670 NORMALIZE_THIS(cx, vp, str);
2672 if (argc == 0) {
2673 Value v = StringValue(str);
2674 JSObject *aobj = js_NewArrayObject(cx, 1, &v);
2675 if (!aobj)
2676 return false;
2677 vp->setObject(*aobj);
2678 return true;
2681 RegExp *re;
2682 JSSubString *sep, tmp;
2683 if (VALUE_IS_REGEXP(cx, vp[2])) {
2684 re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
2685 sep = &tmp;
2687 /* Set a magic value so we can detect a successful re match. */
2688 sep->chars = NULL;
2689 sep->length = 0;
2690 } else {
2691 JSString *str2 = js_ValueToString(cx, vp[2]);
2692 if (!str2)
2693 return false;
2694 vp[2].setString(str2);
2697 * Point sep at a local copy of str2's header because find_split
2698 * will modify sep->length.
2700 str2->getCharsAndLength(tmp.chars, tmp.length);
2701 sep = &tmp;
2702 re = NULL;
2705 /* Use the second argument as the split limit, if given. */
2706 uint32 limit = 0; /* Avoid warning. */
2707 bool limited = (argc > 1) && !vp[3].isUndefined();
2708 if (limited) {
2709 jsdouble d;
2710 if (!ValueToNumber(cx, vp[3], &d))
2711 return false;
2713 /* Clamp limit between 0 and 1 + string length. */
2714 limit = js_DoubleToECMAUint32(d);
2715 if (limit > str->length())
2716 limit = 1 + str->length();
2719 AutoValueVector splits(cx);
2721 RegExpStatics *res = cx->regExpStatics();
2722 jsint i, j;
2723 uint32 len = i = 0;
2724 while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
2725 if (limited && len >= limit)
2726 break;
2728 JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
2729 if (!sub || !splits.append(StringValue(sub)))
2730 return false;
2731 len++;
2734 * Imitate perl's feature of including parenthesized substrings that
2735 * matched part of the delimiter in the new array, after the split
2736 * substring that was delimited.
2738 if (re && sep->chars) {
2739 for (uintN num = 0; num < res->getParenCount(); num++) {
2740 if (limited && len >= limit)
2741 break;
2742 JSSubString parsub;
2743 res->getParen(num, &parsub);
2744 sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
2745 if (!sub || !splits.append(StringValue(sub)))
2746 return false;
2747 len++;
2749 sep->chars = NULL;
2751 i = j + sep->length;
2754 if (j == -2)
2755 return false;
2757 JSObject *aobj = js_NewArrayObject(cx, splits.length(), splits.begin());
2758 if (!aobj)
2759 return false;
2760 vp->setObject(*aobj);
2761 return true;
2764 #if JS_HAS_PERL_SUBSTR
2765 static JSBool
2766 str_substr(JSContext *cx, uintN argc, Value *vp)
2768 JSString *str;
2769 jsdouble d;
2770 jsdouble length, begin, end;
2772 NORMALIZE_THIS(cx, vp, str);
2773 if (argc != 0) {
2774 if (!ValueToNumber(cx, vp[2], &d))
2775 return JS_FALSE;
2776 length = str->length();
2777 begin = js_DoubleToInteger(d);
2778 if (begin < 0) {
2779 begin += length;
2780 if (begin < 0)
2781 begin = 0;
2782 } else if (begin > length) {
2783 begin = length;
2786 if (argc == 1 || vp[3].isUndefined()) {
2787 end = length;
2788 } else {
2789 if (!ValueToNumber(cx, vp[3], &d))
2790 return JS_FALSE;
2791 end = js_DoubleToInteger(d);
2792 if (end < 0)
2793 end = 0;
2794 end += begin;
2795 if (end > length)
2796 end = length;
2799 str = js_NewDependentString(cx, str,
2800 (size_t)begin,
2801 (size_t)(end - begin));
2802 if (!str)
2803 return JS_FALSE;
2805 vp->setString(str);
2806 return JS_TRUE;
2808 #endif /* JS_HAS_PERL_SUBSTR */
2811 * Python-esque sequence operations.
2813 static JSBool
2814 str_concat(JSContext *cx, uintN argc, Value *vp)
2816 JSString *str, *str2;
2817 Value *argv;
2818 uintN i;
2820 NORMALIZE_THIS(cx, vp, str);
2822 /* Set vp (aka rval) early to handle the argc == 0 case. */
2823 vp->setString(str);
2825 for (i = 0, argv = vp + 2; i < argc; i++) {
2826 str2 = js_ValueToString(cx, argv[i]);
2827 if (!str2)
2828 return JS_FALSE;
2829 argv[i].setString(str2);
2831 str = js_ConcatStrings(cx, str, str2);
2832 if (!str)
2833 return JS_FALSE;
2834 vp->setString(str);
2837 return JS_TRUE;
2840 static JSBool
2841 str_slice(JSContext *cx, uintN argc, Value *vp)
2843 if (argc == 1 && vp[1].isString() && vp[2].isInt32()) {
2844 size_t begin, end, length;
2846 JSString *str = vp[1].toString();
2847 begin = vp[2].toInt32();
2848 end = str->length();
2849 if (begin <= end) {
2850 length = end - begin;
2851 if (length == 0) {
2852 str = cx->runtime->emptyString;
2853 } else {
2854 str = (length == 1)
2855 ? JSString::getUnitString(cx, str, begin)
2856 : js_NewDependentString(cx, str, begin, length);
2857 if (!str)
2858 return JS_FALSE;
2860 vp->setString(str);
2861 return JS_TRUE;
2865 JSString *str;
2866 NORMALIZE_THIS(cx, vp, str);
2868 if (argc != 0) {
2869 double begin, end, length;
2871 if (!ValueToNumber(cx, vp[2], &begin))
2872 return JS_FALSE;
2873 begin = js_DoubleToInteger(begin);
2874 length = str->length();
2875 if (begin < 0) {
2876 begin += length;
2877 if (begin < 0)
2878 begin = 0;
2879 } else if (begin > length) {
2880 begin = length;
2883 if (argc == 1 || vp[3].isUndefined()) {
2884 end = length;
2885 } else {
2886 if (!ValueToNumber(cx, vp[3], &end))
2887 return JS_FALSE;
2888 end = js_DoubleToInteger(end);
2889 if (end < 0) {
2890 end += length;
2891 if (end < 0)
2892 end = 0;
2893 } else if (end > length) {
2894 end = length;
2896 if (end < begin)
2897 end = begin;
2900 str = js_NewDependentString(cx, str,
2901 (size_t)begin,
2902 (size_t)(end - begin));
2903 if (!str)
2904 return JS_FALSE;
2906 vp->setString(str);
2907 return JS_TRUE;
2910 #if JS_HAS_STR_HTML_HELPERS
2912 * HTML composition aids.
2914 static JSBool
2915 tagify(JSContext *cx, const char *begin, JSString *param, const char *end,
2916 Value *vp)
2918 JSString *str;
2919 jschar *tagbuf;
2920 size_t beglen, endlen, parlen, taglen;
2921 size_t i, j;
2923 NORMALIZE_THIS(cx, vp, str);
2925 if (!end)
2926 end = begin;
2928 beglen = strlen(begin);
2929 taglen = 1 + beglen + 1; /* '<begin' + '>' */
2930 parlen = 0; /* Avoid warning. */
2931 if (param) {
2932 parlen = param->length();
2933 taglen += 2 + parlen + 1; /* '="param"' */
2935 endlen = strlen(end);
2936 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2938 if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2939 js_ReportAllocationOverflow(cx);
2940 return JS_FALSE;
2943 tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2944 if (!tagbuf)
2945 return JS_FALSE;
2947 j = 0;
2948 tagbuf[j++] = '<';
2949 for (i = 0; i < beglen; i++)
2950 tagbuf[j++] = (jschar)begin[i];
2951 if (param) {
2952 tagbuf[j++] = '=';
2953 tagbuf[j++] = '"';
2954 js_strncpy(&tagbuf[j], param->chars(), parlen);
2955 j += parlen;
2956 tagbuf[j++] = '"';
2958 tagbuf[j++] = '>';
2959 js_strncpy(&tagbuf[j], str->chars(), str->length());
2960 j += str->length();
2961 tagbuf[j++] = '<';
2962 tagbuf[j++] = '/';
2963 for (i = 0; i < endlen; i++)
2964 tagbuf[j++] = (jschar)end[i];
2965 tagbuf[j++] = '>';
2966 JS_ASSERT(j == taglen);
2967 tagbuf[j] = 0;
2969 str = js_NewString(cx, tagbuf, taglen);
2970 if (!str) {
2971 js_free((char *)tagbuf);
2972 return JS_FALSE;
2974 vp->setString(str);
2975 return JS_TRUE;
2978 static JSBool
2979 tagify_value(JSContext *cx, uintN argc, Value *vp,
2980 const char *begin, const char *end)
2982 JSString *param;
2984 param = ArgToRootedString(cx, argc, vp, 0);
2985 if (!param)
2986 return JS_FALSE;
2987 return tagify(cx, begin, param, end, vp);
2990 static JSBool
2991 str_bold(JSContext *cx, uintN argc, Value *vp)
2993 return tagify(cx, "b", NULL, NULL, vp);
2996 static JSBool
2997 str_italics(JSContext *cx, uintN argc, Value *vp)
2999 return tagify(cx, "i", NULL, NULL, vp);
3002 static JSBool
3003 str_fixed(JSContext *cx, uintN argc, Value *vp)
3005 return tagify(cx, "tt", NULL, NULL, vp);
3008 static JSBool
3009 str_fontsize(JSContext *cx, uintN argc, Value *vp)
3011 return tagify_value(cx, argc, vp, "font size", "font");
3014 static JSBool
3015 str_fontcolor(JSContext *cx, uintN argc, Value *vp)
3017 return tagify_value(cx, argc, vp, "font color", "font");
3020 static JSBool
3021 str_link(JSContext *cx, uintN argc, Value *vp)
3023 return tagify_value(cx, argc, vp, "a href", "a");
3026 static JSBool
3027 str_anchor(JSContext *cx, uintN argc, Value *vp)
3029 return tagify_value(cx, argc, vp, "a name", "a");
3032 static JSBool
3033 str_strike(JSContext *cx, uintN argc, Value *vp)
3035 return tagify(cx, "strike", NULL, NULL, vp);
3038 static JSBool
3039 str_small(JSContext *cx, uintN argc, Value *vp)
3041 return tagify(cx, "small", NULL, NULL, vp);
3044 static JSBool
3045 str_big(JSContext *cx, uintN argc, Value *vp)
3047 return tagify(cx, "big", NULL, NULL, vp);
3050 static JSBool
3051 str_blink(JSContext *cx, uintN argc, Value *vp)
3053 return tagify(cx, "blink", NULL, NULL, vp);
3056 static JSBool
3057 str_sup(JSContext *cx, uintN argc, Value *vp)
3059 return tagify(cx, "sup", NULL, NULL, vp);
3062 static JSBool
3063 str_sub(JSContext *cx, uintN argc, Value *vp)
3065 return tagify(cx, "sub", NULL, NULL, vp);
3067 #endif /* JS_HAS_STR_HTML_HELPERS */
3069 #ifdef JS_TRACER
3070 JSString* FASTCALL
3071 js_String_getelem(JSContext* cx, JSString* str, int32 i)
3073 if ((size_t)i >= str->length())
3074 return NULL;
3075 return JSString::getUnitString(cx, str, size_t(i));
3077 #endif
3079 JS_DEFINE_TRCINFO_1(str_concat,
3080 (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING,
3081 1, nanojit::ACCSET_NONE)))
3083 static const uint16 GENERIC_PRIMITIVE = JSFUN_GENERIC_NATIVE | JSFUN_PRIMITIVE_THIS;
3085 static JSFunctionSpec string_methods[] = {
3086 #if JS_HAS_TOSOURCE
3087 JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE),
3088 JS_FN(js_toSource_str, str_toSource, 0,JSFUN_PRIMITIVE_THIS),
3089 #endif
3091 /* Java-like methods. */
3092 JS_FN(js_toString_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3093 JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3094 JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS),
3095 JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE),
3096 JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE),
3097 JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE),
3098 JS_FN("charAt", js_str_charAt, 1,GENERIC_PRIMITIVE),
3099 JS_FN("charCodeAt", js_str_charCodeAt, 1,GENERIC_PRIMITIVE),
3100 JS_FN("indexOf", str_indexOf, 1,GENERIC_PRIMITIVE),
3101 JS_FN("lastIndexOf", str_lastIndexOf, 1,GENERIC_PRIMITIVE),
3102 JS_FN("trim", str_trim, 0,GENERIC_PRIMITIVE),
3103 JS_FN("trimLeft", str_trimLeft, 0,GENERIC_PRIMITIVE),
3104 JS_FN("trimRight", str_trimRight, 0,GENERIC_PRIMITIVE),
3105 JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE),
3106 JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE),
3107 JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE),
3109 /* Perl-ish methods (search is actually Python-esque). */
3110 JS_FN("match", str_match, 1,GENERIC_PRIMITIVE),
3111 JS_FN("search", str_search, 1,GENERIC_PRIMITIVE),
3112 JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE),
3113 JS_FN("split", str_split, 2,GENERIC_PRIMITIVE),
3114 #if JS_HAS_PERL_SUBSTR
3115 JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE),
3116 #endif
3118 /* Python-esque sequence methods. */
3119 JS_TN("concat", str_concat, 1,GENERIC_PRIMITIVE, &str_concat_trcinfo),
3120 JS_FN("slice", str_slice, 2,GENERIC_PRIMITIVE),
3122 /* HTML string methods. */
3123 #if JS_HAS_STR_HTML_HELPERS
3124 JS_FN("bold", str_bold, 0,JSFUN_PRIMITIVE_THIS),
3125 JS_FN("italics", str_italics, 0,JSFUN_PRIMITIVE_THIS),
3126 JS_FN("fixed", str_fixed, 0,JSFUN_PRIMITIVE_THIS),
3127 JS_FN("fontsize", str_fontsize, 1,JSFUN_PRIMITIVE_THIS),
3128 JS_FN("fontcolor", str_fontcolor, 1,JSFUN_PRIMITIVE_THIS),
3129 JS_FN("link", str_link, 1,JSFUN_PRIMITIVE_THIS),
3130 JS_FN("anchor", str_anchor, 1,JSFUN_PRIMITIVE_THIS),
3131 JS_FN("strike", str_strike, 0,JSFUN_PRIMITIVE_THIS),
3132 JS_FN("small", str_small, 0,JSFUN_PRIMITIVE_THIS),
3133 JS_FN("big", str_big, 0,JSFUN_PRIMITIVE_THIS),
3134 JS_FN("blink", str_blink, 0,JSFUN_PRIMITIVE_THIS),
3135 JS_FN("sup", str_sup, 0,JSFUN_PRIMITIVE_THIS),
3136 JS_FN("sub", str_sub, 0,JSFUN_PRIMITIVE_THIS),
3137 #endif
3139 JS_FS_END
3143 * Set up some tools to make it easier to generate large tables. After constant
3144 * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
3145 * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
3146 * To use this, define R appropriately, then use Rn(0) (for some value of n), then
3147 * undefine R.
3149 #define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0))
3150 #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
3151 #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
3152 #define R8(n) R6(n), R6((n) + (1 << 6)), R6((n) + (2 << 6)), R6((n) + (3 << 6))
3153 #define R10(n) R8(n), R8((n) + (1 << 8)), R8((n) + (2 << 8)), R8((n) + (3 << 8))
3154 #define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10))
3156 #define R3(n) R2(n), R2((n) + (1 << 2))
3157 #define R7(n) R6(n), R6((n) + (1 << 6))
3160 * Declare unit strings. Pack the string data itself into the mInlineChars
3161 * place in the header.
3163 #define R(c) { \
3164 JSString::FLAT | JSString::ATOMIZED | (1 << JSString::FLAGS_LENGTH_SHIFT),\
3165 { (jschar *)(((char *)(unitStringTable + (c))) + \
3166 offsetof(JSString, mInlineStorage)) }, \
3167 { {(c), 0x00} } }
3169 #ifdef __SUNPRO_CC
3170 #pragma pack(8)
3171 #else
3172 #pragma pack(push, 8)
3173 #endif
3175 const JSString JSString::unitStringTable[]
3176 #ifdef __GNUC__
3177 __attribute__ ((aligned (8)))
3178 #endif
3179 = { R8(0) };
3181 #ifdef __SUNPRO_CC
3182 #pragma pack(0)
3183 #else
3184 #pragma pack(pop)
3185 #endif
3187 #undef R
3190 * Declare length-2 strings. We only store strings where both characters are
3191 * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
3192 * the lowercase letters, and the next 26 are the uppercase letters.
3194 #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
3195 (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \
3196 (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \
3197 JSString::INVALID_SMALL_CHAR)
3199 #define R TO_SMALL_CHAR
3201 const JSString::SmallChar JSString::toSmallChar[] = { R7(0) };
3203 #undef R
3206 * This is used when we generate our table of short strings, so the compiler is
3207 * happier if we use |c| as few times as possible.
3209 #define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' : \
3210 (c) < 36 ? 'a' - 10 : \
3211 'A' - 36))
3212 #define R FROM_SMALL_CHAR
3214 const jschar JSString::fromSmallChar[] = { R6(0) };
3216 #undef R
3219 * For code-generation ease, length-2 strings are encoded as 12-bit int values,
3220 * where the upper 6 bits is the first character and the lower 6 bits is the
3221 * second character.
3223 #define R(c) { \
3224 JSString::FLAT | JSString::ATOMIZED | (2 << JSString::FLAGS_LENGTH_SHIFT),\
3225 { (jschar *)(((char *)(length2StringTable + (c))) + \
3226 offsetof(JSString, mInlineStorage)) }, \
3227 { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
3229 #ifdef __SUNPRO_CC
3230 #pragma pack(8)
3231 #else
3232 #pragma pack(push, 8)
3233 #endif
3235 const JSString JSString::length2StringTable[]
3236 #ifdef __GNUC__
3237 __attribute__ ((aligned (8)))
3238 #endif
3239 = { R12(0) };
3241 #ifdef __SUNPRO_CC
3242 #pragma pack(0)
3243 #else
3244 #pragma pack(pop)
3245 #endif
3247 #undef R
3249 #define R(c) FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3f), 0x00
3251 const char JSString::deflatedLength2StringTable[] = { R12(0) };
3253 #undef R
3256 * Declare int strings. Only int strings from 100 to 255 actually have to be
3257 * generated, since the rest are either unit strings or length-2 strings. To
3258 * avoid the runtime cost of figuring out where to look for the string for a
3259 * particular integer, we precompute a table of JSString*s which refer to the
3260 * correct location of the int string.
3262 #define R(c) { \
3263 JSString::FLAT | JSString::ATOMIZED | (3 << JSString::FLAGS_LENGTH_SHIFT),\
3264 { (jschar *)(((char *)(hundredStringTable + ((c) - 100))) + \
3265 offsetof(JSString, mInlineStorage)) }, \
3266 { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
3269 JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256);
3271 #ifdef __SUNPRO_CC
3272 #pragma pack(8)
3273 #else
3274 #pragma pack(push, 8)
3275 #endif
3277 const JSString JSString::hundredStringTable[]
3278 #ifdef __GNUC__
3279 __attribute__ ((aligned (8)))
3280 #endif
3281 = { R7(100), /* 100 through 227 */
3282 R4(100 + (1 << 7)), /* 228 through 243 */
3283 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3284 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3287 #undef R
3289 #define R(c) ((c) < 10 ? JSString::unitStringTable + ((c) + '0') : \
3290 (c) < 100 ? JSString::length2StringTable + \
3291 ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) + \
3292 TO_SMALL_CHAR(((c) % 10) + '0') : \
3293 JSString::hundredStringTable + ((c) - 100))
3295 const JSString *const JSString::intStringTable[] = { R8(0) };
3297 #undef R
3299 #ifdef __SUNPRO_CC
3300 #pragma pack(0)
3301 #else
3302 #pragma pack(pop)
3303 #endif
3305 #define R(c) ((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00
3307 const char JSString::deflatedIntStringTable[] = {
3308 R7(100), /* 100 through 227 */
3309 R4(100 + (1 << 7)), /* 228 through 243 */
3310 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3311 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3314 #undef R
3315 #undef R2
3316 #undef R4
3317 #undef R6
3318 #undef R8
3319 #undef R10
3320 #undef R12
3322 #undef R3
3323 #undef R7
3325 /* Static table for common UTF8 encoding */
3326 #define U8(c) char(((c) >> 6) | 0xc0), char(((c) & 0x3f) | 0x80), 0
3327 #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)
3329 const char JSString::deflatedUnitStringTable[] = {
3330 U(0x80), U(0x88), U(0x90), U(0x98), U(0xa0), U(0xa8), U(0xb0), U(0xb8),
3331 U(0xc0), U(0xc8), U(0xd0), U(0xd8), U(0xe0), U(0xe8), U(0xf0), U(0xf8)
3334 #undef U
3335 #undef U8
3337 JSBool
3338 js_String(JSContext *cx, uintN argc, Value *vp)
3340 Value *argv = vp + 2;
3342 JSString *str;
3343 if (argc > 0) {
3344 str = js_ValueToString(cx, argv[0]);
3345 if (!str)
3346 return false;
3347 } else {
3348 str = cx->runtime->emptyString;
3351 if (IsConstructing(vp)) {
3352 JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass);
3353 if (!obj)
3354 return false;
3355 obj->setPrimitiveThis(StringValue(str));
3356 vp->setObject(*obj);
3357 } else {
3358 vp->setString(str);
3360 return true;
3363 static JSBool
3364 str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
3366 Value *argv;
3367 uintN i;
3368 jschar *chars;
3369 JSString *str;
3371 argv = vp + 2;
3372 JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
3373 if (argc == 1) {
3374 uint16_t code;
3375 if (!ValueToUint16(cx, argv[0], &code))
3376 return JS_FALSE;
3377 if (code < UNIT_STRING_LIMIT) {
3378 str = JSString::unitString(code);
3379 if (!str)
3380 return JS_FALSE;
3381 vp->setString(str);
3382 return JS_TRUE;
3384 argv[0].setInt32(code);
3386 chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar));
3387 if (!chars)
3388 return JS_FALSE;
3389 for (i = 0; i < argc; i++) {
3390 uint16_t code;
3391 if (!ValueToUint16(cx, argv[i], &code)) {
3392 cx->free(chars);
3393 return JS_FALSE;
3395 chars[i] = (jschar)code;
3397 chars[i] = 0;
3398 str = js_NewString(cx, chars, argc);
3399 if (!str) {
3400 cx->free(chars);
3401 return JS_FALSE;
3403 vp->setString(str);
3404 return JS_TRUE;
3407 #ifdef JS_TRACER
3408 static JSString* FASTCALL
3409 String_fromCharCode(JSContext* cx, int32 i)
3411 JS_ASSERT(JS_ON_TRACE(cx));
3412 jschar c = (jschar)i;
3413 if (c < UNIT_STRING_LIMIT)
3414 return JSString::unitString(c);
3415 return js_NewStringCopyN(cx, &c, 1);
3417 #endif
3419 JS_DEFINE_TRCINFO_1(str_fromCharCode,
3420 (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE)))
3422 static JSFunctionSpec string_static_methods[] = {
3423 JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo),
3424 JS_FS_END
3427 JSObject *
3428 js_InitStringClass(JSContext *cx, JSObject *obj)
3430 JSObject *proto;
3432 /* Define the escape, unescape functions in the global object. */
3433 if (!JS_DefineFunctions(cx, obj, string_functions))
3434 return NULL;
3436 proto = js_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
3437 NULL, string_methods,
3438 NULL, string_static_methods);
3439 if (!proto)
3440 return NULL;
3441 proto->setPrimitiveThis(StringValue(cx->runtime->emptyString));
3442 if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
3443 UndefinedValue(), NULL, NULL,
3444 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
3445 NULL)) {
3446 return JS_FALSE;
3449 return proto;
3452 JSString *
3453 js_NewString(JSContext *cx, jschar *chars, size_t length)
3455 JSString *str;
3457 if (length > JSString::MAX_LENGTH) {
3458 if (JS_ON_TRACE(cx)) {
3460 * If we can't leave the trace, signal OOM condition, otherwise
3461 * exit from trace before throwing.
3463 if (!CanLeaveTrace(cx))
3464 return NULL;
3466 LeaveTrace(cx);
3468 js_ReportAllocationOverflow(cx);
3469 return NULL;
3472 str = js_NewGCString(cx);
3473 if (!str)
3474 return NULL;
3475 str->initFlat(chars, length);
3476 #ifdef DEBUG
3478 JSRuntime *rt = cx->runtime;
3479 JS_RUNTIME_METER(rt, liveStrings);
3480 JS_RUNTIME_METER(rt, totalStrings);
3481 JS_LOCK_RUNTIME_VOID(rt,
3482 (rt->lengthSum += (double)length,
3483 rt->lengthSquaredSum += (double)length * (double)length));
3485 #endif
3486 return str;
3489 static JS_ALWAYS_INLINE JSString *
3490 NewShortString(JSContext *cx, const jschar *chars, size_t length)
3492 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3493 JSShortString *str = js_NewGCShortString(cx);
3494 if (!str)
3495 return NULL;
3496 jschar *storage = str->init(length);
3497 js_short_strncpy(storage, chars, length);
3498 storage[length] = 0;
3499 return str->header();
3502 static JSString *
3503 NewShortString(JSContext *cx, const char *chars, size_t length)
3505 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3506 JSShortString *str = js_NewGCShortString(cx);
3507 if (!str)
3508 return NULL;
3509 jschar *storage = str->init(length);
3511 if (js_CStringsAreUTF8) {
3512 #ifdef DEBUG
3513 size_t oldLength = length;
3514 #endif
3515 if (!js_InflateStringToBuffer(cx, chars, length, storage, &length))
3516 return NULL;
3517 JS_ASSERT(length <= oldLength);
3518 storage[length] = 0;
3519 str->resetLength(length);
3520 } else {
3521 size_t n = length;
3522 jschar *p = storage;
3523 while (n--)
3524 *p++ = jschar(*chars++);
3525 *p = 0;
3527 return str->header();
3530 static const size_t sMinWasteSize = 16;
3532 JSString *
3533 js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb)
3535 if (cb.empty())
3536 return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
3538 size_t length = cb.length();
3540 JS_STATIC_ASSERT(JSShortString::MAX_SHORT_STRING_LENGTH < JSCharBuffer::InlineLength);
3541 if (JSShortString::fitsIntoShortString(length))
3542 return NewShortString(cx, cb.begin(), length);
3544 if (!cb.append('\0'))
3545 return NULL;
3547 size_t capacity = cb.capacity();
3549 jschar *buf = cb.extractRawBuffer();
3550 if (!buf)
3551 return NULL;
3553 /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
3554 JS_ASSERT(capacity >= length);
3555 if (capacity > sMinWasteSize && capacity - length > (length >> 2)) {
3556 size_t bytes = sizeof(jschar) * (length + 1);
3557 jschar *tmp = (jschar *)cx->realloc(buf, bytes);
3558 if (!tmp) {
3559 cx->free(buf);
3560 return NULL;
3562 buf = tmp;
3565 JSString *str = js_NewString(cx, buf, length);
3566 if (!str)
3567 cx->free(buf);
3568 return str;
3571 JSString *
3572 js_NewDependentString(JSContext *cx, JSString *base, size_t start,
3573 size_t length)
3575 JSString *ds;
3577 if (length == 0)
3578 return cx->runtime->emptyString;
3580 if (start == 0 && length == base->length())
3581 return base;
3583 jschar *chars = base->chars() + start;
3585 JSString *staticStr = JSString::lookupStaticString(chars, length);
3586 if (staticStr)
3587 return staticStr;
3589 /* Try to avoid long chains of dependent strings. */
3590 while (base->isDependent())
3591 base = base->dependentBase();
3593 JS_ASSERT(base->isFlat());
3595 ds = js_NewGCString(cx);
3596 if (!ds)
3597 return NULL;
3598 ds->initDependent(base, chars, length);
3599 #ifdef DEBUG
3601 JSRuntime *rt = cx->runtime;
3602 JS_RUNTIME_METER(rt, liveDependentStrings);
3603 JS_RUNTIME_METER(rt, totalDependentStrings);
3604 JS_RUNTIME_METER(rt, liveStrings);
3605 JS_RUNTIME_METER(rt, totalStrings);
3606 JS_LOCK_RUNTIME_VOID(rt,
3607 (rt->strdepLengthSum += (double)length,
3608 rt->strdepLengthSquaredSum += (double)length * (double)length));
3609 JS_LOCK_RUNTIME_VOID(rt,
3610 (rt->lengthSum += (double)length,
3611 rt->lengthSquaredSum += (double)length * (double)length));
3613 #endif
3614 return ds;
3617 #ifdef DEBUG
3618 #include <math.h>
3620 void printJSStringStats(JSRuntime *rt)
3622 double mean, sigma;
3624 mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum,
3625 rt->lengthSquaredSum, &sigma);
3627 fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
3628 (unsigned long)rt->totalStrings, mean, sigma);
3630 mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum,
3631 rt->strdepLengthSquaredSum, &sigma);
3633 fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n",
3634 (unsigned long)rt->totalDependentStrings, mean, sigma);
3636 #endif
3638 JSString *
3639 js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
3641 if (JSShortString::fitsIntoShortString(n))
3642 return NewShortString(cx, s, n);
3644 jschar *news;
3645 JSString *str;
3647 news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
3648 if (!news)
3649 return NULL;
3650 js_strncpy(news, s, n);
3651 news[n] = 0;
3652 str = js_NewString(cx, news, n);
3653 if (!str)
3654 cx->free(news);
3655 return str;
3658 JSString *
3659 js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
3661 if (JSShortString::fitsIntoShortString(n))
3662 return NewShortString(cx, s, n);
3663 return JS_NewStringCopyN(cx, s, n);
3666 JSString *
3667 js_NewStringCopyZ(JSContext *cx, const jschar *s)
3669 size_t n, m;
3670 jschar *news;
3671 JSString *str;
3673 n = js_strlen(s);
3675 if (JSShortString::fitsIntoShortString(n))
3676 return NewShortString(cx, s, n);
3678 m = (n + 1) * sizeof(jschar);
3679 news = (jschar *) cx->malloc(m);
3680 if (!news)
3681 return NULL;
3682 memcpy(news, s, m);
3683 str = js_NewString(cx, news, n);
3684 if (!str)
3685 cx->free(news);
3686 return str;
3689 JSString *
3690 js_NewStringCopyZ(JSContext *cx, const char *s)
3692 return js_NewStringCopyN(cx, s, strlen(s));
3695 JS_FRIEND_API(const char *)
3696 js_ValueToPrintable(JSContext *cx, const Value &v, JSValueToStringFun v2sfun)
3698 JSString *str;
3700 str = v2sfun(cx, v);
3701 if (!str)
3702 return NULL;
3703 str = js_QuoteString(cx, str, 0);
3704 if (!str)
3705 return NULL;
3706 return js_GetStringBytes(cx, str);
3709 JSString *
3710 js_ValueToString(JSContext *cx, const Value &arg)
3712 Value v = arg;
3713 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3714 return NULL;
3716 JSString *str;
3717 if (v.isString()) {
3718 str = v.toString();
3719 } else if (v.isInt32()) {
3720 str = js_NumberToString(cx, v.toInt32());
3721 } else if (v.isDouble()) {
3722 str = js_NumberToString(cx, v.toDouble());
3723 } else if (v.isBoolean()) {
3724 str = js_BooleanToString(cx, v.toBoolean());
3725 } else if (v.isNull()) {
3726 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3727 } else {
3728 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3730 return str;
3733 static inline JSBool
3734 AppendAtom(JSAtom *atom, JSCharBuffer &cb)
3736 JSString *str = ATOM_TO_STRING(atom);
3737 const jschar *chars;
3738 size_t length;
3739 str->getCharsAndLength(chars, length);
3740 return cb.append(chars, length);
3743 /* This function implements E-262-3 section 9.8, toString. */
3744 JSBool
3745 js_ValueToCharBuffer(JSContext *cx, const Value &arg, JSCharBuffer &cb)
3747 Value v = arg;
3748 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3749 return JS_FALSE;
3751 if (v.isString()) {
3752 const jschar *chars;
3753 size_t length;
3754 v.toString()->getCharsAndLength(chars, length);
3755 return cb.append(chars, length);
3757 if (v.isNumber())
3758 return js_NumberValueToCharBuffer(cx, v, cb);
3759 if (v.isBoolean())
3760 return js_BooleanToCharBuffer(cx, v.toBoolean(), cb);
3761 if (v.isNull())
3762 return AppendAtom(cx->runtime->atomState.nullAtom, cb);
3763 JS_ASSERT(v.isUndefined());
3764 return AppendAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb);
3767 JS_FRIEND_API(JSString *)
3768 js_ValueToSource(JSContext *cx, const Value &v)
3770 if (v.isUndefined())
3771 return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
3772 if (v.isString())
3773 return js_QuoteString(cx, v.toString(), '"');
3774 if (v.isPrimitive()) {
3775 /* Special case to preserve negative zero, _contra_ toString. */
3776 if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
3777 /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
3778 static const jschar js_negzero_ucNstr[] = {'-', '0'};
3780 return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
3782 return js_ValueToString(cx, v);
3785 JSAtom *atom = cx->runtime->atomState.toSourceAtom;
3786 AutoValueRooter tvr(cx);
3787 if (!js_TryMethod(cx, &v.toObject(), atom, 0, NULL, tvr.addr()))
3788 return NULL;
3789 return js_ValueToString(cx, tvr.value());
3793 * str is not necessarily a GC thing here.
3795 uint32
3796 js_HashString(JSString *str)
3798 const jschar *s;
3799 size_t n;
3800 uint32 h;
3802 str->getCharsAndLength(s, n);
3803 for (h = 0; n; s++, n--)
3804 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
3805 return h;
3809 * str is not necessarily a GC thing here.
3811 JSBool JS_FASTCALL
3812 js_EqualStrings(JSString *str1, JSString *str2)
3814 size_t n;
3815 const jschar *s1, *s2;
3817 JS_ASSERT(str1);
3818 JS_ASSERT(str2);
3820 /* Fast case: pointer equality could be a quick win. */
3821 if (str1 == str2)
3822 return JS_TRUE;
3824 n = str1->length();
3825 if (n != str2->length())
3826 return JS_FALSE;
3828 if (n == 0)
3829 return JS_TRUE;
3831 s1 = str1->chars(), s2 = str2->chars();
3832 do {
3833 if (*s1 != *s2)
3834 return JS_FALSE;
3835 ++s1, ++s2;
3836 } while (--n != 0);
3838 return JS_TRUE;
3840 JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, nanojit::ACCSET_NONE)
3842 int32 JS_FASTCALL
3843 js_CompareStrings(JSString *str1, JSString *str2)
3845 size_t l1, l2, n, i;
3846 const jschar *s1, *s2;
3847 intN cmp;
3849 JS_ASSERT(str1);
3850 JS_ASSERT(str2);
3852 /* Fast case: pointer equality could be a quick win. */
3853 if (str1 == str2)
3854 return 0;
3856 str1->getCharsAndLength(s1, l1);
3857 str2->getCharsAndLength(s2, l2);
3858 n = JS_MIN(l1, l2);
3859 for (i = 0; i < n; i++) {
3860 cmp = s1[i] - s2[i];
3861 if (cmp != 0)
3862 return cmp;
3864 return (intN)(l1 - l2);
3866 JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, nanojit::ACCSET_NONE)
3868 size_t
3869 js_strlen(const jschar *s)
3871 const jschar *t;
3873 for (t = s; *t != 0; t++)
3874 continue;
3875 return (size_t)(t - s);
3878 jschar *
3879 js_strchr(const jschar *s, jschar c)
3881 while (*s != 0) {
3882 if (*s == c)
3883 return (jschar *)s;
3884 s++;
3886 return NULL;
3889 jschar *
3890 js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
3892 while (s < limit) {
3893 if (*s == c)
3894 return (jschar *)s;
3895 s++;
3897 return NULL;
3900 jschar *
3901 js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)
3903 size_t nbytes, nchars, i;
3904 jschar *chars;
3905 #ifdef DEBUG
3906 JSBool ok;
3907 #endif
3909 nbytes = *lengthp;
3910 if (js_CStringsAreUTF8) {
3911 if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))
3912 goto bad;
3913 chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar));
3914 if (!chars)
3915 goto bad;
3916 #ifdef DEBUG
3917 ok =
3918 #endif
3919 js_InflateStringToBuffer(cx, bytes, nbytes, chars, &nchars);
3920 JS_ASSERT(ok);
3921 } else {
3922 nchars = nbytes;
3923 chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
3924 if (!chars)
3925 goto bad;
3926 for (i = 0; i < nchars; i++)
3927 chars[i] = (unsigned char) bytes[i];
3929 *lengthp = nchars;
3930 chars[nchars] = 0;
3931 return chars;
3933 bad:
3935 * For compatibility with callers of JS_DecodeBytes we must zero lengthp
3936 * on errors.
3938 *lengthp = 0;
3939 return NULL;
3943 * May be called with null cx by js_GetStringBytes, see below.
3945 char *
3946 js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
3948 size_t nbytes, i;
3949 char *bytes;
3950 #ifdef DEBUG
3951 JSBool ok;
3952 #endif
3954 if (js_CStringsAreUTF8) {
3955 nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
3956 if (nbytes == (size_t) -1)
3957 return NULL;
3958 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3959 if (!bytes)
3960 return NULL;
3961 #ifdef DEBUG
3962 ok =
3963 #endif
3964 js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes);
3965 JS_ASSERT(ok);
3966 } else {
3967 nbytes = nchars;
3968 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3969 if (!bytes)
3970 return NULL;
3971 for (i = 0; i < nbytes; i++)
3972 bytes[i] = (char) chars[i];
3974 bytes[nbytes] = 0;
3975 return bytes;
3978 size_t
3979 js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
3981 if (!js_CStringsAreUTF8)
3982 return nchars;
3984 return js_GetDeflatedUTF8StringLength(cx, chars, nchars);
3988 * May be called with null cx through js_GetStringBytes, see below.
3990 size_t
3991 js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, size_t nchars)
3993 size_t nbytes;
3994 const jschar *end;
3995 uintN c, c2;
3996 char buffer[10];
3998 nbytes = nchars;
3999 for (end = chars + nchars; chars != end; chars++) {
4000 c = *chars;
4001 if (c < 0x80)
4002 continue;
4003 if (0xD800 <= c && c <= 0xDFFF) {
4004 /* Surrogate pair. */
4005 chars++;
4007 /* nbytes sets 1 length since this is surrogate pair. */
4008 nbytes--;
4009 if (c >= 0xDC00 || chars == end)
4010 goto bad_surrogate;
4011 c2 = *chars;
4012 if (c2 < 0xDC00 || c2 > 0xDFFF)
4013 goto bad_surrogate;
4014 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4016 c >>= 11;
4017 nbytes++;
4018 while (c) {
4019 c >>= 5;
4020 nbytes++;
4023 return nbytes;
4025 bad_surrogate:
4026 if (cx) {
4027 JS_snprintf(buffer, 10, "0x%x", c);
4028 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
4029 NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
4031 return (size_t) -1;
4034 JSBool
4035 js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
4036 char *dst, size_t *dstlenp)
4038 size_t dstlen, i;
4040 dstlen = *dstlenp;
4041 if (!js_CStringsAreUTF8) {
4042 if (srclen > dstlen) {
4043 for (i = 0; i < dstlen; i++)
4044 dst[i] = (char) src[i];
4045 if (cx) {
4046 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4047 JSMSG_BUFFER_TOO_SMALL);
4049 return JS_FALSE;
4051 for (i = 0; i < srclen; i++)
4052 dst[i] = (char) src[i];
4053 *dstlenp = srclen;
4054 return JS_TRUE;
4057 return js_DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
4060 JSBool
4061 js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
4062 char *dst, size_t *dstlenp)
4064 size_t dstlen, i, origDstlen, utf8Len;
4065 jschar c, c2;
4066 uint32 v;
4067 uint8 utf8buf[6];
4069 dstlen = *dstlenp;
4070 origDstlen = dstlen;
4071 while (srclen) {
4072 c = *src++;
4073 srclen--;
4074 if ((c >= 0xDC00) && (c <= 0xDFFF))
4075 goto badSurrogate;
4076 if (c < 0xD800 || c > 0xDBFF) {
4077 v = c;
4078 } else {
4079 if (srclen < 1)
4080 goto badSurrogate;
4081 c2 = *src;
4082 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
4083 goto badSurrogate;
4084 src++;
4085 srclen--;
4086 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4088 if (v < 0x0080) {
4089 /* no encoding necessary - performance hack */
4090 if (dstlen == 0)
4091 goto bufferTooSmall;
4092 *dst++ = (char) v;
4093 utf8Len = 1;
4094 } else {
4095 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
4096 if (utf8Len > dstlen)
4097 goto bufferTooSmall;
4098 for (i = 0; i < utf8Len; i++)
4099 *dst++ = (char) utf8buf[i];
4101 dstlen -= utf8Len;
4103 *dstlenp = (origDstlen - dstlen);
4104 return JS_TRUE;
4106 badSurrogate:
4107 *dstlenp = (origDstlen - dstlen);
4108 /* Delegate error reporting to the measurement function. */
4109 if (cx)
4110 js_GetDeflatedStringLength(cx, src - 1, srclen + 1);
4111 return JS_FALSE;
4113 bufferTooSmall:
4114 *dstlenp = (origDstlen - dstlen);
4115 if (cx) {
4116 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4117 JSMSG_BUFFER_TOO_SMALL);
4119 return JS_FALSE;
4122 JSBool
4123 js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
4124 jschar *dst, size_t *dstlenp)
4126 size_t dstlen, i;
4128 if (!js_CStringsAreUTF8) {
4129 if (dst) {
4130 dstlen = *dstlenp;
4131 if (srclen > dstlen) {
4132 for (i = 0; i < dstlen; i++)
4133 dst[i] = (unsigned char) src[i];
4134 if (cx) {
4135 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4136 JSMSG_BUFFER_TOO_SMALL);
4138 return JS_FALSE;
4140 for (i = 0; i < srclen; i++)
4141 dst[i] = (unsigned char) src[i];
4143 *dstlenp = srclen;
4144 return JS_TRUE;
4147 return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
4150 JSBool
4151 js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
4152 jschar *dst, size_t *dstlenp)
4154 size_t dstlen, origDstlen, offset, j, n;
4155 uint32 v;
4157 dstlen = dst ? *dstlenp : (size_t) -1;
4158 origDstlen = dstlen;
4159 offset = 0;
4161 while (srclen) {
4162 v = (uint8) *src;
4163 n = 1;
4164 if (v & 0x80) {
4165 while (v & (0x80 >> n))
4166 n++;
4167 if (n > srclen)
4168 goto bufferTooSmall;
4169 if (n == 1 || n > 4)
4170 goto badCharacter;
4171 for (j = 1; j < n; j++) {
4172 if ((src[j] & 0xC0) != 0x80)
4173 goto badCharacter;
4175 v = Utf8ToOneUcs4Char((uint8 *)src, n);
4176 if (v >= 0x10000) {
4177 v -= 0x10000;
4178 if (v > 0xFFFFF || dstlen < 2) {
4179 *dstlenp = (origDstlen - dstlen);
4180 if (cx) {
4181 char buffer[10];
4182 JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
4183 JS_ReportErrorFlagsAndNumber(cx,
4184 JSREPORT_ERROR,
4185 js_GetErrorMessage, NULL,
4186 JSMSG_UTF8_CHAR_TOO_LARGE,
4187 buffer);
4189 return JS_FALSE;
4191 if (dst) {
4192 *dst++ = (jschar)((v >> 10) + 0xD800);
4193 v = (jschar)((v & 0x3FF) + 0xDC00);
4195 dstlen--;
4198 if (!dstlen)
4199 goto bufferTooSmall;
4200 if (dst)
4201 *dst++ = (jschar) v;
4202 dstlen--;
4203 offset += n;
4204 src += n;
4205 srclen -= n;
4207 *dstlenp = (origDstlen - dstlen);
4208 return JS_TRUE;
4210 badCharacter:
4211 *dstlenp = (origDstlen - dstlen);
4212 if (cx) {
4213 char buffer[10];
4214 JS_snprintf(buffer, 10, "%d", offset);
4215 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
4216 js_GetErrorMessage, NULL,
4217 JSMSG_MALFORMED_UTF8_CHAR,
4218 buffer);
4220 return JS_FALSE;
4222 bufferTooSmall:
4223 *dstlenp = (origDstlen - dstlen);
4224 if (cx) {
4225 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4226 JSMSG_BUFFER_TOO_SMALL);
4228 return JS_FALSE;
4231 namespace js {
4233 DeflatedStringCache::DeflatedStringCache()
4235 #ifdef JS_THREADSAFE
4236 lock = NULL;
4237 #endif
4240 bool
4241 DeflatedStringCache::init()
4243 #ifdef JS_THREADSAFE
4244 JS_ASSERT(!lock);
4245 lock = JS_NEW_LOCK();
4246 if (!lock)
4247 return false;
4248 #endif
4251 * Make room for 2K deflated strings that a typical browser session
4252 * creates.
4254 return map.init(2048);
4257 DeflatedStringCache::~DeflatedStringCache()
4259 #ifdef JS_THREADSAFE
4260 if (lock)
4261 JS_DESTROY_LOCK(lock);
4262 #endif
4265 void
4266 DeflatedStringCache::sweep(JSContext *cx)
4269 * We must take a lock even during the GC as JS_GetStringBytes() can be
4270 * called outside the request.
4272 JS_ACQUIRE_LOCK(lock);
4274 for (Map::Enum e(map); !e.empty(); e.popFront()) {
4275 JSString *str = e.front().key;
4276 if (IsAboutToBeFinalized(str)) {
4277 char *bytes = e.front().value;
4278 e.removeFront();
4281 * We cannot use cx->free here as bytes may come from the
4282 * embedding that calls JS_NewString(cx, bytes, length). Those
4283 * bytes may not be allocated via js_malloc and may not have
4284 * space for the background free list.
4286 js_free(bytes);
4290 JS_RELEASE_LOCK(lock);
4293 void
4294 DeflatedStringCache::remove(JSString *str)
4296 JS_ACQUIRE_LOCK(lock);
4298 Map::Ptr p = map.lookup(str);
4299 if (p) {
4300 js_free(p->value);
4301 map.remove(p);
4304 JS_RELEASE_LOCK(lock);
4307 bool
4308 DeflatedStringCache::setBytes(JSContext *cx, JSString *str, char *bytes)
4310 JS_ACQUIRE_LOCK(lock);
4312 Map::AddPtr p = map.lookupForAdd(str);
4313 JS_ASSERT(!p);
4314 bool ok = map.add(p, str, bytes);
4316 JS_RELEASE_LOCK(lock);
4318 if (!ok)
4319 js_ReportOutOfMemory(cx);
4320 return ok;
4323 char *
4324 DeflatedStringCache::getBytes(JSContext *cx, JSString *str)
4326 JS_ACQUIRE_LOCK(lock);
4327 Map::AddPtr p = map.lookupForAdd(str);
4328 char *bytes = p ? p->value : NULL;
4329 JS_RELEASE_LOCK(lock);
4331 if (bytes)
4332 return bytes;
4334 bytes = js_DeflateString(cx, str->chars(), str->length());
4335 if (!bytes)
4336 return NULL;
4339 * In the single-threaded case we use the add method as js_DeflateString
4340 * cannot mutate the map. In particular, it cannot run the GC that may
4341 * delete entries from the map. But the JS_THREADSAFE version requires to
4342 * deal with other threads adding the entries to the map.
4344 char *bytesToFree = NULL;
4345 JSBool ok;
4346 #ifdef JS_THREADSAFE
4347 JS_ACQUIRE_LOCK(lock);
4348 ok = map.relookupOrAdd(p, str, bytes);
4349 if (ok && p->value != bytes) {
4350 /* Some other thread has asked for str bytes .*/
4351 JS_ASSERT(!strcmp(p->value, bytes));
4352 bytesToFree = bytes;
4353 bytes = p->value;
4355 JS_RELEASE_LOCK(lock);
4356 #else /* !JS_THREADSAFE */
4357 ok = map.add(p, str, bytes);
4358 #endif
4359 if (!ok) {
4360 bytesToFree = bytes;
4361 bytes = NULL;
4362 if (cx)
4363 js_ReportOutOfMemory(cx);
4366 if (bytesToFree) {
4367 if (cx)
4368 cx->free(bytesToFree);
4369 else
4370 js_free(bytesToFree);
4372 return bytes;
4375 } /* namespace js */
4377 const char *
4378 js_GetStringBytes(JSContext *cx, JSString *str)
4380 JSRuntime *rt;
4381 char *bytes;
4383 if (JSString::isUnitString(str)) {
4384 #ifdef IS_LITTLE_ENDIAN
4385 /* Unit string data is {c, 0, 0, 0} so we can just cast. */
4386 bytes = (char *)str->chars();
4387 #else
4388 /* Unit string data is {0, c, 0, 0} so we can point into the middle. */
4389 bytes = (char *)str->chars() + 1;
4390 #endif
4391 return ((*bytes & 0x80) && js_CStringsAreUTF8)
4392 ? JSString::deflatedUnitStringTable + ((*bytes & 0x7f) * 3)
4393 : bytes;
4397 * We must burn some space on deflated int strings and length-2 strings
4398 * to preserve static allocation (which is to say, JSRuntime independence).
4400 if (JSString::isLength2String(str))
4401 return JSString::deflatedLength2StringTable + ((str - JSString::length2StringTable) * 3);
4403 if (JSString::isHundredString(str)) {
4405 * We handled the 1 and 2-digit number cases already, so we know that
4406 * str is between 100 and 255.
4408 return JSString::deflatedIntStringTable + ((str - JSString::hundredStringTable) * 4);
4411 if (cx) {
4412 rt = cx->runtime;
4413 } else {
4414 /* JS_GetStringBytes calls us with null cx. */
4415 rt = GetGCThingRuntime(str);
4418 return rt->deflatedStringCache->getBytes(cx, str);
4422 * From java.lang.Character.java:
4424 * The character properties are currently encoded into 32 bits in the
4425 * following manner:
4427 * 10 bits signed offset used for converting case
4428 * 1 bit if 1, adding the signed offset converts the character to
4429 * lowercase
4430 * 1 bit if 1, subtracting the signed offset converts the character to
4431 * uppercase
4432 * 1 bit if 1, character has a titlecase equivalent (possibly itself)
4433 * 3 bits 0 may not be part of an identifier
4434 * 1 ignorable control; may continue a Unicode identifier or JS
4435 * identifier
4436 * 2 may continue a JS identifier but not a Unicode identifier
4437 * (unused)
4438 * 3 may continue a Unicode identifier or JS identifier
4439 * 4 is a JS whitespace character
4440 * 5 may start or continue a JS identifier;
4441 * may continue but not start a Unicode identifier (_)
4442 * 6 may start or continue a JS identifier but not a Unicode
4443 * identifier ($)
4444 * 7 may start or continue a Unicode identifier or JS identifier
4445 * Thus:
4446 * 5, 6, 7 may start a JS identifier
4447 * 1, 2, 3, 5, 6, 7 may continue a JS identifier
4448 * 7 may start a Unicode identifier
4449 * 1, 3, 5, 7 may continue a Unicode identifier
4450 * 1 is ignorable within an identifier
4451 * 4 is JS whitespace
4452 * 2 bits 0 this character has no numeric property
4453 * 1 adding the digit offset to the character code and then
4454 * masking with 0x1F will produce the desired numeric value
4455 * 2 this character has a "strange" numeric value
4456 * 3 a JS supradecimal digit: adding the digit offset to the
4457 * character code, then masking with 0x1F, then adding 10
4458 * will produce the desired numeric value
4459 * 5 bits digit offset
4460 * 1 bit XML 1.0 name start character
4461 * 1 bit XML 1.0 name character
4462 * 2 bits reserved for future use
4463 * 5 bits character type
4466 /* The X table has 1024 entries for a total of 1024 bytes. */
4468 const uint8 js_X[] = {
4469 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */
4470 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */
4471 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */
4472 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */
4473 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */
4474 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */
4475 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */
4476 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */
4477 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */
4478 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */
4479 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */
4480 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */
4481 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */
4482 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */
4483 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */
4484 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */
4485 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */
4486 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */
4487 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */
4488 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */
4489 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */
4490 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */
4491 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */
4492 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */
4493 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */
4494 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */
4495 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */
4496 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */
4497 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */
4498 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */
4499 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */
4500 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */
4501 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */
4502 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */
4503 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */
4504 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */
4505 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */
4506 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */
4507 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */
4508 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */
4509 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */
4510 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */
4511 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */
4512 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */
4513 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */
4514 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */
4515 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */
4516 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */
4517 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */
4518 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */
4519 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */
4520 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */
4521 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */
4522 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */
4523 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */
4524 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */
4525 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */
4526 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */
4527 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */
4528 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */
4529 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */
4530 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */
4531 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */
4532 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */
4533 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */
4534 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */
4535 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */
4536 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */
4537 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */
4538 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */
4539 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */
4540 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */
4541 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */
4542 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */
4543 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */
4544 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */
4545 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */
4546 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */
4547 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */
4548 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */
4549 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */
4550 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */
4551 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */
4552 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */
4553 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */
4554 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */
4555 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */
4556 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */
4557 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */
4558 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */
4559 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */
4560 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */
4561 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */
4562 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */
4563 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */
4564 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */
4565 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */
4566 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */
4567 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */
4568 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */
4569 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */
4570 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */
4571 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */
4572 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */
4573 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */
4574 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */
4575 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */
4576 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */
4577 104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */
4578 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */
4579 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */
4580 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */
4581 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */
4582 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */
4583 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */
4584 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */
4585 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */
4586 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */
4587 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */
4588 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */
4589 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */
4590 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */
4591 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */
4592 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */
4593 105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */
4594 106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */
4595 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */
4596 115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */
4599 /* The Y table has 7808 entries for a total of 7808 bytes. */
4601 const uint8 js_Y[] = {
4602 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4603 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */
4604 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4605 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4606 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */
4607 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */
4608 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */
4609 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */
4610 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4611 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4612 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4613 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */
4614 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4615 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4616 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4617 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */
4618 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4619 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4620 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4621 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4622 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */
4623 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */
4624 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */
4625 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */
4626 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4627 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4628 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */
4629 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */
4630 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4631 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4632 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */
4633 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */
4634 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4635 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4636 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
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 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */
4641 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */
4642 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */
4643 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */
4644 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4645 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4646 23, 24, 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 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */
4650 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */
4651 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */
4652 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */
4653 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */
4654 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */
4655 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */
4656 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */
4657 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */
4658 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */
4659 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */
4660 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */
4661 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */
4662 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4663 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4664 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */
4665 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */
4666 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4667 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4668 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4669 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4670 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4671 46, 46, 46, 46, 46, 46, 46, 46, /* 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, /* 9 */
4675 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4676 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */
4677 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */
4678 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */
4679 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */
4680 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */
4681 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */
4682 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */
4683 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */
4684 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */
4685 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4686 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4687 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */
4688 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */
4689 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */
4690 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4691 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */
4692 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4693 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */
4694 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */
4695 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */
4696 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4697 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4698 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4699 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4700 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
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, 46, 46, /* 13 */
4707 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4708 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4709 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4710 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */
4711 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4712 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */
4713 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */
4714 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */
4715 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */
4716 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4717 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4718 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */
4719 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */
4720 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4721 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4722 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */
4723 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */
4724 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */
4725 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */
4726 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */
4727 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */
4728 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */
4729 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */
4730 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */
4731 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */
4732 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4733 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4734 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4735 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4736 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4737 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4738 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4739 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4740 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */
4741 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */
4742 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4743 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4744 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4745 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4746 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */
4747 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */
4748 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4749 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4750 23, 24, 23, 24, 23, 24, 23, 24, /* 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 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */
4755 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */
4756 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4757 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4758 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4759 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */
4760 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */
4761 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */
4762 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4763 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4764 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
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, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4769 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4770 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4771 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4772 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */
4773 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */
4774 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4775 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4776 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4777 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4778 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */
4779 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */
4780 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4781 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4782 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */
4783 60, 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, 3, 60, /* 22 */
4786 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */
4787 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4788 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4789 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4790 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4791 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */
4792 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */
4793 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4794 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4795 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */
4796 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4797 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */
4798 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4799 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4800 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4801 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */
4802 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4803 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */
4804 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */
4805 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */
4806 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */
4807 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */
4808 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4809 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4810 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4811 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4812 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
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 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */
4818 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */
4819 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */
4820 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */
4821 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */
4822 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */
4823 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */
4824 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */
4825 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */
4826 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4827 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4828 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
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, 60, 60, 80, 46, 40, 40, 40, /* 29 */
4835 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4836 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4837 40, 40, 40, 40, 40, 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, 46, 46, 60, 40, 80, 80, /* 29 */
4842 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */
4843 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */
4844 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */
4845 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */
4846 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */
4847 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */
4848 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4849 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4850 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */
4851 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */
4852 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */
4853 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4854 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4855 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */
4856 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */
4857 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */
4858 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */
4859 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */
4860 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */
4861 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */
4862 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */
4863 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */
4864 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */
4865 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */
4866 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */
4867 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */
4868 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */
4869 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4870 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4871 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */
4872 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */
4873 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */
4874 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */
4875 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */
4876 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4877 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */
4878 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */
4879 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */
4880 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */
4881 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4882 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */
4883 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */
4884 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */
4885 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4886 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4887 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */
4888 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */
4889 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */
4890 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */
4891 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */
4892 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4893 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4894 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */
4895 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */
4896 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4897 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4898 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */
4899 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */
4900 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */
4901 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4902 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4903 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */
4904 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */
4905 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */
4906 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */
4907 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */
4908 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */
4909 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */
4910 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */
4911 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */
4912 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4913 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4914 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */
4915 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4916 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */
4917 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */
4918 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */
4919 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4920 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */
4921 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */
4922 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */
4923 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */
4924 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */
4925 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4926 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */
4927 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */
4928 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */
4929 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4930 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */
4931 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */
4932 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4933 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4934 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4935 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4936 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */
4937 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */
4938 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */
4939 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */
4940 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */
4941 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4942 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */
4943 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */
4944 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4945 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4946 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */
4947 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */
4948 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4949 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4950 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4951 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4952 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */
4953 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */
4954 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */
4955 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */
4956 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */
4957 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */
4958 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */
4959 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */
4960 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4961 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4962 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */
4963 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */
4964 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4965 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4966 40, 40, 40, 40, 40, 40, 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, 46, 46, 46, 46, 80, 80, /* 45 */
4970 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */
4971 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */
4972 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */
4973 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4974 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */
4975 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */
4976 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4977 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4978 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4979 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4980 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4981 40, 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, 3, /* 47 */
4984 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */
4985 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */
4986 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */
4987 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */
4988 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */
4989 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */
4990 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4991 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4992 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4993 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4994 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */
4995 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */
4996 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */
4997 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */
4998 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */
4999 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */
5000 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */
5001 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */
5002 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */
5003 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */
5004 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */
5005 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */
5006 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5007 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5008 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5009 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
5010 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */
5011 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */
5012 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */
5013 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */
5014 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */
5015 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */
5016 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */
5017 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */
5018 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5019 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5020 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5021 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5022 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
5023 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */
5024 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */
5025 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */
5026 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */
5027 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */
5028 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */
5029 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5030 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5031 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */
5032 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
5033 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */
5034 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5035 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5036 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5037 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
5038 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5039 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5040 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5041 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
5042 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */
5043 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */
5044 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5045 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5046 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5047 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
5048 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */
5049 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */
5050 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5051 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
5052 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
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, /* 57 */
5059 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5060 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5061 40, 40, 46, 46, 46, 46, 46, 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, 40, 40, 40, 40, 40, 40, /* 57 */
5065 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
5066 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5067 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5068 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5069 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
5070 40, 40, 40, 46, 46, 46, 46, 46, /* 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, 40, 40, 40, 40, 40, /* 58 */
5074 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5075 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
5076 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
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, 46, 46, 46, 46, 46, 46, /* 59 */
5082 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5083 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
5084 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
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, /* 61 */
5091 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5092 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */
5093 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */
5094 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5095 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5096 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5097 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
5098 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5099 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
5100 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
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, 46, 46, 46, 46, 46, 46, /* 62 */
5106 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5107 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5108 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */
5109 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */
5110 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5111 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5112 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
5113 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
5114 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */
5115 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */
5116 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */
5117 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */
5118 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */
5119 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */
5120 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */
5121 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */
5122 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5123 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5124 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5125 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5126 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
5127 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
5128 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */
5129 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */
5130 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */
5131 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */
5132 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */
5133 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */
5134 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */
5135 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */
5136 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */
5137 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */
5138 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */
5139 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */
5140 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */
5141 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */
5142 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
5143 105, 106, 104, 104, 104, 104, 104, 46, /* 67 */
5144 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
5145 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */
5146 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */
5147 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5148 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5149 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5150 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
5151 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */
5152 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */
5153 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */
5154 107, 107, 107, 107, 107, 107, 107, 107, /* 69 */
5155 107, 107, 7, 7, 7, 5, 6, 46, /* 69 */
5156 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5157 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5158 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */
5159 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */
5160 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5161 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
5162 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5163 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5164 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */
5165 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */
5166 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */
5167 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5168 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5169 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5170 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */
5171 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */
5172 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */
5173 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */
5174 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */
5175 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */
5176 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */
5177 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */
5178 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5179 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5180 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */
5181 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */
5182 109, 109, 109, 109, 109, 109, 109, 109, /* 72 */
5183 109, 109, 109, 109, 110, 110, 110, 110, /* 72 */
5184 111, 111, 111, 111, 111, 111, 111, 111, /* 72 */
5185 111, 111, 111, 111, 112, 112, 112, 112, /* 72 */
5186 113, 113, 113, 46, 46, 46, 46, 46, /* 73 */
5187 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */
5188 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */
5189 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5190 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5191 15, 15, 15, 15, 15, 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, /* 74 */
5195 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5196 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */
5197 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5198 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5199 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */
5200 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5201 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5202 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5203 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5204 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
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, /* 76 */
5211 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5212 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
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, 46, 46, 46, 46, 46, 46, /* 76 */
5217 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */
5218 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */
5219 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */
5220 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5221 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5222 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */
5223 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */
5224 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5225 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5226 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5227 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5228 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
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, 46, 46, 46, 46, 46, /* 78 */
5234 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5235 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5236 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5237 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5238 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */
5239 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5240 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5241 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5242 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */
5243 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */
5244 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5245 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5246 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5247 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5248 114, 114, 114, 114, 82, 82, 82, 82, /* 80 */
5249 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */
5250 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */
5251 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5252 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5253 115, 115, 115, 115, 15, 15, 15, 15, /* 81 */
5254 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5255 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5256 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */
5257 116, 116, 116, 116, 116, 116, 116, 116, /* 81 */
5258 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5259 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5260 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5261 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5262 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5263 117, 117, 118, 46, 46, 46, 46, 46, /* 82 */
5264 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5265 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5266 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5267 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5268 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
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, /* 84 */
5275 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5276 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */
5277 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */
5278 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5279 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5280 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5281 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5282 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5283 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5284 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
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 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5289 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5290 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5291 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5292 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */
5293 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */
5294 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5295 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5296 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5297 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5298 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */
5299 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */
5300 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5301 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5302 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5303 46, 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 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5307 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */
5308 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */
5309 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */
5310 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5311 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */
5312 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */
5313 119, 119, 119, 119, 119, 119, 119, 119, /* 88 */
5314 114, 114, 114, 114, 114, 114, 114, 114, /* 89 */
5315 114, 114, 83, 83, 83, 83, 83, 83, /* 89 */
5316 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */
5317 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5318 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5319 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5320 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5321 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */
5322 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */
5323 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */
5324 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */
5325 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */
5326 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */
5327 121, 121, 60, 60, 60, 60, 60, 60, /* 90 */
5328 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */
5329 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */
5330 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5331 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5332 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5333 40, 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, /* 92 */
5339 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5340 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */
5341 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */
5342 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5343 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5344 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5345 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5346 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5347 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5348 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
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, 3, 59, 59, 59, 46, /* 93 */
5354 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */
5355 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5356 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5357 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5358 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5359 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */
5360 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5361 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5362 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */
5363 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */
5364 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */
5365 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */
5366 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5367 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5368 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5369 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5370 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5371 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5372 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5373 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */
5374 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */
5375 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */
5376 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5377 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5378 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */
5379 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5380 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5381 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5382 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5383 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5384 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5385 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */
5386 114, 114, 114, 114, 114, 114, 114, 114, /* 98 */
5387 114, 114, 15, 15, 15, 15, 15, 15, /* 98 */
5388 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5389 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5390 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5391 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5392 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5393 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5394 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5395 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */
5396 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5397 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5398 15, 15, 15, 15, 15, 15, 15, 15, /* 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, 46, /* 99 */
5402 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5403 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5404 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
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, 46, /* 100 */
5409 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */
5410 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5411 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5412 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5413 15, 15, 15, 15, 15, 15, 46, 46, /* 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, 15, 15, /* 101 */
5417 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */
5418 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5419 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5420 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5421 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5422 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */
5423 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5424 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5425 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5426 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5427 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5428 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5429 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5430 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */
5431 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5432 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5433 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5434 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5435 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5436 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
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 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5443 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5444 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
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 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5451 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5452 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
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, 46, 46, /* 106 */
5456 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5457 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5458 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */
5459 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */
5460 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */
5461 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */
5462 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */
5463 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */
5464 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */
5465 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */
5466 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */
5467 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5468 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5469 40, 40, 40, 40, 40, 40, 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, /* 109 */
5475 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5476 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
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, 46, 46, 46, 46, 46, 46, /* 109 */
5481 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */
5482 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5483 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5484 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */
5485 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5486 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5487 40, 40, 40, 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, /* 111 */
5491 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5492 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
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, 5, 6, /* 111 */
5498 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5499 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5500 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5501 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5502 40, 40, 40, 40, 40, 40, 40, 40, /* 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, /* 113 */
5507 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5508 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */
5509 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5510 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5511 40, 40, 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, /* 114 */
5515 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5516 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5517 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5518 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5519 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5520 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5521 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */
5522 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5523 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5524 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5525 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5526 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */
5527 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5528 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */
5529 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */
5530 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */
5531 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */
5532 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */
5533 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */
5534 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */
5535 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */
5536 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */
5537 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */
5538 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5539 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5540 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
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, 46, 46, 104, /* 117 */
5546 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */
5547 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */
5548 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */
5549 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */
5550 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5551 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5552 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5553 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */
5554 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5555 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5556 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5557 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */
5558 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */
5559 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5560 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5561 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5562 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5563 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5564 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5565 40, 40, 40, 40, 40, 40, 59, 59, /* 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, 40, 40, /* 120 */
5569 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */
5570 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5571 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5572 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5573 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */
5574 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */
5575 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */
5576 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */
5577 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */
5580 /* The A table has 124 entries for a total of 496 bytes. */
5582 const uint32 js_A[] = {
5583 0x0001000F, /* 0 Cc, ignorable */
5584 0x0004000F, /* 1 Cc, whitespace */
5585 0x0004000C, /* 2 Zs, whitespace */
5586 0x00000018, /* 3 Po */
5587 0x0006001A, /* 4 Sc, currency */
5588 0x00000015, /* 5 Ps */
5589 0x00000016, /* 6 Pe */
5590 0x00000019, /* 7 Sm */
5591 0x00000014, /* 8 Pd */
5592 0x00036089, /* 9 Nd, identifier part, decimal 16 */
5593 0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */
5594 0x0000001B, /* 11 Sk */
5595 0x00050017, /* 12 Pc, underscore */
5596 0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */
5597 0x0000000C, /* 14 Zs */
5598 0x0000001C, /* 15 So */
5599 0x00070182, /* 16 Ll, identifier start */
5600 0x0000600B, /* 17 No, decimal 16 */
5601 0x0000500B, /* 18 No, decimal 8 */
5602 0x0000800B, /* 19 No, strange */
5603 0x08270181, /* 20 Lu, hasLower (add 32), identifier start */
5604 0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */
5605 0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */
5606 0x00670181, /* 23 Lu, hasLower (add 1), identifier start */
5607 0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */
5608 0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */
5609 0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */
5610 0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */
5611 0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */
5612 0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */
5613 0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */
5614 0x33670181, /* 31 Lu, hasLower (add 205), identifier start */
5615 0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */
5616 0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */
5617 0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */
5618 0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */
5619 0x34670181, /* 36 Lu, hasLower (add 209), identifier start */
5620 0x35670181, /* 37 Lu, hasLower (add 213), identifier start */
5621 0x00070181, /* 38 Lu, identifier start */
5622 0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */
5623 0x00070185, /* 40 Lo, identifier start */
5624 0x36670181, /* 41 Lu, hasLower (add 217), identifier start */
5625 0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */
5626 0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */
5627 0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */
5628 0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */
5629 0x00000000, /* 46 unassigned */
5630 0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */
5631 0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */
5632 0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */
5633 0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */
5634 0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */
5635 0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */
5636 0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */
5637 0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */
5638 0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */
5639 0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */
5640 0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */
5641 0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */
5642 0x00070084, /* 59 Lm, identifier start */
5643 0x00030086, /* 60 Mn, identifier part */
5644 0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */
5645 0x09670181, /* 62 Lu, hasLower (add 37), identifier start */
5646 0x10270181, /* 63 Lu, hasLower (add 64), identifier start */
5647 0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */
5648 0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */
5649 0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */
5650 0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */
5651 0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */
5652 0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */
5653 0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */
5654 0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */
5655 0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */
5656 0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */
5657 0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */
5658 0x14270181, /* 75 Lu, hasLower (add 80), identifier start */
5659 0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */
5660 0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */
5661 0x00034089, /* 78 Nd, identifier part, decimal 0 */
5662 0x00000087, /* 79 Me */
5663 0x00030088, /* 80 Mc, identifier part */
5664 0x00037489, /* 81 Nd, identifier part, decimal 26 */
5665 0x00005A0B, /* 82 No, decimal 13 */
5666 0x00006E0B, /* 83 No, decimal 23 */
5667 0x0000740B, /* 84 No, decimal 26 */
5668 0x0000000B, /* 85 No */
5669 0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */
5670 0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */
5671 0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */
5672 0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */
5673 0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */
5674 0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */
5675 0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */
5676 0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */
5677 0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */
5678 0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */
5679 0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */
5680 0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */
5681 0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */
5682 0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */
5683 0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */
5684 0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */
5685 0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */
5686 0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */
5687 0x00010010, /* 104 Cf, ignorable */
5688 0x0004000D, /* 105 Zl, whitespace */
5689 0x0004000E, /* 106 Zp, whitespace */
5690 0x0000400B, /* 107 No, decimal 0 */
5691 0x0000440B, /* 108 No, decimal 2 */
5692 0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */
5693 0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */
5694 0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */
5695 0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */
5696 0x0007818A, /* 113 Nl, identifier start, strange */
5697 0x0000420B, /* 114 No, decimal 1 */
5698 0x0000720B, /* 115 No, decimal 25 */
5699 0x06A0001C, /* 116 So, hasLower (add 26) */
5700 0x0690001C, /* 117 So, hasUpper (subtract 26) */
5701 0x00006C0B, /* 118 No, decimal 22 */
5702 0x0000560B, /* 119 No, decimal 11 */
5703 0x0007738A, /* 120 Nl, identifier start, decimal 25 */
5704 0x0007418A, /* 121 Nl, identifier start, decimal 0 */
5705 0x00000013, /* 122 Cs */
5706 0x00000012 /* 123 Co */
5709 const jschar js_uriReservedPlusPound_ucstr[] =
5710 {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
5711 const jschar js_uriUnescaped_ucstr[] =
5712 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
5713 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
5714 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
5715 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
5716 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
5717 '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
5720 * This table allows efficient testing for the regular expression \w which is
5721 * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z].
5723 const bool js_alnum[] = {
5724 /* 0 1 2 3 4 5 5 7 8 9 */
5725 /* 0 */ false, false, false, false, false, false, false, false, false, false,
5726 /* 1 */ false, false, false, false, false, false, false, false, false, false,
5727 /* 2 */ false, false, false, false, false, false, false, false, false, false,
5728 /* 3 */ false, false, false, false, false, false, false, false, false, false,
5729 /* 4 */ false, false, false, false, false, false, false, false, true, true,
5730 /* 5 */ true, true, true, true, true, true, true, true, false, false,
5731 /* 6 */ false, false, false, false, false, true, true, true, true, true,
5732 /* 7 */ true, true, true, true, true, true, true, true, true, true,
5733 /* 8 */ true, true, true, true, true, true, true, true, true, true,
5734 /* 9 */ true, false, false, false, false, true, false, true, true, true,
5735 /* 10 */ true, true, true, true, true, true, true, true, true, true,
5736 /* 11 */ true, true, true, true, true, true, true, true, true, true,
5737 /* 12 */ true, true, true, false, false, false, false, false
5740 #define URI_CHUNK 64U
5742 static inline bool
5743 TransferBufferToString(JSContext *cx, JSCharBuffer &cb, Value *rval)
5745 JSString *str = js_NewStringFromCharBuffer(cx, cb);
5746 if (!str)
5747 return false;
5748 rval->setString(str);
5749 return true;;
5753 * ECMA 3, 15.1.3 URI Handling Function Properties
5755 * The following are implementations of the algorithms
5756 * given in the ECMA specification for the hidden functions
5757 * 'Encode' and 'Decode'.
5759 static JSBool
5760 Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
5761 const jschar *unescapedSet2, Value *rval)
5763 size_t length, j, k, L;
5764 JSCharBuffer cb(cx);
5765 const jschar *chars;
5766 jschar c, c2;
5767 uint32 v;
5768 uint8 utf8buf[4];
5769 jschar hexBuf[4];
5770 static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
5772 str->getCharsAndLength(chars, length);
5773 if (length == 0) {
5774 rval->setString(cx->runtime->emptyString);
5775 return JS_TRUE;
5778 /* From this point the control must goto bad on failures. */
5779 hexBuf[0] = '%';
5780 hexBuf[3] = 0;
5781 for (k = 0; k < length; k++) {
5782 c = chars[k];
5783 if (js_strchr(unescapedSet, c) ||
5784 (unescapedSet2 && js_strchr(unescapedSet2, c))) {
5785 if (!cb.append(c))
5786 return JS_FALSE;
5787 } else {
5788 if ((c >= 0xDC00) && (c <= 0xDFFF)) {
5789 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5790 JSMSG_BAD_URI, NULL);
5791 return JS_FALSE;
5793 if (c < 0xD800 || c > 0xDBFF) {
5794 v = c;
5795 } else {
5796 k++;
5797 if (k == length) {
5798 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5799 JSMSG_BAD_URI, NULL);
5800 return JS_FALSE;
5802 c2 = chars[k];
5803 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
5804 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5805 JSMSG_BAD_URI, NULL);
5806 return JS_FALSE;
5808 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
5810 L = js_OneUcs4ToUtf8Char(utf8buf, v);
5811 for (j = 0; j < L; j++) {
5812 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
5813 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
5814 if (!cb.append(hexBuf, 3))
5815 return JS_FALSE;
5820 return TransferBufferToString(cx, cb, rval);
5823 static JSBool
5824 Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
5826 size_t length, start, k;
5827 JSCharBuffer cb(cx);
5828 const jschar *chars;
5829 jschar c, H;
5830 uint32 v;
5831 jsuint B;
5832 uint8 octets[4];
5833 intN j, n;
5835 str->getCharsAndLength(chars, length);
5836 if (length == 0) {
5837 rval->setString(cx->runtime->emptyString);
5838 return JS_TRUE;
5841 /* From this point the control must goto bad on failures. */
5842 for (k = 0; k < length; k++) {
5843 c = chars[k];
5844 if (c == '%') {
5845 start = k;
5846 if ((k + 2) >= length)
5847 goto report_bad_uri;
5848 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5849 goto report_bad_uri;
5850 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5851 k += 2;
5852 if (!(B & 0x80)) {
5853 c = (jschar)B;
5854 } else {
5855 n = 1;
5856 while (B & (0x80 >> n))
5857 n++;
5858 if (n == 1 || n > 4)
5859 goto report_bad_uri;
5860 octets[0] = (uint8)B;
5861 if (k + 3 * (n - 1) >= length)
5862 goto report_bad_uri;
5863 for (j = 1; j < n; j++) {
5864 k++;
5865 if (chars[k] != '%')
5866 goto report_bad_uri;
5867 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5868 goto report_bad_uri;
5869 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5870 if ((B & 0xC0) != 0x80)
5871 goto report_bad_uri;
5872 k += 2;
5873 octets[j] = (char)B;
5875 v = Utf8ToOneUcs4Char(octets, n);
5876 if (v >= 0x10000) {
5877 v -= 0x10000;
5878 if (v > 0xFFFFF)
5879 goto report_bad_uri;
5880 c = (jschar)((v & 0x3FF) + 0xDC00);
5881 H = (jschar)((v >> 10) + 0xD800);
5882 if (!cb.append(H))
5883 return JS_FALSE;
5884 } else {
5885 c = (jschar)v;
5888 if (js_strchr(reservedSet, c)) {
5889 if (!cb.append(chars + start, k - start + 1))
5890 return JS_FALSE;
5891 } else {
5892 if (!cb.append(c))
5893 return JS_FALSE;
5895 } else {
5896 if (!cb.append(c))
5897 return JS_FALSE;
5901 return TransferBufferToString(cx, cb, rval);
5903 report_bad_uri:
5904 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
5905 /* FALL THROUGH */
5907 return JS_FALSE;
5910 static JSBool
5911 str_decodeURI(JSContext *cx, uintN argc, Value *vp)
5913 JSString *str;
5915 str = ArgToRootedString(cx, argc, vp, 0);
5916 if (!str)
5917 return JS_FALSE;
5918 return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp);
5921 static JSBool
5922 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5924 JSString *str;
5926 str = ArgToRootedString(cx, argc, vp, 0);
5927 if (!str)
5928 return JS_FALSE;
5929 return Decode(cx, str, js_empty_ucstr, vp);
5932 static JSBool
5933 str_encodeURI(JSContext *cx, uintN argc, Value *vp)
5935 JSString *str;
5937 str = ArgToRootedString(cx, argc, vp, 0);
5938 if (!str)
5939 return JS_FALSE;
5940 return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr,
5941 vp);
5944 static JSBool
5945 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5947 JSString *str;
5949 str = ArgToRootedString(cx, argc, vp, 0);
5950 if (!str)
5951 return JS_FALSE;
5952 return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp);
5956 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
5957 * least 4 bytes long. Return the number of UTF-8 bytes of data written.
5960 js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)
5962 int utf8Length = 1;
5964 JS_ASSERT(ucs4Char <= 0x10FFFF);
5965 if (ucs4Char < 0x80) {
5966 *utf8Buffer = (uint8)ucs4Char;
5967 } else {
5968 int i;
5969 uint32 a = ucs4Char >> 11;
5970 utf8Length = 2;
5971 while (a) {
5972 a >>= 5;
5973 utf8Length++;
5975 i = utf8Length;
5976 while (--i) {
5977 utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
5978 ucs4Char >>= 6;
5980 *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
5982 return utf8Length;
5986 * Convert a utf8 character sequence into a UCS-4 character and return that
5987 * character. It is assumed that the caller already checked that the sequence
5988 * is valid.
5990 static uint32
5991 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
5993 uint32 ucs4Char;
5994 uint32 minucs4Char;
5995 /* from Unicode 3.1, non-shortest form is illegal */
5996 static const uint32 minucs4Table[] = {
5997 0x00000080, 0x00000800, 0x00010000
6000 JS_ASSERT(utf8Length >= 1 && utf8Length <= 4);
6001 if (utf8Length == 1) {
6002 ucs4Char = *utf8Buffer;
6003 JS_ASSERT(!(ucs4Char & 0x80));
6004 } else {
6005 JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) ==
6006 (0x100 - (1 << (8-utf8Length))));
6007 ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
6008 minucs4Char = minucs4Table[utf8Length-2];
6009 while (--utf8Length) {
6010 JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
6011 ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
6013 if (JS_UNLIKELY(ucs4Char < minucs4Char)) {
6014 ucs4Char = OVERLONG_UTF8;
6015 } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
6016 ucs4Char = 0xFFFD;
6019 return ucs4Char;
6022 #if defined DEBUG || defined JS_DUMP_CONSERVATIVE_GC_ROOTS
6024 JS_FRIEND_API(size_t)
6025 js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
6026 JSString *str, uint32 quote)
6028 const jschar *chars, *charsEnd;
6029 size_t n;
6030 const char *escape;
6031 char c;
6032 uintN u, hex, shift;
6033 enum {
6034 STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
6035 } state;
6037 JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
6038 JS_ASSERT_IF(buffer, bufferSize != 0);
6039 JS_ASSERT_IF(!buffer, bufferSize == 0);
6040 JS_ASSERT_IF(fp, !buffer);
6042 str->getCharsAndEnd(chars, charsEnd);
6043 n = 0;
6044 --bufferSize;
6045 state = FIRST_QUOTE;
6046 shift = 0;
6047 hex = 0;
6048 u = 0;
6049 c = 0; /* to quell GCC warnings */
6051 for (;;) {
6052 switch (state) {
6053 case STOP:
6054 goto stop;
6055 case FIRST_QUOTE:
6056 state = CHARS;
6057 goto do_quote;
6058 case LAST_QUOTE:
6059 state = STOP;
6060 do_quote:
6061 if (quote == 0)
6062 continue;
6063 c = (char)quote;
6064 break;
6065 case CHARS:
6066 if (chars == charsEnd) {
6067 state = LAST_QUOTE;
6068 continue;
6070 u = *chars++;
6071 if (u < ' ') {
6072 if (u != 0) {
6073 escape = strchr(js_EscapeMap, (int)u);
6074 if (escape) {
6075 u = escape[1];
6076 goto do_escape;
6079 goto do_hex_escape;
6081 if (u < 127) {
6082 if (u == quote || u == '\\')
6083 goto do_escape;
6084 c = (char)u;
6085 } else if (u < 0x100) {
6086 goto do_hex_escape;
6087 } else {
6088 shift = 16;
6089 hex = u;
6090 u = 'u';
6091 goto do_escape;
6093 break;
6094 do_hex_escape:
6095 shift = 8;
6096 hex = u;
6097 u = 'x';
6098 do_escape:
6099 c = '\\';
6100 state = ESCAPE_START;
6101 break;
6102 case ESCAPE_START:
6103 JS_ASSERT(' ' <= u && u < 127);
6104 c = (char)u;
6105 state = ESCAPE_MORE;
6106 break;
6107 case ESCAPE_MORE:
6108 if (shift == 0) {
6109 state = CHARS;
6110 continue;
6112 shift -= 4;
6113 u = 0xF & (hex >> shift);
6114 c = (char)(u + (u < 10 ? '0' : 'A' - 10));
6115 break;
6117 if (buffer) {
6118 if (n == bufferSize)
6119 break;
6120 buffer[n] = c;
6121 } else if (fp) {
6122 fputc(c, fp);
6124 n++;
6126 stop:
6127 if (buffer)
6128 buffer[n] = '\0';
6129 return n;
6132 #endif