Merge mozilla-central to tracemonkey.
[mozilla-central.git] / js / src / jsstr.cpp
blob0a393864bfdb5e7eb4752acaf9fa7ae39ef3ff0e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS string type implementation.
44 * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
45 * native methods store strings (possibly newborn) converted from their 'this'
46 * parameter and arguments on the stack: 'this' conversions at argv[-1], arg
47 * conversions at their index (argv[0], argv[1]). This is a legitimate method
48 * of rooting things that might lose their newborn root due to subsequent GC
49 * allocations in the same native method.
51 #include <stdlib.h>
52 #include <string.h>
53 #include "jstypes.h"
54 #include "jsstdint.h"
55 #include "jsutil.h"
56 #include "jshash.h"
57 #include "jsprf.h"
58 #include "jsapi.h"
59 #include "jsarray.h"
60 #include "jsatom.h"
61 #include "jsbool.h"
62 #include "jsbuiltins.h"
63 #include "jscntxt.h"
64 #include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
65 #include "jsgc.h"
66 #include "jsinterp.h"
67 #include "jslock.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsopcode.h"
71 #include "jsregexp.h"
72 #include "jsscope.h"
73 #include "jsstaticcheck.h"
74 #include "jsstr.h"
75 #include "jsbit.h"
76 #include "jsvector.h"
77 #include "jsversion.h"
79 #include "jscntxtinlines.h"
80 #include "jsinterpinlines.h"
81 #include "jsobjinlines.h"
82 #include "jsregexpinlines.h"
83 #include "jsstrinlines.h"
84 #include "jsautooplen.h" // generated headers last
86 using namespace js;
87 using namespace js::gc;
89 JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
90 JS_STATIC_ASSERT(JSString::MAX_LENGTH <= JSVAL_INT_MAX);
92 const jschar *
93 js_GetStringChars(JSContext *cx, JSString *str)
95 if (!js_MakeStringImmutable(cx, str))
96 return NULL;
97 return str->flatChars();
100 static JS_ALWAYS_INLINE size_t
101 RopeCapacityFor(size_t length)
103 static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
106 * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
107 * next power of 2. This is similar to what we do with arrays; see
108 * JSObject::ensureDenseArrayElements.
110 if (length > ROPE_DOUBLING_MAX)
111 return length + (length / 8);
112 return RoundUpPow2(length);
115 static JS_ALWAYS_INLINE jschar *
116 AllocChars(JSContext *maybecx, size_t wholeCapacity)
118 /* +1 for the null char at the end. */
119 JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
120 size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
121 if (maybecx)
122 return (jschar *)maybecx->malloc(bytes);
123 return (jschar *)js_malloc(bytes);
126 const jschar *
127 JSString::flatten(JSContext *maybecx)
129 JS_ASSERT(isRope());
132 * Perform a depth-first dag traversal, splatting each node's characters
133 * into a contiguous buffer. Visit each rope node three times:
134 * 1. record position in the buffer and recurse into left child;
135 * 2. recurse into the right child;
136 * 3. transform the node into a dependent string.
137 * To avoid maintaining a stack, tree nodes are mutated to indicate how
138 * many times they have been visited. Since ropes can be dags, a node may
139 * be encountered multiple times during traversal. However, step 3 above
140 * leaves a valid dependent string, so everythings works out. This
141 * algorithm is homomorphic to TypedMarker(JSTracer *, JSString *).
143 * While ropes avoid all sorts of quadratic cases with string
144 * concatenation, they can't help when ropes are immediately flattened.
145 * One idiomatic case that we'd like to keep linear (and has traditionally
146 * been linear in SM and other JS engines) is:
148 * while (...) {
149 * s += ...
150 * s.flatten
153 * To do this, when the buffer for a to-be-flattened rope is allocated, the
154 * allocation size is rounded up. Then, if the resulting flat string is the
155 * left-hand side of a new rope that gets flattened and there is enough
156 * capacity, the rope is flattened into the same buffer, thereby avoiding
157 * copying the left-hand side. Clearing the 'extensible' bit turns off this
158 * optimization. This is necessary, e.g., when the JSAPI hands out the raw
159 * null-terminated char array of a flat string.
161 const size_t wholeLength = length();
162 size_t wholeCapacity;
163 jschar *wholeChars;
164 JSString *str = this;
165 jschar *pos;
167 if (u.left->isExtensible() && u.left->s.capacity >= wholeLength) {
168 wholeCapacity = u.left->s.capacity;
169 wholeChars = const_cast<jschar *>(u.left->u.chars);
170 pos = wholeChars + u.left->length();
171 u.left->finishTraversalConversion(this, wholeChars, pos);
172 goto visit_right_child;
175 wholeCapacity = RopeCapacityFor(wholeLength);
176 wholeChars = AllocChars(maybecx, wholeCapacity);
177 if (!wholeChars)
178 return NULL;
179 pos = wholeChars;
180 first_visit_node: {
181 JSString *left = str->u.left; /* Read before clobbered. */
182 str->u.chars = pos;
183 if (left->isRope()) {
184 left->s.parent = str; /* Return to this when 'left' done, */
185 left->lengthAndFlags = 0x200; /* but goto visit_right_child. */
186 str = left;
187 goto first_visit_node;
189 size_t len = left->length();
190 PodCopy(pos, left->u.chars, len);
191 pos += len;
193 visit_right_child: {
194 JSString *right = str->s.right;
195 if (right->isRope()) {
196 right->s.parent = str; /* Return to this node when 'right' done, */
197 right->lengthAndFlags = 0x300; /* but goto finish_node. */
198 str = right;
199 goto first_visit_node;
201 size_t len = right->length();
202 PodCopy(pos, right->u.chars, len);
203 pos += len;
205 finish_node: {
206 if (str == this) {
207 JS_ASSERT(pos == wholeChars + wholeLength);
208 *pos = '\0';
209 initFlatExtensible(wholeChars, wholeLength, wholeCapacity);
210 return wholeChars;
212 size_t progress = str->lengthAndFlags; /* Read before clobbered. */
213 JSString *parent = str->s.parent;
214 str->finishTraversalConversion(this, wholeChars, pos);
215 str = parent;
216 if (progress == 0x200)
217 goto visit_right_child;
218 goto finish_node;
222 JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8);
223 JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
224 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
227 #ifdef JS_TRACER
229 JSBool JS_FASTCALL
230 js_Flatten(JSContext *cx, JSString* str)
232 return !!str->flatten(cx);
234 JS_DEFINE_CALLINFO_2(extern, BOOL, js_Flatten, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY)
236 #endif /* !JS_TRACER */
238 JSString * JS_FASTCALL
239 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
241 size_t leftLen = left->length();
242 if (leftLen == 0)
243 return right;
245 size_t rightLen = right->length();
246 if (rightLen == 0)
247 return left;
249 size_t wholeLength = leftLen + rightLen;
251 if (JSShortString::fitsIntoShortString(wholeLength)) {
252 JSShortString *shortStr = js_NewGCShortString(cx);
253 if (!shortStr)
254 return NULL;
255 const jschar *leftChars = left->getChars(cx);
256 if (!leftChars)
257 return NULL;
258 const jschar *rightChars = right->getChars(cx);
259 if (!rightChars)
260 return NULL;
262 jschar *buf = shortStr->init(wholeLength);
263 js_short_strncpy(buf, leftChars, leftLen);
264 js_short_strncpy(buf + leftLen, rightChars, rightLen);
265 buf[wholeLength] = 0;
266 return shortStr->header();
269 if (wholeLength > JSString::MAX_LENGTH) {
270 if (JS_ON_TRACE(cx)) {
271 if (!CanLeaveTrace(cx))
272 return NULL;
273 LeaveTrace(cx);
275 js_ReportAllocationOverflow(cx);
276 return NULL;
279 JSString *newRoot = js_NewGCString(cx);
280 if (!newRoot)
281 return NULL;
283 newRoot->initRopeNode(left, right, wholeLength);
284 return newRoot;
287 const jschar *
288 JSString::undepend(JSContext *cx)
290 size_t n, size;
291 jschar *s;
293 if (!ensureLinear(cx))
294 return NULL;
296 if (isDependent()) {
297 n = dependentLength();
298 size = (n + 1) * sizeof(jschar);
299 s = (jschar *) cx->malloc(size);
300 if (!s)
301 return NULL;
303 js_strncpy(s, dependentChars(), n);
304 s[n] = 0;
305 initFlat(s, n);
307 #ifdef DEBUG
309 JSRuntime *rt = cx->runtime;
310 JS_RUNTIME_UNMETER(rt, liveDependentStrings);
311 JS_RUNTIME_UNMETER(rt, totalDependentStrings);
312 JS_LOCK_RUNTIME_VOID(rt,
313 (rt->strdepLengthSum -= (double)n,
314 rt->strdepLengthSquaredSum -= (double)n * (double)n));
316 #endif
319 return flatChars();
322 JSBool
323 js_MakeStringImmutable(JSContext *cx, JSString *str)
326 * Flattening a rope may result in a dependent string, so we need to flatten
327 * before undepending the string.
329 if (!str->isFlat() && !str->undepend(cx)) {
330 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
331 return JS_FALSE;
333 str->flatClearExtensible();
334 return JS_TRUE;
337 static JSLinearString *
338 ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg)
340 if (arg >= argc)
341 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
342 vp += 2 + arg;
344 if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp))
345 return NULL;
347 JSLinearString *str;
348 if (vp->isString()) {
349 str = vp->toString()->ensureLinear(cx);
350 } else if (vp->isBoolean()) {
351 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
352 (int)vp->toBoolean()]);
353 } else if (vp->isNull()) {
354 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
355 } else if (vp->isUndefined()) {
356 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
358 else {
359 str = NumberToString(cx, vp->toNumber());
360 if (!str)
361 return NULL;
362 vp->setString(str);
364 return str;
368 * Forward declarations for URI encode/decode and helper routines
370 static JSBool
371 str_decodeURI(JSContext *cx, uintN argc, Value *vp);
373 static JSBool
374 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp);
376 static JSBool
377 str_encodeURI(JSContext *cx, uintN argc, Value *vp);
379 static JSBool
380 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp);
382 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
384 static uint32
385 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
388 * Contributions from the String class to the set of methods defined for the
389 * global object. escape and unescape used to be defined in the Mocha library,
390 * but as ECMA decided to spec them, they've been moved to the core engine
391 * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
392 * characters by unescape.)
396 * Stuff to emulate the old libmocha escape, which took a second argument
397 * giving the type of escape to perform. Retained for compatibility, and
398 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
401 #define URL_XALPHAS ((uint8) 1)
402 #define URL_XPALPHAS ((uint8) 2)
403 #define URL_PATH ((uint8) 4)
405 static const uint8 urlCharType[256] =
406 /* Bit 0 xalpha -- the alphas
407 * Bit 1 xpalpha -- as xalpha but
408 * converts spaces to plus and plus to %20
409 * Bit 2 ... path -- as xalphas but doesn't escape '/'
411 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
412 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
413 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
414 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
415 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
416 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
417 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
418 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
419 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
420 0, };
422 /* This matches the ECMA escape set when mask is 7 (default.) */
424 #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
426 /* See ECMA-262 Edition 3 B.2.1 */
427 JSBool
428 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
430 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
431 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
433 jsint mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
434 if (argc > 1) {
435 double d;
436 if (!ValueToNumber(cx, argv[1], &d))
437 return JS_FALSE;
438 if (!JSDOUBLE_IS_FINITE(d) ||
439 (mask = (jsint)d) != d ||
440 mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
442 char numBuf[12];
443 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
444 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
445 JSMSG_BAD_STRING_MASK, numBuf);
446 return JS_FALSE;
450 JSLinearString *str = ArgToRootedString(cx, argc, argv - 2, 0);
451 if (!str)
452 return JS_FALSE;
454 size_t length = str->length();
455 const jschar *chars = str->chars();
457 /* Take a first pass and see how big the result string will need to be. */
458 size_t newlength = length;
459 for (size_t i = 0; i < length; i++) {
460 jschar ch;
461 if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
462 continue;
463 if (ch < 256) {
464 if (mask == URL_XPALPHAS && ch == ' ')
465 continue; /* The character will be encoded as '+' */
466 newlength += 2; /* The character will be encoded as %XX */
467 } else {
468 newlength += 5; /* The character will be encoded as %uXXXX */
472 * This overflow test works because newlength is incremented by at
473 * most 5 on each iteration.
475 if (newlength < length) {
476 js_ReportAllocationOverflow(cx);
477 return JS_FALSE;
481 if (newlength >= ~(size_t)0 / sizeof(jschar)) {
482 js_ReportAllocationOverflow(cx);
483 return JS_FALSE;
486 jschar *newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
487 if (!newchars)
488 return JS_FALSE;
489 size_t i, ni;
490 for (i = 0, ni = 0; i < length; i++) {
491 jschar ch;
492 if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
493 newchars[ni++] = ch;
494 } else if (ch < 256) {
495 if (mask == URL_XPALPHAS && ch == ' ') {
496 newchars[ni++] = '+'; /* convert spaces to pluses */
497 } else {
498 newchars[ni++] = '%';
499 newchars[ni++] = digits[ch >> 4];
500 newchars[ni++] = digits[ch & 0xF];
502 } else {
503 newchars[ni++] = '%';
504 newchars[ni++] = 'u';
505 newchars[ni++] = digits[ch >> 12];
506 newchars[ni++] = digits[(ch & 0xF00) >> 8];
507 newchars[ni++] = digits[(ch & 0xF0) >> 4];
508 newchars[ni++] = digits[ch & 0xF];
511 JS_ASSERT(ni == newlength);
512 newchars[newlength] = 0;
514 JSString *retstr = js_NewString(cx, newchars, newlength);
515 if (!retstr) {
516 cx->free(newchars);
517 return JS_FALSE;
519 rval->setString(retstr);
520 return JS_TRUE;
522 #undef IS_OK
524 static JSBool
525 str_escape(JSContext *cx, uintN argc, Value *vp)
527 JSObject *obj = ComputeThisFromVp(cx, vp);
528 return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
531 /* See ECMA-262 Edition 3 B.2.2 */
532 static JSBool
533 str_unescape(JSContext *cx, uintN argc, Value *vp)
535 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
536 if (!str)
537 return false;
539 size_t length = str->length();
540 const jschar *chars = str->chars();
542 /* Don't bother allocating less space for the new string. */
543 jschar *newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
544 if (!newchars)
545 return false;
546 size_t ni = 0, i = 0;
547 while (i < length) {
548 jschar ch = chars[i++];
549 if (ch == '%') {
550 if (i + 1 < length &&
551 JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
553 ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
554 i += 2;
555 } else if (i + 4 < length && chars[i] == 'u' &&
556 JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
557 JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
559 ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
560 + JS7_UNHEX(chars[i + 2])) << 4)
561 + JS7_UNHEX(chars[i + 3])) << 4)
562 + JS7_UNHEX(chars[i + 4]);
563 i += 5;
566 newchars[ni++] = ch;
568 newchars[ni] = 0;
570 JSString *retstr = js_NewString(cx, newchars, ni);
571 if (!retstr) {
572 cx->free(newchars);
573 return JS_FALSE;
575 vp->setString(retstr);
576 return JS_TRUE;
579 #if JS_HAS_UNEVAL
580 static JSBool
581 str_uneval(JSContext *cx, uintN argc, Value *vp)
583 JSString *str;
585 str = js_ValueToSource(cx, argc != 0 ? vp[2] : UndefinedValue());
586 if (!str)
587 return JS_FALSE;
588 vp->setString(str);
589 return JS_TRUE;
591 #endif
593 const char js_escape_str[] = "escape";
594 const char js_unescape_str[] = "unescape";
595 #if JS_HAS_UNEVAL
596 const char js_uneval_str[] = "uneval";
597 #endif
598 const char js_decodeURI_str[] = "decodeURI";
599 const char js_encodeURI_str[] = "encodeURI";
600 const char js_decodeURIComponent_str[] = "decodeURIComponent";
601 const char js_encodeURIComponent_str[] = "encodeURIComponent";
603 static JSFunctionSpec string_functions[] = {
604 JS_FN(js_escape_str, str_escape, 1,0),
605 JS_FN(js_unescape_str, str_unescape, 1,0),
606 #if JS_HAS_UNEVAL
607 JS_FN(js_uneval_str, str_uneval, 1,0),
608 #endif
609 JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
610 JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
611 JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
612 JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
614 JS_FS_END
617 jschar js_empty_ucstr[] = {0};
618 JSSubString js_EmptySubString = {0, js_empty_ucstr};
620 static JSBool
621 str_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
623 JSString *str;
625 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
626 if (obj->getClass() == &js_StringClass) {
627 /* Follow ECMA-262 by fetching intrinsic length of our string. */
628 str = obj->getPrimitiveThis().toString();
629 } else {
630 /* Preserve compatibility: convert obj to a string primitive. */
631 str = js_ValueToString(cx, ObjectValue(*obj));
632 if (!str)
633 return JS_FALSE;
636 vp->setInt32(str->length());
639 return JS_TRUE;
642 #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
644 static JSBool
645 str_enumerate(JSContext *cx, JSObject *obj)
647 JSString *str, *str1;
648 size_t i, length;
650 str = obj->getPrimitiveThis().toString();
652 length = str->length();
653 for (i = 0; i < length; i++) {
654 str1 = js_NewDependentString(cx, str, i, 1);
655 if (!str1)
656 return JS_FALSE;
657 if (!obj->defineProperty(cx, INT_TO_JSID(i), StringValue(str1),
658 PropertyStub, PropertyStub,
659 STRING_ELEMENT_ATTRS)) {
660 return JS_FALSE;
664 return obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
665 UndefinedValue(), NULL, NULL,
666 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED);
669 static JSBool
670 str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
671 JSObject **objp)
673 if (!JSID_IS_INT(id))
674 return JS_TRUE;
676 JSString *str = obj->getPrimitiveThis().toString();
678 jsint slot = JSID_TO_INT(id);
679 if ((size_t)slot < str->length()) {
680 JSString *str1 = JSString::getUnitString(cx, str, size_t(slot));
681 if (!str1)
682 return JS_FALSE;
683 if (!obj->defineProperty(cx, id, StringValue(str1), NULL, NULL,
684 STRING_ELEMENT_ATTRS)) {
685 return JS_FALSE;
687 *objp = obj;
689 return JS_TRUE;
692 Class js_StringClass = {
693 js_String_str,
694 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
695 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
696 PropertyStub, /* addProperty */
697 PropertyStub, /* delProperty */
698 str_getProperty,
699 PropertyStub, /* setProperty */
700 str_enumerate,
701 (JSResolveOp)str_resolve,
702 ConvertStub
705 #define NORMALIZE_THIS(cx,vp,str) \
706 JS_BEGIN_MACRO \
707 if (vp[1].isString()) { \
708 str = vp[1].toString(); \
709 } else { \
710 str = NormalizeThis(cx, vp); \
711 if (!str) \
712 return JS_FALSE; \
714 JS_END_MACRO
716 static JSString *
717 NormalizeThis(JSContext *cx, Value *vp)
719 if (vp[1].isNullOrUndefined() && !ComputeThisFromVp(cx, vp))
720 return NULL;
723 * String.prototype.{toString,toSource,valueOf} throw a TypeError if the
724 * this-argument is not a string or a String object. So those methods use
725 * js::GetPrimitiveThis which provides that behavior.
727 * By standard, the rest of the String methods must ToString the
728 * this-argument rather than throw a TypeError. So those methods use
729 * NORMALIZE_THIS (and thus NormalizeThis) instead.
731 if (vp[1].isObject()) {
732 JSObject *obj = &vp[1].toObject();
733 if (obj->getClass() == &js_StringClass) {
734 vp[1] = obj->getPrimitiveThis();
735 return vp[1].toString();
739 JSString *str = js_ValueToString(cx, vp[1]);
740 if (!str)
741 return NULL;
742 vp[1].setString(str);
743 return str;
746 #if JS_HAS_TOSOURCE
749 * String.prototype.quote is generic (as are most string methods), unlike
750 * toSource, toString, and valueOf.
752 static JSBool
753 str_quote(JSContext *cx, uintN argc, Value *vp)
755 JSString *str;
757 NORMALIZE_THIS(cx, vp, str);
758 str = js_QuoteString(cx, str, '"');
759 if (!str)
760 return JS_FALSE;
761 vp->setString(str);
762 return JS_TRUE;
765 static JSBool
766 str_toSource(JSContext *cx, uintN argc, Value *vp)
768 JSString *str;
769 if (!GetPrimitiveThis(cx, vp, &str))
770 return false;
772 str = js_QuoteString(cx, str, '"');
773 if (!str)
774 return false;
776 char buf[16];
777 size_t j = JS_snprintf(buf, sizeof buf, "(new String(");
779 size_t k = str->length();
780 const jschar *s = str->getChars(cx);
781 if (!s)
782 return false;
784 size_t n = j + k + 2;
785 jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
786 if (!t)
787 return false;
789 size_t i;
790 for (i = 0; i < j; i++)
791 t[i] = buf[i];
792 for (j = 0; j < k; i++, j++)
793 t[i] = s[j];
794 t[i++] = ')';
795 t[i++] = ')';
796 t[i] = 0;
798 str = js_NewString(cx, t, n);
799 if (!str) {
800 cx->free(t);
801 return false;
803 vp->setString(str);
804 return true;
807 #endif /* JS_HAS_TOSOURCE */
809 JSBool
810 js_str_toString(JSContext *cx, uintN argc, Value *vp)
812 JSString *str;
813 if (!GetPrimitiveThis(cx, vp, &str))
814 return false;
815 vp->setString(str);
816 return true;
820 * Java-like string native methods.
823 JS_ALWAYS_INLINE bool
824 ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
826 if (v.isInt32()) {
827 *out = v.toInt32();
828 } else {
829 double d;
831 if (!ValueToNumber(cx, v, &d))
832 return false;
834 d = js_DoubleToInteger(d);
835 if (d > INT32_MAX)
836 *out = INT32_MAX;
837 else if (d < INT32_MIN)
838 *out = INT32_MIN;
839 else
840 *out = int32(d);
843 return true;
846 static JSBool
847 str_substring(JSContext *cx, uintN argc, Value *vp)
849 JSString *str;
850 int32 length, begin, end;
852 NORMALIZE_THIS(cx, vp, str);
854 if (argc > 0) {
855 end = length = int32(str->length());
857 if (!ValueToIntegerRange(cx, vp[2], &begin))
858 return false;
860 if (begin < 0)
861 begin = 0;
862 else if (begin > length)
863 begin = length;
865 if (argc > 1 && !vp[3].isUndefined()) {
866 if (!ValueToIntegerRange(cx, vp[3], &end))
867 return false;
869 if (end > length) {
870 end = length;
871 } else {
872 if (end < 0)
873 end = 0;
874 if (end < begin) {
875 int32_t tmp = begin;
876 begin = end;
877 end = tmp;
882 str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
883 if (!str)
884 return false;
887 vp->setString(str);
888 return true;
891 JSString* JS_FASTCALL
892 js_toLowerCase(JSContext *cx, JSString *str)
894 size_t n = str->length();
895 const jschar *s = str->getChars(cx);
896 if (!s)
897 return NULL;
899 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
900 if (!news)
901 return NULL;
902 for (size_t i = 0; i < n; i++)
903 news[i] = JS_TOLOWER(s[i]);
904 news[n] = 0;
905 str = js_NewString(cx, news, n);
906 if (!str) {
907 cx->free(news);
908 return NULL;
910 return str;
913 static JSBool
914 str_toLowerCase(JSContext *cx, uintN argc, Value *vp)
916 JSString *str;
918 NORMALIZE_THIS(cx, vp, str);
919 str = js_toLowerCase(cx, str);
920 if (!str)
921 return JS_FALSE;
922 vp->setString(str);
923 return JS_TRUE;
926 static JSBool
927 str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp)
929 JSString *str;
932 * Forcefully ignore the first (or any) argument and return toLowerCase(),
933 * ECMA has reserved that argument, presumably for defining the locale.
935 if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
936 NORMALIZE_THIS(cx, vp, str);
937 return cx->localeCallbacks->localeToLowerCase(cx, str, Jsvalify(vp));
939 return str_toLowerCase(cx, 0, vp);
942 JSString* JS_FASTCALL
943 js_toUpperCase(JSContext *cx, JSString *str)
945 size_t n = str->length();
946 const jschar *s = str->getChars(cx);
947 if (!s)
948 return NULL;
949 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
950 if (!news)
951 return NULL;
952 for (size_t i = 0; i < n; i++)
953 news[i] = JS_TOUPPER(s[i]);
954 news[n] = 0;
955 str = js_NewString(cx, news, n);
956 if (!str) {
957 cx->free(news);
958 return NULL;
960 return str;
963 static JSBool
964 str_toUpperCase(JSContext *cx, uintN argc, Value *vp)
966 JSString *str;
968 NORMALIZE_THIS(cx, vp, str);
969 str = js_toUpperCase(cx, str);
970 if (!str)
971 return JS_FALSE;
972 vp->setString(str);
973 return JS_TRUE;
976 static JSBool
977 str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp)
979 JSString *str;
982 * Forcefully ignore the first (or any) argument and return toUpperCase(),
983 * ECMA has reserved that argument, presumably for defining the locale.
985 if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
986 NORMALIZE_THIS(cx, vp, str);
987 return cx->localeCallbacks->localeToUpperCase(cx, str, Jsvalify(vp));
989 return str_toUpperCase(cx, 0, vp);
992 static JSBool
993 str_localeCompare(JSContext *cx, uintN argc, Value *vp)
995 JSString *str, *thatStr;
997 NORMALIZE_THIS(cx, vp, str);
998 if (argc == 0) {
999 vp->setInt32(0);
1000 } else {
1001 thatStr = js_ValueToString(cx, vp[2]);
1002 if (!thatStr)
1003 return JS_FALSE;
1004 if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
1005 vp[2].setString(thatStr);
1006 return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp));
1008 int32 result;
1009 if (!CompareStrings(cx, str, thatStr, &result))
1010 return JS_FALSE;
1011 vp->setInt32(result);
1013 return JS_TRUE;
1016 JSBool
1017 js_str_charAt(JSContext *cx, uintN argc, Value *vp)
1019 JSString *str;
1020 jsint i;
1021 jsdouble d;
1023 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1024 str = vp[1].toString();
1025 i = vp[2].toInt32();
1026 if ((size_t)i >= str->length())
1027 goto out_of_range;
1028 } else {
1029 NORMALIZE_THIS(cx, vp, str);
1031 if (argc == 0) {
1032 d = 0.0;
1033 } else {
1034 if (!ValueToNumber(cx, vp[2], &d))
1035 return JS_FALSE;
1036 d = js_DoubleToInteger(d);
1039 if (d < 0 || str->length() <= d)
1040 goto out_of_range;
1041 i = (jsint) d;
1044 str = JSString::getUnitString(cx, str, size_t(i));
1045 if (!str)
1046 return JS_FALSE;
1047 vp->setString(str);
1048 return JS_TRUE;
1050 out_of_range:
1051 vp->setString(cx->runtime->emptyString);
1052 return JS_TRUE;
1055 JSBool
1056 js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp)
1058 JSString *str;
1059 jsint i;
1060 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1061 str = vp[1].toString();
1062 i = vp[2].toInt32();
1063 if ((size_t)i >= str->length())
1064 goto out_of_range;
1065 } else {
1066 NORMALIZE_THIS(cx, vp, str);
1068 double d;
1069 if (argc == 0) {
1070 d = 0.0;
1071 } else {
1072 if (!ValueToNumber(cx, vp[2], &d))
1073 return false;
1074 d = js_DoubleToInteger(d);
1077 if (d < 0 || str->length() <= d)
1078 goto out_of_range;
1079 i = (jsint) d;
1082 const jschar *chars;
1083 chars = str->getChars(cx);
1084 if (!chars)
1085 return false;
1087 vp->setInt32(chars[i]);
1088 return true;
1090 out_of_range:
1091 vp->setDouble(js_NaN);
1092 return true;
1095 jsint
1096 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1097 const jschar *pat, jsuint patlen)
1099 uint8 skip[sBMHCharSetSize];
1101 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1102 for (jsuint i = 0; i < sBMHCharSetSize; i++)
1103 skip[i] = (uint8)patlen;
1104 jsuint m = patlen - 1;
1105 for (jsuint i = 0; i < m; i++) {
1106 jschar c = pat[i];
1107 if (c >= sBMHCharSetSize)
1108 return sBMHBadPattern;
1109 skip[c] = (uint8)(m - i);
1111 jschar c;
1112 for (jsuint k = m;
1113 k < textlen;
1114 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1115 for (jsuint i = k, j = m; ; i--, j--) {
1116 if (text[i] != pat[j])
1117 break;
1118 if (j == 0)
1119 return static_cast<jsint>(i); /* safe: max string size */
1122 return -1;
1125 struct MemCmp {
1126 typedef jsuint Extent;
1127 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) {
1128 return (patlen - 1) * sizeof(jschar);
1130 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1131 return memcmp(p, t, extent) == 0;
1135 struct ManualCmp {
1136 typedef const jschar *Extent;
1137 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) {
1138 return pat + patlen;
1140 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1141 for (; p != extent; ++p, ++t) {
1142 if (*p != *t)
1143 return false;
1145 return true;
1149 template <class InnerMatch>
1150 static jsint
1151 UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
1153 JS_ASSERT(patlen > 0 && textlen > 0);
1154 const jschar *textend = text + textlen - (patlen - 1);
1155 const jschar p0 = *pat;
1156 const jschar *const patNext = pat + 1;
1157 const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
1158 uint8 fixup;
1160 const jschar *t = text;
1161 switch ((textend - t) & 7) {
1162 case 0: if (*t++ == p0) { fixup = 8; goto match; }
1163 case 7: if (*t++ == p0) { fixup = 7; goto match; }
1164 case 6: if (*t++ == p0) { fixup = 6; goto match; }
1165 case 5: if (*t++ == p0) { fixup = 5; goto match; }
1166 case 4: if (*t++ == p0) { fixup = 4; goto match; }
1167 case 3: if (*t++ == p0) { fixup = 3; goto match; }
1168 case 2: if (*t++ == p0) { fixup = 2; goto match; }
1169 case 1: if (*t++ == p0) { fixup = 1; goto match; }
1171 while (t != textend) {
1172 if (t[0] == p0) { t += 1; fixup = 8; goto match; }
1173 if (t[1] == p0) { t += 2; fixup = 7; goto match; }
1174 if (t[2] == p0) { t += 3; fixup = 6; goto match; }
1175 if (t[3] == p0) { t += 4; fixup = 5; goto match; }
1176 if (t[4] == p0) { t += 5; fixup = 4; goto match; }
1177 if (t[5] == p0) { t += 6; fixup = 3; goto match; }
1178 if (t[6] == p0) { t += 7; fixup = 2; goto match; }
1179 if (t[7] == p0) { t += 8; fixup = 1; goto match; }
1180 t += 8;
1181 continue;
1182 do {
1183 if (*t++ == p0) {
1184 match:
1185 if (!InnerMatch::match(patNext, t, extent))
1186 goto failed_match;
1187 return t - text - 1;
1189 failed_match:;
1190 } while (--fixup > 0);
1192 return -1;
1195 static JS_ALWAYS_INLINE jsint
1196 StringMatch(const jschar *text, jsuint textlen,
1197 const jschar *pat, jsuint patlen)
1199 if (patlen == 0)
1200 return 0;
1201 if (textlen < patlen)
1202 return -1;
1204 #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
1206 * Given enough registers, the unrolled loop below is faster than the
1207 * following loop. 32-bit x86 does not have enough registers.
1209 if (patlen == 1) {
1210 const jschar p0 = *pat;
1211 for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1212 if (*c == p0)
1213 return c - text;
1215 return -1;
1217 #endif
1220 * If the text or pattern string is short, BMH will be more expensive than
1221 * the basic linear scan due to initialization cost and a more complex loop
1222 * body. While the correct threshold is input-dependent, we can make a few
1223 * conservative observations:
1224 * - When |textlen| is "big enough", the initialization time will be
1225 * proportionally small, so the worst-case slowdown is minimized.
1226 * - When |patlen| is "too small", even the best case for BMH will be
1227 * slower than a simple scan for large |textlen| due to the more complex
1228 * loop body of BMH.
1229 * From this, the values for "big enough" and "too small" are determined
1230 * empirically. See bug 526348.
1232 if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
1233 jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1234 if (index != sBMHBadPattern)
1235 return index;
1239 * For big patterns with large potential overlap we want the SIMD-optimized
1240 * speed of memcmp. For small patterns, a simple loop is faster.
1242 * FIXME: Linux memcmp performance is sad and the manual loop is faster.
1244 return
1245 #if !defined(__linux__)
1246 patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
1248 #endif
1249 UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
1252 static const size_t sRopeMatchThresholdRatioLog2 = 5;
1255 * RopeMatch takes the text to search, the patern to search for in the text.
1256 * RopeMatch returns false on OOM and otherwise returns the match index through
1257 * the 'match' outparam (-1 for not found).
1259 static bool
1260 RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, jsint *match)
1262 JS_ASSERT(textstr->isRope());
1264 if (patlen == 0) {
1265 *match = 0;
1266 return true;
1268 if (textstr->length() < patlen) {
1269 *match = -1;
1270 return true;
1274 * List of leaf nodes in the rope. If we run out of memory when trying to
1275 * append to this list, we can still fall back to StringMatch, so use the
1276 * system allocator so we don't report OOM in that case.
1278 Vector<JSString *, 16, SystemAllocPolicy> strs;
1281 * We don't want to do rope matching if there is a poor node-to-char ratio,
1282 * since this means spending a lot of time in the match loop below. We also
1283 * need to build the list of leaf nodes. Do both here: iterate over the
1284 * nodes so long as there are not too many.
1287 size_t textstrlen = textstr->length();
1288 size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
1289 StringSegmentRange r(cx);
1290 if (!r.init(textstr))
1291 return false;
1292 while (!r.empty()) {
1293 if (threshold-- == 0 || !strs.append(r.front())) {
1294 const jschar *chars = textstr->getChars(cx);
1295 if (!chars)
1296 return false;
1297 *match = StringMatch(chars, textstrlen, pat, patlen);
1298 return true;
1300 if (!r.popFront())
1301 return false;
1305 /* Absolute offset from the beginning of the logical string textstr. */
1306 jsint pos = 0;
1308 // TODO: consider branching to a simple loop if patlen == 1
1310 for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
1311 /* First try to match without spanning two nodes. */
1312 JSString *outer = *outerp;
1313 const jschar *chars = outer->nonRopeChars();
1314 size_t len = outer->length();
1315 jsint matchResult = StringMatch(chars, len, pat, patlen);
1316 if (matchResult != -1) {
1317 *match = pos + matchResult;
1318 return true;
1321 /* Test the overlap. */
1322 JSString **innerp = outerp;
1325 * Start searching at the first place where StringMatch wouldn't have
1326 * found the match.
1328 const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
1329 const jschar *const textend = chars + len;
1330 const jschar p0 = *pat;
1331 const jschar *const p1 = pat + 1;
1332 const jschar *const patend = pat + patlen;
1333 for (const jschar *t = text; t != textend; ) {
1334 if (*t++ != p0)
1335 continue;
1336 const jschar *ttend = textend;
1337 for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
1338 while (tt == ttend) {
1339 if (++innerp == strs.end()) {
1340 *match = -1;
1341 return true;
1343 JSString *inner = *innerp;
1344 tt = inner->nonRopeChars();
1345 ttend = tt + inner->length();
1347 if (*pp != *tt)
1348 goto break_continue;
1351 /* Matched! */
1352 *match = pos + (t - chars) - 1; /* -1 because of *t++ above */
1353 return true;
1355 break_continue:;
1358 pos += len;
1361 *match = -1;
1362 return true;
1365 static JSBool
1366 str_indexOf(JSContext *cx, uintN argc, Value *vp)
1369 JSString *str;
1370 NORMALIZE_THIS(cx, vp, str);
1372 JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0);
1373 if (!patstr)
1374 return false;
1376 jsuint textlen = str->length();
1377 const jschar *text = str->getChars(cx);
1378 if (!text)
1379 return false;
1381 jsuint patlen = patstr->length();
1382 const jschar *pat = patstr->chars();
1384 jsuint start;
1385 if (argc > 1) {
1386 if (vp[3].isInt32()) {
1387 jsint i = vp[3].toInt32();
1388 if (i <= 0) {
1389 start = 0;
1390 } else if (jsuint(i) > textlen) {
1391 start = textlen;
1392 textlen = 0;
1393 } else {
1394 start = i;
1395 text += start;
1396 textlen -= start;
1398 } else {
1399 jsdouble d;
1400 if (!ValueToNumber(cx, vp[3], &d))
1401 return JS_FALSE;
1402 d = js_DoubleToInteger(d);
1403 if (d <= 0) {
1404 start = 0;
1405 } else if (d > textlen) {
1406 start = textlen;
1407 textlen = 0;
1408 } else {
1409 start = (jsint)d;
1410 text += start;
1411 textlen -= start;
1414 } else {
1415 start = 0;
1418 jsint match = StringMatch(text, textlen, pat, patlen);
1419 vp->setInt32((match == -1) ? -1 : start + match);
1420 return true;
1423 static JSBool
1424 str_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
1426 JSString *textstr;
1427 NORMALIZE_THIS(cx, vp, textstr);
1428 size_t textlen = textstr->length();
1429 const jschar *text = textstr->getChars(cx);
1430 if (!text)
1431 return false;
1433 JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0);
1434 if (!patstr)
1435 return false;
1437 size_t patlen = patstr->length();
1438 const jschar *pat = patstr->chars();
1440 jsint i = textlen - patlen; // Start searching here
1441 if (i < 0) {
1442 vp->setInt32(-1);
1443 return true;
1446 if (argc > 1) {
1447 if (vp[3].isInt32()) {
1448 jsint j = vp[3].toInt32();
1449 if (j <= 0)
1450 i = 0;
1451 else if (j < i)
1452 i = j;
1453 } else {
1454 double d;
1455 if (!ValueToNumber(cx, vp[3], &d))
1456 return false;
1457 if (!JSDOUBLE_IS_NaN(d)) {
1458 d = js_DoubleToInteger(d);
1459 if (d <= 0)
1460 i = 0;
1461 else if (d < i)
1462 i = (jsint)d;
1467 if (patlen == 0) {
1468 vp->setInt32(i);
1469 return true;
1472 const jschar *t = text + i;
1473 const jschar *textend = text - 1;
1474 const jschar p0 = *pat;
1475 const jschar *patNext = pat + 1;
1476 const jschar *patEnd = pat + patlen;
1478 for (; t != textend; --t) {
1479 if (*t == p0) {
1480 const jschar *t1 = t + 1;
1481 for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1482 if (*t1 != *p1)
1483 goto break_continue;
1485 vp->setInt32(t - text);
1486 return true;
1488 break_continue:;
1491 vp->setInt32(-1);
1492 return true;
1495 static JSBool
1496 js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
1498 JSString *str;
1499 NORMALIZE_THIS(cx, vp, str);
1500 size_t length = str->length();
1501 const jschar *chars = str->getChars(cx);
1502 if (!chars)
1503 return false;
1505 size_t begin = 0;
1506 size_t end = length;
1508 if (trimLeft) {
1509 while (begin < length && JS_ISSPACE(chars[begin]))
1510 ++begin;
1513 if (trimRight) {
1514 while (end > begin && JS_ISSPACE(chars[end-1]))
1515 --end;
1518 str = js_NewDependentString(cx, str, begin, end - begin);
1519 if (!str)
1520 return false;
1522 vp->setString(str);
1523 return true;
1526 static JSBool
1527 str_trim(JSContext *cx, uintN argc, Value *vp)
1529 return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
1532 static JSBool
1533 str_trimLeft(JSContext *cx, uintN argc, Value *vp)
1535 return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
1538 static JSBool
1539 str_trimRight(JSContext *cx, uintN argc, Value *vp)
1541 return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
1545 * Perl-inspired string functions.
1548 /* Result of a successfully performed flat match. */
1549 class FlatMatch
1551 JSLinearString *patstr;
1552 const jschar *pat;
1553 size_t patlen;
1554 int32 match_;
1556 friend class RegExpGuard;
1558 public:
1559 FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
1560 JSString *pattern() const { return patstr; }
1561 size_t patternLength() const { return patlen; }
1564 * @note The match is -1 when the match is performed successfully,
1565 * but no match is found.
1567 int32 match() const { return match_; }
1570 /* A regexp and optional associated object. */
1571 class RegExpPair
1573 JSObject *reobj_;
1574 RegExp *re_;
1576 explicit RegExpPair(RegExp *re): re_(re) {}
1577 friend class RegExpGuard;
1579 public:
1580 /* @note May be null. */
1581 JSObject *reobj() const { return reobj_; }
1582 RegExp &re() const { JS_ASSERT(re_); return *re_; }
1586 * RegExpGuard factors logic out of String regexp operations.
1588 * @param optarg Indicates in which argument position RegExp
1589 * flags will be found, if present. This is a Mozilla
1590 * extension and not part of any ECMA spec.
1592 class RegExpGuard
1594 RegExpGuard(const RegExpGuard &);
1595 void operator=(const RegExpGuard &);
1597 JSContext *cx;
1598 RegExpPair rep;
1599 FlatMatch fm;
1602 * Upper bound on the number of characters we are willing to potentially
1603 * waste on searching for RegExp meta-characters.
1605 static const size_t MAX_FLAT_PAT_LEN = 256;
1607 static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
1608 JSCharBuffer cb(cx);
1609 if (!cb.reserve(patstr->length()))
1610 return NULL;
1612 static const jschar ESCAPE_CHAR = '\\';
1613 const jschar *chars = patstr->chars();
1614 size_t len = patstr->length();
1615 for (const jschar *it = chars; it != chars + len; ++it) {
1616 if (RegExp::isMetaChar(*it)) {
1617 if (!cb.append(ESCAPE_CHAR) || !cb.append(*it))
1618 return NULL;
1619 } else {
1620 if (!cb.append(*it))
1621 return NULL;
1624 return js_NewStringFromCharBuffer(cx, cb);
1627 public:
1628 explicit RegExpGuard(JSContext *cx) : cx(cx), rep(NULL) {}
1630 ~RegExpGuard() {
1631 if (rep.re_)
1632 rep.re_->decref(cx);
1635 /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1636 bool
1637 init(uintN argc, Value *vp)
1639 if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
1640 rep.reobj_ = &vp[2].toObject();
1641 rep.re_ = RegExp::extractFrom(rep.reobj_);
1642 rep.re_->incref(cx);
1643 } else {
1644 fm.patstr = ArgToRootedString(cx, argc, vp, 0);
1645 if (!fm.patstr)
1646 return false;
1648 return true;
1652 * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
1653 * pattern string, or a lengthy pattern string can thwart this process.
1655 * @param checkMetaChars Look for regexp metachars in the pattern string.
1656 * @return Whether flat matching could be used.
1658 * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->throwing.
1660 const FlatMatch *
1661 tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
1662 bool checkMetaChars = true)
1664 if (rep.re_)
1665 return NULL;
1667 fm.pat = fm.patstr->chars();
1668 fm.patlen = fm.patstr->length();
1670 if (optarg < argc)
1671 return NULL;
1673 if (checkMetaChars &&
1674 (fm.patlen > MAX_FLAT_PAT_LEN || RegExp::hasMetaChars(fm.pat, fm.patlen))) {
1675 return NULL;
1679 * textstr could be a rope, so we want to avoid flattening it for as
1680 * long as possible.
1682 if (textstr->isRope()) {
1683 if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_))
1684 return NULL;
1685 } else {
1686 const jschar *text = textstr->nonRopeChars();
1687 size_t textlen = textstr->length();
1688 fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
1690 return &fm;
1693 /* If the pattern is not already a regular expression, make it so. */
1694 const RegExpPair *
1695 normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
1697 /* If we don't have a RegExp, build RegExp from pattern string. */
1698 if (rep.re_)
1699 return &rep;
1701 JSString *opt;
1702 if (optarg < argc) {
1703 opt = js_ValueToString(cx, vp[2 + optarg]);
1704 if (!opt)
1705 return NULL;
1706 } else {
1707 opt = NULL;
1710 JSString *patstr;
1711 if (flat) {
1712 patstr = flattenPattern(cx, fm.patstr);
1713 if (!patstr)
1714 return false;
1715 } else {
1716 patstr = fm.patstr;
1718 JS_ASSERT(patstr);
1720 rep.re_ = RegExp::createFlagged(cx, patstr, opt);
1721 if (!rep.re_)
1722 return NULL;
1723 rep.reobj_ = NULL;
1724 return &rep;
1727 #if DEBUG
1728 bool hasRegExpPair() const { return rep.re_; }
1729 #endif
1732 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1733 static JS_ALWAYS_INLINE bool
1734 Matched(bool test, const Value &v)
1736 return test ? v.isTrue() : !v.isNull();
1739 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
1742 * BitOR-ing these flags allows the DoMatch caller to control when how the
1743 * RegExp engine is called and when callbacks are fired.
1745 enum MatchControlFlags {
1746 TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
1747 TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
1748 CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
1750 MATCH_ARGS = TEST_GLOBAL_BIT,
1751 MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1752 REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1755 /* Factor out looping and matching logic. */
1756 static bool
1757 DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
1758 DoMatchCallback callback, void *data, MatchControlFlags flags)
1760 RegExp &re = rep.re();
1761 if (re.global()) {
1762 /* global matching ('g') */
1763 bool testGlobal = flags & TEST_GLOBAL_BIT;
1764 if (rep.reobj())
1765 rep.reobj()->zeroRegExpLastIndex();
1766 for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1767 if (!re.execute(cx, res, str, &i, testGlobal, vp))
1768 return false;
1769 if (!Matched(testGlobal, *vp))
1770 break;
1771 if (!callback(cx, res, count, data))
1772 return false;
1773 if (!res->matched())
1774 ++i;
1776 } else {
1777 /* single match */
1778 bool testSingle = !!(flags & TEST_SINGLE_BIT),
1779 callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
1780 size_t i = 0;
1781 if (!re.execute(cx, res, str, &i, testSingle, vp))
1782 return false;
1783 if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
1784 return false;
1786 return true;
1789 static bool
1790 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
1792 if (fm.match() < 0) {
1793 vp->setNull();
1794 return true;
1797 /* For this non-global match, produce a RegExp.exec-style array. */
1798 JSObject *obj = NewSlowEmptyArray(cx);
1799 if (!obj)
1800 return false;
1801 vp->setObject(*obj);
1803 return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) &&
1804 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1805 Int32Value(fm.match())) &&
1806 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1807 StringValue(textstr));
1810 typedef JSObject **MatchArgType;
1813 * DoMatch will only callback on global matches, hence this function builds
1814 * only the "array of matches" returned by match on global regexps.
1816 static bool
1817 MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
1819 JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */
1821 JSObject *&arrayobj = *static_cast<MatchArgType>(p);
1822 if (!arrayobj) {
1823 arrayobj = NewDenseEmptyArray(cx);
1824 if (!arrayobj)
1825 return false;
1828 Value v;
1829 if (!res->createLastMatch(cx, &v))
1830 return false;
1832 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1833 return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false);
1836 static JSBool
1837 str_match(JSContext *cx, uintN argc, Value *vp)
1839 JSString *str;
1840 NORMALIZE_THIS(cx, vp, str);
1842 RegExpGuard g(cx);
1843 if (!g.init(argc, vp))
1844 return false;
1845 if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc))
1846 return BuildFlatMatchArray(cx, str, *fm, vp);
1847 if (cx->throwing) /* from tryFlatMatch */
1848 return false;
1850 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1851 if (!rep)
1852 return false;
1854 AutoObjectRooter array(cx);
1855 MatchArgType arg = array.addr();
1856 RegExpStatics *res = cx->regExpStatics();
1857 if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
1858 return false;
1860 /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
1861 if (rep->re().global())
1862 vp->setObjectOrNull(array.object());
1863 return true;
1866 static JSBool
1867 str_search(JSContext *cx, uintN argc, Value *vp)
1869 JSString *str;
1870 NORMALIZE_THIS(cx, vp, str);
1872 RegExpGuard g(cx);
1873 if (!g.init(argc, vp))
1874 return false;
1875 if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) {
1876 vp->setInt32(fm->match());
1877 return true;
1879 if (cx->throwing) /* from tryFlatMatch */
1880 return false;
1881 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1882 if (!rep)
1883 return false;
1885 RegExpStatics *res = cx->regExpStatics();
1886 size_t i = 0;
1887 if (!rep->re().execute(cx, res, str, &i, true, vp))
1888 return false;
1890 if (vp->isTrue())
1891 vp->setInt32(res->matchStart());
1892 else
1893 vp->setInt32(-1);
1894 return true;
1897 struct ReplaceData
1899 ReplaceData(JSContext *cx)
1900 : g(cx), cb(cx)
1903 JSString *str; /* 'this' parameter object as a string */
1904 RegExpGuard g; /* regexp parameter object and private data */
1905 JSObject *lambda; /* replacement function object or null */
1906 JSObject *elembase; /* object for function(a){return b[a]} replace */
1907 JSLinearString *repstr; /* replacement string */
1908 const jschar *dollar; /* null or pointer to first $ in repstr */
1909 const jschar *dollarEnd; /* limit pointer for js_strchr_limit */
1910 jsint index; /* index in result of next replacement */
1911 jsint leftIndex; /* left context index in str->chars */
1912 JSSubString dollarStr; /* for "$$" InterpretDollar result */
1913 bool calledBack; /* record whether callback has been called */
1914 InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
1915 InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
1916 JSCharBuffer cb; /* buffer built during DoMatch */
1919 static bool
1920 InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep,
1921 ReplaceData &rdata, JSSubString *out, size_t *skip)
1923 JS_ASSERT(*dp == '$');
1925 /* If there is only a dollar, bail now */
1926 if (dp + 1 >= ep)
1927 return false;
1929 /* Interpret all Perl match-induced dollar variables. */
1930 jschar dc = dp[1];
1931 if (JS7_ISDEC(dc)) {
1932 /* ECMA-262 Edition 3: 1-9 or 01-99 */
1933 uintN num = JS7_UNDEC(dc);
1934 if (num > res->parenCount())
1935 return false;
1937 const jschar *cp = dp + 2;
1938 if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
1939 uintN tmp = 10 * num + JS7_UNDEC(dc);
1940 if (tmp <= res->parenCount()) {
1941 cp++;
1942 num = tmp;
1945 if (num == 0)
1946 return false;
1948 *skip = cp - dp;
1950 JS_ASSERT(num <= res->parenCount());
1953 * Note: we index to get the paren with the (1-indexed) pair
1954 * number, as opposed to a (0-indexed) paren number.
1956 res->getParen(num, out);
1957 return true;
1960 *skip = 2;
1961 switch (dc) {
1962 case '$':
1963 rdata.dollarStr.chars = dp;
1964 rdata.dollarStr.length = 1;
1965 *out = rdata.dollarStr;
1966 return true;
1967 case '&':
1968 res->getLastMatch(out);
1969 return true;
1970 case '+':
1971 res->getLastParen(out);
1972 return true;
1973 case '`':
1974 res->getLeftContext(out);
1975 return true;
1976 case '\'':
1977 res->getRightContext(out);
1978 return true;
1980 return false;
1983 static bool
1984 FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
1986 JSObject *base = rdata.elembase;
1987 if (base) {
1989 * The base object is used when replace was passed a lambda which looks like
1990 * 'function(a) { return b[a]; }' for the base object b. b will not change
1991 * in the course of the replace unless we end up making a scripted call due
1992 * to accessing a scripted getter or a value with a scripted toString.
1994 JS_ASSERT(rdata.lambda);
1995 JS_ASSERT(!base->getOps()->lookupProperty);
1996 JS_ASSERT(!base->getOps()->getProperty);
1998 Value match;
1999 if (!res->createLastMatch(cx, &match))
2000 return false;
2001 JSString *str = match.toString();
2003 JSAtom *atom;
2004 if (str->isAtomized()) {
2005 atom = STRING_TO_ATOM(str);
2006 } else {
2007 atom = js_AtomizeString(cx, str, 0);
2008 if (!atom)
2009 return false;
2011 jsid id = ATOM_TO_JSID(atom);
2013 JSObject *holder;
2014 JSProperty *prop = NULL;
2015 if (js_LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop) < 0)
2016 return false;
2018 /* Only handle the case where the property exists and is on this object. */
2019 if (prop && holder == base) {
2020 Shape *shape = (Shape *) prop;
2021 if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) {
2022 Value value = base->getSlot(shape->slot);
2023 if (value.isString()) {
2024 rdata.repstr = value.toString()->ensureLinear(cx);
2025 if (!rdata.repstr)
2026 return false;
2027 *sizep = rdata.repstr->length();
2028 return true;
2034 * Couldn't handle this property, fall through and despecialize to the
2035 * general lambda case.
2037 rdata.elembase = NULL;
2040 JSObject *lambda = rdata.lambda;
2041 if (lambda) {
2043 * In the lambda case, not only do we find the replacement string's
2044 * length, we compute repstr and return it via rdata for use within
2045 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
2046 * index, input), i.e., all the properties of a regexp match array.
2047 * For $&, etc., we must create string jsvals from cx->regExpStatics.
2048 * We grab up stack space to keep the newborn strings GC-rooted.
2050 uintN p = res->parenCount();
2051 uintN argc = 1 + p + 2;
2053 InvokeSessionGuard &session = rdata.session;
2054 if (!session.started()) {
2055 Value lambdav = ObjectValue(*lambda);
2056 if (!session.start(cx, lambdav, UndefinedValue(), argc))
2057 return false;
2060 PreserveRegExpStatics staticsGuard(res);
2061 if (!staticsGuard.init(cx))
2062 return false;
2064 /* Push $&, $1, $2, ... */
2065 uintN argi = 0;
2066 if (!res->createLastMatch(cx, &session[argi++]))
2067 return false;
2069 for (size_t i = 0; i < res->parenCount(); ++i) {
2070 if (!res->createParen(cx, i + 1, &session[argi++]))
2071 return false;
2074 /* Push match index and input string. */
2075 session[argi++].setInt32(res->matchStart());
2076 session[argi].setString(rdata.str);
2078 if (!session.invoke(cx))
2079 return false;
2081 /* root repstr: rdata is on the stack, so scanned by conservative gc. */
2082 JSString *repstr = ValueToString_TestForStringInline(cx, session.rval());
2083 if (!repstr)
2084 return false;
2085 rdata.repstr = repstr->ensureLinear(cx);
2086 if (!rdata.repstr)
2087 return false;
2088 *sizep = rdata.repstr->length();
2089 return true;
2092 JSString *repstr = rdata.repstr;
2093 size_t replen = repstr->length();
2094 for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp;
2095 dp = js_strchr_limit(dp, '$', ep)) {
2096 JSSubString sub;
2097 size_t skip;
2098 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2099 replen += sub.length - skip;
2100 dp += skip;
2101 } else {
2102 dp++;
2105 *sizep = replen;
2106 return true;
2109 static void
2110 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars)
2112 JSLinearString *repstr = rdata.repstr;
2113 const jschar *cp;
2114 const jschar *bp = cp = repstr->chars();
2116 const jschar *dp = rdata.dollar;
2117 const jschar *ep = rdata.dollarEnd;
2118 for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
2119 size_t len = dp - cp;
2120 js_strncpy(chars, cp, len);
2121 chars += len;
2122 cp = dp;
2124 JSSubString sub;
2125 size_t skip;
2126 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2127 len = sub.length;
2128 js_strncpy(chars, sub.chars, len);
2129 chars += len;
2130 cp += skip;
2131 dp += skip;
2132 } else {
2133 dp++;
2136 js_strncpy(chars, cp, repstr->length() - (cp - bp));
2139 static bool
2140 ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
2142 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
2144 rdata.calledBack = true;
2145 JSLinearString *str = rdata.str->assertIsLinear(); /* flattened for regexp */
2146 size_t leftoff = rdata.leftIndex;
2147 const jschar *left = str->chars() + leftoff;
2148 size_t leftlen = res->matchStart() - leftoff;
2149 rdata.leftIndex = res->matchLimit();
2151 size_t replen = 0; /* silence 'unused' warning */
2152 if (!FindReplaceLength(cx, res, rdata, &replen))
2153 return false;
2155 size_t growth = leftlen + replen;
2156 if (!rdata.cb.growByUninitialized(growth))
2157 return false;
2159 jschar *chars = rdata.cb.begin() + rdata.index;
2160 rdata.index += growth;
2161 js_strncpy(chars, left, leftlen);
2162 chars += leftlen;
2163 DoReplace(cx, res, rdata, chars);
2164 return true;
2167 static bool
2168 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2169 const FlatMatch &fm, Value *vp)
2171 RopeBuilder builder(cx);
2172 size_t match = fm.match();
2173 size_t matchEnd = match + fm.patternLength();
2175 if (textstr->isRope()) {
2177 * If we are replacing over a rope, avoid flattening it by iterating
2178 * through it, building a new rope.
2180 StringSegmentRange r(cx);
2181 if (!r.init(textstr))
2182 return false;
2183 size_t pos = 0;
2184 while (!r.empty()) {
2185 JSString *str = r.front();
2186 size_t len = str->length();
2187 size_t strEnd = pos + len;
2188 if (pos < matchEnd && strEnd > match) {
2190 * We need to special-case any part of the rope that overlaps
2191 * with the replacement string.
2193 if (match >= pos) {
2195 * If this part of the rope overlaps with the left side of
2196 * the pattern, then it must be the only one to overlap with
2197 * the first character in the pattern, so we include the
2198 * replacement string here.
2200 JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
2201 if (!leftSide ||
2202 !builder.append(leftSide) ||
2203 !builder.append(repstr)) {
2204 return false;
2209 * If str runs off the end of the matched string, append the
2210 * last part of str.
2212 if (strEnd > matchEnd) {
2213 JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
2214 strEnd - matchEnd);
2215 if (!rightSide || !builder.append(rightSide))
2216 return false;
2218 } else {
2219 if (!builder.append(str))
2220 return false;
2222 pos += str->length();
2223 if (!r.popFront())
2224 return false;
2226 } else {
2227 JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
2228 if (!leftSide)
2229 return false;
2230 JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
2231 textstr->length() - match - fm.patternLength());
2232 if (!rightSide ||
2233 !builder.append(leftSide) ||
2234 !builder.append(repstr) ||
2235 !builder.append(rightSide)) {
2236 return false;
2240 vp->setString(builder.result());
2241 return true;
2245 * Perform a linear-scan dollar substitution on the replacement text,
2246 * constructing a result string that looks like:
2248 * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
2250 static inline bool
2251 BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
2252 const jschar *firstDollar, const FlatMatch &fm, Value *vp)
2254 JSLinearString *textstr = textstrArg->ensureLinear(cx);
2255 if (!textstr)
2256 return NULL;
2258 JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
2259 size_t matchStart = fm.match();
2260 size_t matchLimit = matchStart + fm.patternLength();
2263 * Most probably:
2265 * len(newstr) >= len(orig) - len(match) + len(replacement)
2267 * Note that dollar vars _could_ make the resulting text smaller than this.
2269 JSCharBuffer newReplaceChars(cx);
2270 if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
2271 return false;
2273 /* Move the pre-dollar chunk in bulk. */
2274 JS_ALWAYS_TRUE(newReplaceChars.append(repstr->chars(), firstDollar));
2276 /* Move the rest char-by-char, interpreting dollars as we encounter them. */
2277 #define ENSURE(__cond) if (!(__cond)) return false;
2278 const jschar *repstrLimit = repstr->chars() + repstr->length();
2279 for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
2280 if (*it != '$' || it == repstrLimit - 1) {
2281 ENSURE(newReplaceChars.append(*it));
2282 continue;
2285 switch (*(it + 1)) {
2286 case '$': /* Eat one of the dollars. */
2287 ENSURE(newReplaceChars.append(*it));
2288 break;
2289 case '&':
2290 ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
2291 textstr->chars() + matchLimit));
2292 break;
2293 case '`':
2294 ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
2295 break;
2296 case '\'':
2297 ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
2298 textstr->chars() + textstr->length()));
2299 break;
2300 default: /* The dollar we saw was not special (no matter what its mother told it). */
2301 ENSURE(newReplaceChars.append(*it));
2302 continue;
2304 ++it; /* We always eat an extra char in the above switch. */
2307 JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
2308 ENSURE(leftSide);
2310 JSString *newReplace = js_NewStringFromCharBuffer(cx, newReplaceChars);
2311 ENSURE(newReplace);
2313 JS_ASSERT(textstr->length() >= matchLimit);
2314 JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
2315 textstr->length() - matchLimit);
2316 ENSURE(rightSide);
2318 RopeBuilder builder(cx);
2319 ENSURE(builder.append(leftSide) &&
2320 builder.append(newReplace) &&
2321 builder.append(rightSide));
2322 #undef ENSURE
2324 vp->setString(builder.result());
2325 return true;
2328 static inline bool
2329 str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
2331 const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
2332 if (!rep)
2333 return false;
2335 rdata.index = 0;
2336 rdata.leftIndex = 0;
2337 rdata.calledBack = false;
2339 RegExpStatics *res = cx->regExpStatics();
2340 if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
2341 return false;
2343 if (!rdata.calledBack) {
2344 /* Didn't match, so the string is unmodified. */
2345 vp->setString(rdata.str);
2346 return true;
2349 JSSubString sub;
2350 res->getRightContext(&sub);
2351 if (!rdata.cb.append(sub.chars, sub.length))
2352 return false;
2354 JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb);
2355 if (!retstr)
2356 return false;
2358 vp->setString(retstr);
2359 return true;
2362 static inline bool
2363 str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata,
2364 const FlatMatch &fm)
2366 JS_ASSERT(fm.match() >= 0);
2367 LeaveTrace(cx);
2369 JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
2370 if (!matchStr)
2371 return false;
2373 /* lambda(matchStr, matchStart, textstr) */
2374 static const uint32 lambdaArgc = 3;
2375 if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
2376 return false;
2378 CallArgs &args = rdata.singleShot;
2379 args.callee().setObject(*rdata.lambda);
2380 args.thisv().setUndefined();
2382 Value *sp = args.argv();
2383 sp[0].setString(matchStr);
2384 sp[1].setInt32(fm.match());
2385 sp[2].setString(rdata.str);
2387 if (!Invoke(cx, rdata.singleShot, 0))
2388 return false;
2390 JSString *repstr = js_ValueToString(cx, args.rval());
2391 if (!repstr)
2392 return false;
2394 JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
2395 if (!leftSide)
2396 return false;
2398 size_t matchLimit = fm.match() + fm.patternLength();
2399 JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
2400 rdata.str->length() - matchLimit);
2401 if (!rightSide)
2402 return false;
2404 RopeBuilder builder(cx);
2405 if (!(builder.append(leftSide) &&
2406 builder.append(repstr) &&
2407 builder.append(rightSide))) {
2408 return false;
2411 vp->setString(builder.result());
2412 return true;
2415 JSBool
2416 js::str_replace(JSContext *cx, uintN argc, Value *vp)
2418 ReplaceData rdata(cx);
2419 NORMALIZE_THIS(cx, vp, rdata.str);
2420 static const uint32 optarg = 2;
2422 /* Extract replacement string/function. */
2423 if (argc >= optarg && js_IsCallable(vp[3])) {
2424 rdata.lambda = &vp[3].toObject();
2425 rdata.elembase = NULL;
2426 rdata.repstr = NULL;
2427 rdata.dollar = rdata.dollarEnd = NULL;
2429 if (rdata.lambda->isFunction()) {
2430 JSFunction *fun = rdata.lambda->getFunctionPrivate();
2431 if (fun->isInterpreted()) {
2433 * Pattern match the script to check if it is is indexing into a
2434 * particular object, e.g. 'function(a) { return b[a]; }'. Avoid
2435 * calling the script in such cases, which are used by javascript
2436 * packers (particularly the popular Dean Edwards packer) to efficiently
2437 * encode large scripts. We only handle the code patterns generated
2438 * by such packers here.
2440 JSScript *script = fun->u.i.script;
2441 jsbytecode *pc = script->code;
2443 Value table = UndefinedValue();
2444 if (JSOp(*pc) == JSOP_GETFCSLOT) {
2445 table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc));
2446 pc += JSOP_GETFCSLOT_LENGTH;
2449 if (table.isObject() &&
2450 JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
2451 JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM &&
2452 JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) {
2453 Class *clasp = table.toObject().getClass();
2454 if (clasp->isNative() &&
2455 !clasp->ops.lookupProperty &&
2456 !clasp->ops.getProperty) {
2457 rdata.elembase = &table.toObject();
2462 } else {
2463 rdata.lambda = NULL;
2464 rdata.elembase = NULL;
2465 rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
2466 if (!rdata.repstr)
2467 return false;
2469 /* We're about to store pointers into the middle of our string. */
2470 if (!js_MakeStringImmutable(cx, rdata.repstr))
2471 return false;
2472 rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
2473 rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
2474 rdata.dollarEnd);
2477 if (!rdata.g.init(argc, vp))
2478 return false;
2481 * Unlike its |String.prototype| brethren, |replace| doesn't convert
2482 * its input to a regular expression. (Even if it contains metachars.)
2484 * However, if the user invokes our (non-standard) |flags| argument
2485 * extension then we revert to creating a regular expression. Note that
2486 * this is observable behavior through the side-effect mutation of the
2487 * |RegExp| statics.
2490 const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false);
2491 if (!fm) {
2492 if (cx->throwing) /* oom in RopeMatch in tryFlatMatch */
2493 return false;
2494 JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
2495 return str_replace_regexp(cx, argc, vp, rdata);
2498 if (fm->match() < 0) {
2499 vp->setString(rdata.str);
2500 return true;
2503 if (rdata.lambda)
2504 return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
2507 * Note: we could optimize the text.length == pattern.length case if we wanted,
2508 * even in the presence of dollar metachars.
2510 if (rdata.dollar)
2511 return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
2513 return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
2517 * Subroutine used by str_split to find the next split point in str, starting
2518 * at offset *ip and looking either for the separator substring given by sep, or
2519 * for the next re match. In the re case, return the matched separator in *sep,
2520 * and the possibly updated offset in *ip.
2522 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
2523 * separator occurrence if found, or str->length if no separator is found.
2525 static jsint
2526 find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
2527 JSSubString *sep)
2530 * Stop if past end of string. If at end of string, we will compare the
2531 * null char stored there (by js_NewString*) to sep->chars[j] in the while
2532 * loop at the end of this function, so that
2534 * "ab,".split(',') => ["ab", ""]
2536 * and the resulting array converts back to the string "ab," for symmetry.
2537 * However, we ape Perl and do this only if there is a sufficiently large
2538 * limit argument (see str_split).
2540 jsint i = *ip;
2541 size_t length = str->length();
2542 if ((size_t)i > length)
2543 return -1;
2545 const jschar *chars = str->getChars(cx);
2546 if (!chars)
2547 return -2;
2550 * Match a regular expression against the separator at or above index i.
2551 * Call js_ExecuteRegExp with true for the test argument. On successful
2552 * match, get the separator from cx->regExpStatics.lastMatch.
2554 if (re) {
2555 size_t index;
2556 Value rval;
2558 again:
2559 /* JS1.2 deviated from Perl by never matching at end of string. */
2560 index = (size_t)i;
2561 if (!re->execute(cx, res, str, &index, true, &rval))
2562 return -2;
2563 if (!rval.isTrue()) {
2564 /* Mismatch: ensure our caller advances i past end of string. */
2565 sep->length = 1;
2566 return length;
2568 i = (jsint)index;
2569 JS_ASSERT(sep);
2570 res->getLastMatch(sep);
2571 if (sep->length == 0) {
2573 * Empty string match: never split on an empty match at the start
2574 * of a find_split cycle. Same rule as for an empty global match
2575 * in DoMatch.
2577 if (i == *ip) {
2579 * "Bump-along" to avoid sticking at an empty match, but don't
2580 * bump past end of string -- our caller must do that by adding
2581 * sep->length to our return value.
2583 if ((size_t)i == length)
2584 return -1;
2585 i++;
2586 goto again;
2588 if ((size_t)i == length) {
2590 * If there was a trivial zero-length match at the end of the
2591 * split, then we shouldn't output the matched string at the end
2592 * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
2594 sep->chars = NULL;
2597 JS_ASSERT((size_t)i >= sep->length);
2598 return i - sep->length;
2602 * Special case: if sep is the empty string, split str into one character
2603 * substrings. Let our caller worry about whether to split once at end of
2604 * string into an empty substring.
2606 if (sep->length == 0)
2607 return ((size_t)i == length) ? -1 : i + 1;
2610 * Now that we know sep is non-empty, search starting at i in str for an
2611 * occurrence of all of sep's chars. If we find them, return the index of
2612 * the first separator char. Otherwise, return length.
2614 jsint match = StringMatch(chars + i, length - i, sep->chars, sep->length);
2615 return match == -1 ? length : match + i;
2618 static JSBool
2619 str_split(JSContext *cx, uintN argc, Value *vp)
2621 JSString *str;
2622 NORMALIZE_THIS(cx, vp, str);
2624 if (argc == 0) {
2625 Value v = StringValue(str);
2626 JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
2627 if (!aobj)
2628 return false;
2629 vp->setObject(*aobj);
2630 return true;
2633 RegExp *re;
2634 JSSubString *sep, tmp;
2635 if (VALUE_IS_REGEXP(cx, vp[2])) {
2636 re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
2637 sep = &tmp;
2639 /* Set a magic value so we can detect a successful re match. */
2640 sep->chars = NULL;
2641 sep->length = 0;
2642 } else {
2643 JSString *sepstr = js_ValueToString(cx, vp[2]);
2644 if (!sepstr)
2645 return false;
2646 vp[2].setString(sepstr);
2649 * Point sep at a local copy of sepstr's header because find_split
2650 * will modify sep->length.
2652 tmp.length = sepstr->length();
2653 tmp.chars = sepstr->getChars(cx);
2654 if (!tmp.chars)
2655 return false;
2656 re = NULL;
2657 sep = &tmp;
2660 /* Use the second argument as the split limit, if given. */
2661 uint32 limit = 0; /* Avoid warning. */
2662 bool limited = (argc > 1) && !vp[3].isUndefined();
2663 if (limited) {
2664 jsdouble d;
2665 if (!ValueToNumber(cx, vp[3], &d))
2666 return false;
2668 /* Clamp limit between 0 and 1 + string length. */
2669 limit = js_DoubleToECMAUint32(d);
2670 if (limit > str->length())
2671 limit = 1 + str->length();
2674 AutoValueVector splits(cx);
2676 RegExpStatics *res = cx->regExpStatics();
2677 jsint i, j;
2678 uint32 len = i = 0;
2679 while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
2680 if (limited && len >= limit)
2681 break;
2683 JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
2684 if (!sub || !splits.append(StringValue(sub)))
2685 return false;
2686 len++;
2689 * Imitate perl's feature of including parenthesized substrings that
2690 * matched part of the delimiter in the new array, after the split
2691 * substring that was delimited.
2693 if (re && sep->chars) {
2694 for (uintN num = 0; num < res->parenCount(); num++) {
2695 if (limited && len >= limit)
2696 break;
2697 JSSubString parsub;
2698 res->getParen(num + 1, &parsub);
2699 sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
2700 if (!sub || !splits.append(StringValue(sub)))
2701 return false;
2702 len++;
2704 sep->chars = NULL;
2706 i = j + sep->length;
2709 if (j == -2)
2710 return false;
2712 JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
2713 if (!aobj)
2714 return false;
2715 vp->setObject(*aobj);
2716 return true;
2719 #if JS_HAS_PERL_SUBSTR
2720 static JSBool
2721 str_substr(JSContext *cx, uintN argc, Value *vp)
2723 JSString *str;
2724 int32 length, len, begin;
2726 NORMALIZE_THIS(cx, vp, str);
2728 if (argc > 0) {
2729 length = int32(str->length());
2730 if (!ValueToIntegerRange(cx, vp[2], &begin))
2731 return false;
2733 if (begin >= length) {
2734 str = cx->runtime->emptyString;
2735 goto out;
2737 if (begin < 0) {
2738 begin += length; /* length + INT_MIN will always be less then 0 */
2739 if (begin < 0)
2740 begin = 0;
2743 if (argc == 1 || vp[3].isUndefined()) {
2744 len = length - begin;
2745 } else {
2746 if (!ValueToIntegerRange(cx, vp[3], &len))
2747 return false;
2749 if (len <= 0) {
2750 str = cx->runtime->emptyString;
2751 goto out;
2754 if (uint32(length) < uint32(begin + len))
2755 len = length - begin;
2758 str = js_NewDependentString(cx, str, size_t(begin), size_t(len));
2759 if (!str)
2760 return false;
2763 out:
2764 vp->setString(str);
2765 return true;
2767 #endif /* JS_HAS_PERL_SUBSTR */
2770 * Python-esque sequence operations.
2772 static JSBool
2773 str_concat(JSContext *cx, uintN argc, Value *vp)
2775 JSString *str, *str2;
2776 Value *argv;
2777 uintN i;
2779 NORMALIZE_THIS(cx, vp, str);
2781 /* Set vp (aka rval) early to handle the argc == 0 case. */
2782 vp->setString(str);
2784 for (i = 0, argv = vp + 2; i < argc; i++) {
2785 str2 = js_ValueToString(cx, argv[i]);
2786 if (!str2)
2787 return JS_FALSE;
2788 argv[i].setString(str2);
2790 str = js_ConcatStrings(cx, str, str2);
2791 if (!str)
2792 return JS_FALSE;
2793 vp->setString(str);
2796 return JS_TRUE;
2799 static JSBool
2800 str_slice(JSContext *cx, uintN argc, Value *vp)
2802 if (argc == 1 && vp[1].isString() && vp[2].isInt32()) {
2803 size_t begin, end, length;
2805 JSString *str = vp[1].toString();
2806 begin = vp[2].toInt32();
2807 end = str->length();
2808 if (begin <= end) {
2809 length = end - begin;
2810 if (length == 0) {
2811 str = cx->runtime->emptyString;
2812 } else {
2813 str = (length == 1)
2814 ? JSString::getUnitString(cx, str, begin)
2815 : js_NewDependentString(cx, str, begin, length);
2816 if (!str)
2817 return JS_FALSE;
2819 vp->setString(str);
2820 return JS_TRUE;
2824 JSString *str;
2825 NORMALIZE_THIS(cx, vp, str);
2827 if (argc != 0) {
2828 double begin, end, length;
2830 if (!ValueToNumber(cx, vp[2], &begin))
2831 return JS_FALSE;
2832 begin = js_DoubleToInteger(begin);
2833 length = str->length();
2834 if (begin < 0) {
2835 begin += length;
2836 if (begin < 0)
2837 begin = 0;
2838 } else if (begin > length) {
2839 begin = length;
2842 if (argc == 1 || vp[3].isUndefined()) {
2843 end = length;
2844 } else {
2845 if (!ValueToNumber(cx, vp[3], &end))
2846 return JS_FALSE;
2847 end = js_DoubleToInteger(end);
2848 if (end < 0) {
2849 end += length;
2850 if (end < 0)
2851 end = 0;
2852 } else if (end > length) {
2853 end = length;
2855 if (end < begin)
2856 end = begin;
2859 str = js_NewDependentString(cx, str,
2860 (size_t)begin,
2861 (size_t)(end - begin));
2862 if (!str)
2863 return JS_FALSE;
2865 vp->setString(str);
2866 return JS_TRUE;
2869 #if JS_HAS_STR_HTML_HELPERS
2871 * HTML composition aids.
2873 static bool
2874 tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end,
2875 Value *vp)
2877 JSString *thisstr;
2878 NORMALIZE_THIS(cx, vp, thisstr);
2879 JSLinearString *str = thisstr->ensureLinear(cx);
2880 if (!str)
2881 return false;
2883 if (!end)
2884 end = begin;
2886 size_t beglen = strlen(begin);
2887 size_t taglen = 1 + beglen + 1; /* '<begin' + '>' */
2888 size_t parlen = 0; /* Avoid warning. */
2889 if (param) {
2890 parlen = param->length();
2891 taglen += 2 + parlen + 1; /* '="param"' */
2893 size_t endlen = strlen(end);
2894 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2896 if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2897 js_ReportAllocationOverflow(cx);
2898 return false;
2901 jschar *tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2902 if (!tagbuf)
2903 return false;
2905 size_t j = 0;
2906 tagbuf[j++] = '<';
2907 for (size_t i = 0; i < beglen; i++)
2908 tagbuf[j++] = (jschar)begin[i];
2909 if (param) {
2910 tagbuf[j++] = '=';
2911 tagbuf[j++] = '"';
2912 js_strncpy(&tagbuf[j], param->chars(), parlen);
2913 j += parlen;
2914 tagbuf[j++] = '"';
2916 tagbuf[j++] = '>';
2918 js_strncpy(&tagbuf[j], str->chars(), str->length());
2919 j += str->length();
2920 tagbuf[j++] = '<';
2921 tagbuf[j++] = '/';
2922 for (size_t i = 0; i < endlen; i++)
2923 tagbuf[j++] = (jschar)end[i];
2924 tagbuf[j++] = '>';
2925 JS_ASSERT(j == taglen);
2926 tagbuf[j] = 0;
2928 JSString *retstr = js_NewString(cx, tagbuf, taglen);
2929 if (!retstr) {
2930 js_free((char *)tagbuf);
2931 return false;
2933 vp->setString(retstr);
2934 return true;
2937 static JSBool
2938 tagify_value(JSContext *cx, uintN argc, Value *vp,
2939 const char *begin, const char *end)
2941 JSLinearString *param = ArgToRootedString(cx, argc, vp, 0);
2942 if (!param)
2943 return JS_FALSE;
2944 return tagify(cx, begin, param, end, vp);
2947 static JSBool
2948 str_bold(JSContext *cx, uintN argc, Value *vp)
2950 return tagify(cx, "b", NULL, NULL, vp);
2953 static JSBool
2954 str_italics(JSContext *cx, uintN argc, Value *vp)
2956 return tagify(cx, "i", NULL, NULL, vp);
2959 static JSBool
2960 str_fixed(JSContext *cx, uintN argc, Value *vp)
2962 return tagify(cx, "tt", NULL, NULL, vp);
2965 static JSBool
2966 str_fontsize(JSContext *cx, uintN argc, Value *vp)
2968 return tagify_value(cx, argc, vp, "font size", "font");
2971 static JSBool
2972 str_fontcolor(JSContext *cx, uintN argc, Value *vp)
2974 return tagify_value(cx, argc, vp, "font color", "font");
2977 static JSBool
2978 str_link(JSContext *cx, uintN argc, Value *vp)
2980 return tagify_value(cx, argc, vp, "a href", "a");
2983 static JSBool
2984 str_anchor(JSContext *cx, uintN argc, Value *vp)
2986 return tagify_value(cx, argc, vp, "a name", "a");
2989 static JSBool
2990 str_strike(JSContext *cx, uintN argc, Value *vp)
2992 return tagify(cx, "strike", NULL, NULL, vp);
2995 static JSBool
2996 str_small(JSContext *cx, uintN argc, Value *vp)
2998 return tagify(cx, "small", NULL, NULL, vp);
3001 static JSBool
3002 str_big(JSContext *cx, uintN argc, Value *vp)
3004 return tagify(cx, "big", NULL, NULL, vp);
3007 static JSBool
3008 str_blink(JSContext *cx, uintN argc, Value *vp)
3010 return tagify(cx, "blink", NULL, NULL, vp);
3013 static JSBool
3014 str_sup(JSContext *cx, uintN argc, Value *vp)
3016 return tagify(cx, "sup", NULL, NULL, vp);
3019 static JSBool
3020 str_sub(JSContext *cx, uintN argc, Value *vp)
3022 return tagify(cx, "sub", NULL, NULL, vp);
3024 #endif /* JS_HAS_STR_HTML_HELPERS */
3026 #ifdef JS_TRACER
3027 JSString* FASTCALL
3028 js_String_getelem(JSContext* cx, JSString* str, int32 i)
3030 if ((size_t)i >= str->length())
3031 return NULL;
3032 return JSString::getUnitString(cx, str, size_t(i));
3034 #endif
3036 JS_DEFINE_TRCINFO_1(str_concat,
3037 (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING,
3038 1, nanojit::ACCSET_NONE)))
3040 static JSFunctionSpec string_methods[] = {
3041 #if JS_HAS_TOSOURCE
3042 JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE),
3043 JS_FN(js_toSource_str, str_toSource, 0,0),
3044 #endif
3046 /* Java-like methods. */
3047 JS_FN(js_toString_str, js_str_toString, 0,0),
3048 JS_FN(js_valueOf_str, js_str_toString, 0,0),
3049 JS_FN(js_toJSON_str, js_str_toString, 0,0),
3050 JS_FN("substring", str_substring, 2,JSFUN_GENERIC_NATIVE),
3051 JS_FN("toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE),
3052 JS_FN("toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE),
3053 JS_FN("charAt", js_str_charAt, 1,JSFUN_GENERIC_NATIVE),
3054 JS_FN("charCodeAt", js_str_charCodeAt, 1,JSFUN_GENERIC_NATIVE),
3055 JS_FN("indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE),
3056 JS_FN("lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE),
3057 JS_FN("trim", str_trim, 0,JSFUN_GENERIC_NATIVE),
3058 JS_FN("trimLeft", str_trimLeft, 0,JSFUN_GENERIC_NATIVE),
3059 JS_FN("trimRight", str_trimRight, 0,JSFUN_GENERIC_NATIVE),
3060 JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
3061 JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE),
3062 JS_FN("localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE),
3064 /* Perl-ish methods (search is actually Python-esque). */
3065 JS_FN("match", str_match, 1,JSFUN_GENERIC_NATIVE),
3066 JS_FN("search", str_search, 1,JSFUN_GENERIC_NATIVE),
3067 JS_FN("replace", str_replace, 2,JSFUN_GENERIC_NATIVE),
3068 JS_FN("split", str_split, 2,JSFUN_GENERIC_NATIVE),
3069 #if JS_HAS_PERL_SUBSTR
3070 JS_FN("substr", str_substr, 2,JSFUN_GENERIC_NATIVE),
3071 #endif
3073 /* Python-esque sequence methods. */
3074 JS_TN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE, &str_concat_trcinfo),
3075 JS_FN("slice", str_slice, 2,JSFUN_GENERIC_NATIVE),
3077 /* HTML string methods. */
3078 #if JS_HAS_STR_HTML_HELPERS
3079 JS_FN("bold", str_bold, 0,0),
3080 JS_FN("italics", str_italics, 0,0),
3081 JS_FN("fixed", str_fixed, 0,0),
3082 JS_FN("fontsize", str_fontsize, 1,0),
3083 JS_FN("fontcolor", str_fontcolor, 1,0),
3084 JS_FN("link", str_link, 1,0),
3085 JS_FN("anchor", str_anchor, 1,0),
3086 JS_FN("strike", str_strike, 0,0),
3087 JS_FN("small", str_small, 0,0),
3088 JS_FN("big", str_big, 0,0),
3089 JS_FN("blink", str_blink, 0,0),
3090 JS_FN("sup", str_sup, 0,0),
3091 JS_FN("sub", str_sub, 0,0),
3092 #endif
3094 JS_FS_END
3098 * Set up some tools to make it easier to generate large tables. After constant
3099 * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
3100 * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
3101 * To use this, define R appropriately, then use Rn(0) (for some value of n), then
3102 * undefine R.
3104 #define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0))
3105 #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
3106 #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
3107 #define R8(n) R6(n), R6((n) + (1 << 6)), R6((n) + (2 << 6)), R6((n) + (3 << 6))
3108 #define R10(n) R8(n), R8((n) + (1 << 8)), R8((n) + (2 << 8)), R8((n) + (3 << 8))
3109 #define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10))
3111 #define R3(n) R2(n), R2((n) + (1 << 2))
3112 #define R7(n) R6(n), R6((n) + (1 << 6))
3114 #define BUILD_LENGTH_AND_FLAGS(length, flags) \
3115 (((length) << JSString::LENGTH_SHIFT) | (flags))
3118 * Declare unit strings. Pack the string data itself into the mInlineChars
3119 * place in the header.
3121 #define R(c) { \
3122 BUILD_LENGTH_AND_FLAGS(1, JSString::FLAT | JSString::ATOMIZED), \
3123 { (jschar *)(((char *)(unitStringTable + (c))) + \
3124 offsetof(JSString, inlineStorage)) }, \
3125 { {(c), 0x00} } }
3127 #ifdef __SUNPRO_CC
3128 #pragma pack(8)
3129 #else
3130 #pragma pack(push, 8)
3131 #endif
3133 const JSString JSString::unitStringTable[]
3134 #ifdef __GNUC__
3135 __attribute__ ((aligned (8)))
3136 #endif
3137 = { R8(0) };
3139 #ifdef __SUNPRO_CC
3140 #pragma pack(0)
3141 #else
3142 #pragma pack(pop)
3143 #endif
3145 #undef R
3148 * Declare length-2 strings. We only store strings where both characters are
3149 * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
3150 * the lowercase letters, and the next 26 are the uppercase letters.
3152 #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
3153 (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \
3154 (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \
3155 JSString::INVALID_SMALL_CHAR)
3157 #define R TO_SMALL_CHAR
3159 const JSString::SmallChar JSString::toSmallChar[] = { R7(0) };
3161 #undef R
3164 * This is used when we generate our table of short strings, so the compiler is
3165 * happier if we use |c| as few times as possible.
3167 #define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' : \
3168 (c) < 36 ? 'a' - 10 : \
3169 'A' - 36))
3170 #define R FROM_SMALL_CHAR
3172 const jschar JSString::fromSmallChar[] = { R6(0) };
3174 #undef R
3177 * For code-generation ease, length-2 strings are encoded as 12-bit int values,
3178 * where the upper 6 bits is the first character and the lower 6 bits is the
3179 * second character.
3181 #define R(c) { \
3182 BUILD_LENGTH_AND_FLAGS(2, JSString::FLAT | JSString::ATOMIZED), \
3183 { (jschar *)(((char *)(length2StringTable + (c))) + \
3184 offsetof(JSString, inlineStorage)) }, \
3185 { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
3187 #ifdef __SUNPRO_CC
3188 #pragma pack(8)
3189 #else
3190 #pragma pack(push, 8)
3191 #endif
3193 const JSString JSString::length2StringTable[]
3194 #ifdef __GNUC__
3195 __attribute__ ((aligned (8)))
3196 #endif
3197 = { R12(0) };
3199 #ifdef __SUNPRO_CC
3200 #pragma pack(0)
3201 #else
3202 #pragma pack(pop)
3203 #endif
3205 #undef R
3208 * Declare int strings. Only int strings from 100 to 255 actually have to be
3209 * generated, since the rest are either unit strings or length-2 strings. To
3210 * avoid the runtime cost of figuring out where to look for the string for a
3211 * particular integer, we precompute a table of JSString*s which refer to the
3212 * correct location of the int string.
3214 #define R(c) { \
3215 BUILD_LENGTH_AND_FLAGS(3, JSString::FLAT | JSString::ATOMIZED), \
3216 { (jschar *)(((char *)(hundredStringTable + ((c) - 100))) + \
3217 offsetof(JSString, inlineStorage)) }, \
3218 { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
3221 JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256);
3223 #ifdef __SUNPRO_CC
3224 #pragma pack(8)
3225 #else
3226 #pragma pack(push, 8)
3227 #endif
3229 const JSString JSString::hundredStringTable[]
3230 #ifdef __GNUC__
3231 __attribute__ ((aligned (8)))
3232 #endif
3233 = { R7(100), /* 100 through 227 */
3234 R4(100 + (1 << 7)), /* 228 through 243 */
3235 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3236 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3239 #undef R
3241 #define R(c) ((c) < 10 ? JSString::unitStringTable + ((c) + '0') : \
3242 (c) < 100 ? JSString::length2StringTable + \
3243 ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) + \
3244 TO_SMALL_CHAR(((c) % 10) + '0') : \
3245 JSString::hundredStringTable + ((c) - 100))
3247 const JSString *const JSString::intStringTable[] = { R8(0) };
3249 #undef R
3251 #ifdef __SUNPRO_CC
3252 #pragma pack(0)
3253 #else
3254 #pragma pack(pop)
3255 #endif
3257 #undef R2
3258 #undef R4
3259 #undef R6
3260 #undef R8
3261 #undef R10
3262 #undef R12
3264 #undef R3
3265 #undef R7
3267 JSBool
3268 js_String(JSContext *cx, uintN argc, Value *vp)
3270 Value *argv = vp + 2;
3272 JSString *str;
3273 if (argc > 0) {
3274 str = js_ValueToString(cx, argv[0]);
3275 if (!str)
3276 return false;
3277 } else {
3278 str = cx->runtime->emptyString;
3281 if (IsConstructing(vp)) {
3282 JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass);
3283 if (!obj)
3284 return false;
3285 obj->setPrimitiveThis(StringValue(str));
3286 vp->setObject(*obj);
3287 } else {
3288 vp->setString(str);
3290 return true;
3293 static JSBool
3294 str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
3296 Value *argv;
3297 uintN i;
3298 jschar *chars;
3299 JSString *str;
3301 argv = vp + 2;
3302 JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
3303 if (argc == 1) {
3304 uint16_t code;
3305 if (!ValueToUint16(cx, argv[0], &code))
3306 return JS_FALSE;
3307 if (code < UNIT_STRING_LIMIT) {
3308 str = JSString::unitString(code);
3309 if (!str)
3310 return JS_FALSE;
3311 vp->setString(str);
3312 return JS_TRUE;
3314 argv[0].setInt32(code);
3316 chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar));
3317 if (!chars)
3318 return JS_FALSE;
3319 for (i = 0; i < argc; i++) {
3320 uint16_t code;
3321 if (!ValueToUint16(cx, argv[i], &code)) {
3322 cx->free(chars);
3323 return JS_FALSE;
3325 chars[i] = (jschar)code;
3327 chars[i] = 0;
3328 str = js_NewString(cx, chars, argc);
3329 if (!str) {
3330 cx->free(chars);
3331 return JS_FALSE;
3333 vp->setString(str);
3334 return JS_TRUE;
3337 #ifdef JS_TRACER
3338 static JSString* FASTCALL
3339 String_fromCharCode(JSContext* cx, int32 i)
3341 JS_ASSERT(JS_ON_TRACE(cx));
3342 jschar c = (jschar)i;
3343 if (c < UNIT_STRING_LIMIT)
3344 return JSString::unitString(c);
3345 return js_NewStringCopyN(cx, &c, 1);
3347 #endif
3349 JS_DEFINE_TRCINFO_1(str_fromCharCode,
3350 (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE)))
3352 static JSFunctionSpec string_static_methods[] = {
3353 JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo),
3354 JS_FS_END
3357 JSObject *
3358 js_InitStringClass(JSContext *cx, JSObject *obj)
3360 JSObject *proto;
3362 /* Define the escape, unescape functions in the global object. */
3363 if (!JS_DefineFunctions(cx, obj, string_functions))
3364 return NULL;
3366 proto = js_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
3367 NULL, string_methods,
3368 NULL, string_static_methods);
3369 if (!proto)
3370 return NULL;
3371 proto->setPrimitiveThis(StringValue(cx->runtime->emptyString));
3372 if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
3373 UndefinedValue(), NULL, NULL,
3374 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
3375 NULL)) {
3376 return JS_FALSE;
3379 return proto;
3382 JSFlatString *
3383 js_NewString(JSContext *cx, jschar *chars, size_t length)
3385 JSString *str;
3387 if (length > JSString::MAX_LENGTH) {
3388 if (JS_ON_TRACE(cx)) {
3390 * If we can't leave the trace, signal OOM condition, otherwise
3391 * exit from trace before throwing.
3393 if (!CanLeaveTrace(cx))
3394 return NULL;
3396 LeaveTrace(cx);
3398 js_ReportAllocationOverflow(cx);
3399 return NULL;
3402 str = js_NewGCString(cx);
3403 if (!str)
3404 return NULL;
3405 str->initFlat(chars, length);
3406 #ifdef DEBUG
3408 JSRuntime *rt = cx->runtime;
3409 JS_RUNTIME_METER(rt, liveStrings);
3410 JS_RUNTIME_METER(rt, totalStrings);
3411 JS_LOCK_RUNTIME_VOID(rt,
3412 (rt->lengthSum += (double)length,
3413 rt->lengthSquaredSum += (double)length * (double)length));
3415 #endif
3416 return str->assertIsFlat();
3419 static JS_ALWAYS_INLINE JSFlatString *
3420 NewShortString(JSContext *cx, const jschar *chars, size_t length)
3422 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3423 JSShortString *str = js_NewGCShortString(cx);
3424 if (!str)
3425 return NULL;
3426 jschar *storage = str->init(length);
3427 js_short_strncpy(storage, chars, length);
3428 storage[length] = 0;
3429 return str->header()->assertIsFlat();
3432 static JSFlatString *
3433 NewShortString(JSContext *cx, const char *chars, size_t length)
3435 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3436 JSShortString *str = js_NewGCShortString(cx);
3437 if (!str)
3438 return NULL;
3439 jschar *storage = str->init(length);
3441 if (js_CStringsAreUTF8) {
3442 #ifdef DEBUG
3443 size_t oldLength = length;
3444 #endif
3445 if (!js_InflateStringToBuffer(cx, chars, length, storage, &length))
3446 return NULL;
3447 JS_ASSERT(length <= oldLength);
3448 storage[length] = 0;
3449 str->resetLength(length);
3450 } else {
3451 size_t n = length;
3452 jschar *p = storage;
3453 while (n--)
3454 *p++ = (unsigned char)*chars++;
3455 *p = 0;
3457 return str->header()->assertIsFlat();
3460 static const size_t sMinWasteSize = 16;
3462 JSFlatString *
3463 js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb)
3465 if (cb.empty())
3466 return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
3468 size_t length = cb.length();
3470 JS_STATIC_ASSERT(JSShortString::MAX_SHORT_STRING_LENGTH < JSCharBuffer::InlineLength);
3471 if (JSShortString::fitsIntoShortString(length))
3472 return NewShortString(cx, cb.begin(), length);
3474 if (!cb.append('\0'))
3475 return NULL;
3477 size_t capacity = cb.capacity();
3479 jschar *buf = cb.extractRawBuffer();
3480 if (!buf)
3481 return NULL;
3483 /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
3484 JS_ASSERT(capacity >= length);
3485 if (capacity > sMinWasteSize && capacity - length > (length >> 2)) {
3486 size_t bytes = sizeof(jschar) * (length + 1);
3487 jschar *tmp = (jschar *)cx->realloc(buf, bytes);
3488 if (!tmp) {
3489 cx->free(buf);
3490 return NULL;
3492 buf = tmp;
3495 JSFlatString *str = js_NewString(cx, buf, length);
3496 if (!str)
3497 cx->free(buf);
3498 return str;
3501 JSLinearString *
3502 js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start,
3503 size_t length)
3505 JSString *ds;
3507 if (length == 0)
3508 return cx->runtime->emptyString;
3510 JSLinearString *base = baseArg->ensureLinear(cx);
3511 if (!base)
3512 return NULL;
3514 if (start == 0 && length == base->length())
3515 return base;
3517 const jschar *chars = base->chars() + start;
3519 JSLinearString *staticStr = JSString::lookupStaticString(chars, length);
3520 if (staticStr)
3521 return staticStr;
3523 /* Try to avoid long chains of dependent strings. */
3524 while (base->isDependent())
3525 base = base->dependentBase();
3527 JS_ASSERT(base->isFlat());
3529 ds = js_NewGCString(cx);
3530 if (!ds)
3531 return NULL;
3532 ds->initDependent(base, chars, length);
3533 #ifdef DEBUG
3535 JSRuntime *rt = cx->runtime;
3536 JS_RUNTIME_METER(rt, liveDependentStrings);
3537 JS_RUNTIME_METER(rt, totalDependentStrings);
3538 JS_RUNTIME_METER(rt, liveStrings);
3539 JS_RUNTIME_METER(rt, totalStrings);
3540 JS_LOCK_RUNTIME_VOID(rt,
3541 (rt->strdepLengthSum += (double)length,
3542 rt->strdepLengthSquaredSum += (double)length * (double)length));
3543 JS_LOCK_RUNTIME_VOID(rt,
3544 (rt->lengthSum += (double)length,
3545 rt->lengthSquaredSum += (double)length * (double)length));
3547 #endif
3548 return ds->assertIsLinear();
3551 #ifdef DEBUG
3552 #include <math.h>
3554 void printJSStringStats(JSRuntime *rt)
3556 double mean, sigma;
3558 mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum,
3559 rt->lengthSquaredSum, &sigma);
3561 fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
3562 (unsigned long)rt->totalStrings, mean, sigma);
3564 mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum,
3565 rt->strdepLengthSquaredSum, &sigma);
3567 fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n",
3568 (unsigned long)rt->totalDependentStrings, mean, sigma);
3570 #endif
3572 JSFlatString *
3573 js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
3575 if (JSShortString::fitsIntoShortString(n))
3576 return NewShortString(cx, s, n);
3578 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
3579 if (!news)
3580 return NULL;
3581 js_strncpy(news, s, n);
3582 news[n] = 0;
3583 JSFlatString *str = js_NewString(cx, news, n);
3584 if (!str)
3585 cx->free(news);
3586 return str;
3589 JSFlatString *
3590 js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
3592 if (JSShortString::fitsIntoShortString(n))
3593 return NewShortString(cx, s, n);
3595 jschar *chars = js_InflateString(cx, s, &n);
3596 if (!chars)
3597 return NULL;
3598 JSFlatString *str = js_NewString(cx, chars, n);
3599 if (!str)
3600 cx->free(chars);
3601 return str;
3604 JSFlatString *
3605 js_NewStringCopyZ(JSContext *cx, const jschar *s)
3607 size_t n = js_strlen(s);
3608 if (JSShortString::fitsIntoShortString(n))
3609 return NewShortString(cx, s, n);
3611 size_t m = (n + 1) * sizeof(jschar);
3612 jschar *news = (jschar *) cx->malloc(m);
3613 if (!news)
3614 return NULL;
3615 memcpy(news, s, m);
3616 JSFlatString *str = js_NewString(cx, news, n);
3617 if (!str)
3618 cx->free(news);
3619 return str;
3622 JSFlatString *
3623 js_NewStringCopyZ(JSContext *cx, const char *s)
3625 return js_NewStringCopyN(cx, s, strlen(s));
3628 const char *
3629 js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
3631 JSString *str;
3633 str = (asSource ? js_ValueToSource : js_ValueToString)(cx, v);
3634 if (!str)
3635 return NULL;
3636 str = js_QuoteString(cx, str, 0);
3637 if (!str)
3638 return NULL;
3639 return bytes->encode(cx, str);
3642 JSString *
3643 js_ValueToString(JSContext *cx, const Value &arg)
3645 Value v = arg;
3646 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3647 return NULL;
3649 JSString *str;
3650 if (v.isString()) {
3651 str = v.toString();
3652 } else if (v.isInt32()) {
3653 str = js_IntToString(cx, v.toInt32());
3654 } else if (v.isDouble()) {
3655 str = js_NumberToString(cx, v.toDouble());
3656 } else if (v.isBoolean()) {
3657 str = js_BooleanToString(cx, v.toBoolean());
3658 } else if (v.isNull()) {
3659 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3660 } else {
3661 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3663 return str;
3666 static inline JSBool
3667 AppendAtom(JSAtom *atom, JSCharBuffer &cb)
3669 return cb.append(atom->chars(), atom->length());
3672 /* This function implements E-262-3 section 9.8, toString. */
3673 JSBool
3674 js_ValueToCharBuffer(JSContext *cx, const Value &arg, JSCharBuffer &cb)
3676 Value v = arg;
3677 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3678 return false;
3680 if (v.isString()) {
3681 JSString *str = v.toString();
3682 size_t length = str->length();
3683 const jschar *chars = str->getChars(cx);
3684 if (!chars)
3685 return false;
3686 return cb.append(chars, length);
3688 if (v.isNumber())
3689 return js_NumberValueToCharBuffer(cx, v, cb);
3690 if (v.isBoolean())
3691 return js_BooleanToCharBuffer(cx, v.toBoolean(), cb);
3692 if (v.isNull())
3693 return AppendAtom(cx->runtime->atomState.nullAtom, cb);
3694 JS_ASSERT(v.isUndefined());
3695 return AppendAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb);
3698 JS_FRIEND_API(JSString *)
3699 js_ValueToSource(JSContext *cx, const Value &v)
3701 if (v.isUndefined())
3702 return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
3703 if (v.isString())
3704 return js_QuoteString(cx, v.toString(), '"');
3705 if (v.isPrimitive()) {
3706 /* Special case to preserve negative zero, _contra_ toString. */
3707 if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
3708 /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
3709 static const jschar js_negzero_ucNstr[] = {'-', '0'};
3711 return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
3713 return js_ValueToString(cx, v);
3716 JSAtom *atom = cx->runtime->atomState.toSourceAtom;
3717 AutoValueRooter tvr(cx);
3718 if (!js_TryMethod(cx, &v.toObject(), atom, 0, NULL, tvr.addr()))
3719 return NULL;
3720 return js_ValueToString(cx, tvr.value());
3723 namespace js {
3726 * str is not necessarily a GC thing here.
3728 static JS_ALWAYS_INLINE bool
3729 EqualStringsTail(JSLinearString *str1, size_t length1, JSLinearString *str2)
3731 const jschar *s1 = str1->chars();
3732 const jschar *s1end = s1 + length1;
3733 const jschar *s2 = str2->chars();
3734 do {
3735 if (*s1 != *s2) {
3736 return false;
3738 ++s1, ++s2;
3739 } while (s1 != s1end);
3741 return true;
3744 bool
3745 EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result)
3747 if (str1 == str2) {
3748 *result = true;
3749 return true;
3752 size_t length1 = str1->length();
3753 if (length1 != str2->length()) {
3754 *result = false;
3755 return true;
3758 if (length1 == 0) {
3759 *result = true;
3760 return true;
3763 JSLinearString *linear1 = str1->ensureLinear(cx);
3764 if (!linear1)
3765 return false;
3766 JSLinearString *linear2 = str2->ensureLinear(cx);
3767 if (!linear2)
3768 return false;
3770 *result = EqualStringsTail(linear1, length1, linear2);
3771 return true;
3774 bool
3775 EqualStrings(JSLinearString *str1, JSLinearString *str2)
3777 if (str1 == str2)
3778 return true;
3780 size_t length1 = str1->length();
3781 if (length1 != str2->length())
3782 return false;
3784 if (length1 == 0)
3785 return true;
3787 return EqualStringsTail(str1, length1, str2);
3790 } /* namespace js */
3792 JSBool JS_FASTCALL
3793 js_EqualStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2)
3795 JSBool result;
3796 return EqualStrings(cx, str1, str2, &result) ? result : JS_NEITHER;
3798 JS_DEFINE_CALLINFO_3(extern, BOOL, js_EqualStringsOnTrace,
3799 CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE)
3801 namespace js {
3803 static bool
3804 CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
3806 JS_ASSERT(str1);
3807 JS_ASSERT(str2);
3809 if (str1 == str2) {
3810 *result = 0;
3811 return true;
3814 size_t l1 = str1->length();
3815 const jschar *s1 = str1->getChars(cx);
3816 if (!s1)
3817 return false;
3819 size_t l2 = str2->length();
3820 const jschar *s2 = str2->getChars(cx);
3821 if (!s2)
3822 return false;
3824 size_t n = JS_MIN(l1, l2);
3825 for (size_t i = 0; i < n; i++) {
3826 if (int32 cmp = s1[i] - s2[i]) {
3827 *result = cmp;
3828 return true;
3831 *result = (int32)(l1 - l2);
3832 return true;
3835 bool
3836 CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
3838 return CompareStringsImpl(cx, str1, str2, result);
3841 } /* namespace js */
3843 int32 JS_FASTCALL
3844 js_CompareStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2)
3846 int32 result;
3847 if (!CompareStringsImpl(cx, str1, str2, &result))
3848 return INT32_MIN;
3849 JS_ASSERT(result != INT32_MIN);
3850 return result;
3852 JS_DEFINE_CALLINFO_3(extern, INT32, js_CompareStringsOnTrace,
3853 CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE)
3855 namespace js {
3857 bool
3858 StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
3860 size_t length = strlen(asciiBytes);
3861 #ifdef DEBUG
3862 for (size_t i = 0; i != length; ++i)
3863 JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
3864 #endif
3865 if (length != str->length())
3866 return false;
3867 const jschar *chars = str->chars();
3868 for (size_t i = 0; i != length; ++i) {
3869 if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
3870 return false;
3872 return true;
3875 } /* namespacejs */
3877 size_t
3878 js_strlen(const jschar *s)
3880 const jschar *t;
3882 for (t = s; *t != 0; t++)
3883 continue;
3884 return (size_t)(t - s);
3887 jschar *
3888 js_strchr(const jschar *s, jschar c)
3890 while (*s != 0) {
3891 if (*s == c)
3892 return (jschar *)s;
3893 s++;
3895 return NULL;
3898 jschar *
3899 js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
3901 while (s < limit) {
3902 if (*s == c)
3903 return (jschar *)s;
3904 s++;
3906 return NULL;
3909 jschar *
3910 js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)
3912 size_t nbytes, nchars, i;
3913 jschar *chars;
3914 #ifdef DEBUG
3915 JSBool ok;
3916 #endif
3918 nbytes = *lengthp;
3919 if (js_CStringsAreUTF8) {
3920 if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))
3921 goto bad;
3922 chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar));
3923 if (!chars)
3924 goto bad;
3925 #ifdef DEBUG
3926 ok =
3927 #endif
3928 js_InflateStringToBuffer(cx, bytes, nbytes, chars, &nchars);
3929 JS_ASSERT(ok);
3930 } else {
3931 nchars = nbytes;
3932 chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
3933 if (!chars)
3934 goto bad;
3935 for (i = 0; i < nchars; i++)
3936 chars[i] = (unsigned char) bytes[i];
3938 *lengthp = nchars;
3939 chars[nchars] = 0;
3940 return chars;
3942 bad:
3944 * For compatibility with callers of JS_DecodeBytes we must zero lengthp
3945 * on errors.
3947 *lengthp = 0;
3948 return NULL;
3952 * May be called with null cx.
3954 char *
3955 js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
3957 size_t nbytes, i;
3958 char *bytes;
3959 #ifdef DEBUG
3960 JSBool ok;
3961 #endif
3963 if (js_CStringsAreUTF8) {
3964 nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
3965 if (nbytes == (size_t) -1)
3966 return NULL;
3967 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3968 if (!bytes)
3969 return NULL;
3970 #ifdef DEBUG
3971 ok =
3972 #endif
3973 js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes);
3974 JS_ASSERT(ok);
3975 } else {
3976 nbytes = nchars;
3977 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3978 if (!bytes)
3979 return NULL;
3980 for (i = 0; i < nbytes; i++)
3981 bytes[i] = (char) chars[i];
3983 bytes[nbytes] = 0;
3984 return bytes;
3987 size_t
3988 js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
3990 if (!js_CStringsAreUTF8)
3991 return nchars;
3993 return js_GetDeflatedUTF8StringLength(cx, chars, nchars);
3997 * May be called with null cx through public API, see below.
3999 size_t
4000 js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, size_t nchars)
4002 size_t nbytes;
4003 const jschar *end;
4004 uintN c, c2;
4005 char buffer[10];
4007 nbytes = nchars;
4008 for (end = chars + nchars; chars != end; chars++) {
4009 c = *chars;
4010 if (c < 0x80)
4011 continue;
4012 if (0xD800 <= c && c <= 0xDFFF) {
4013 /* Surrogate pair. */
4014 chars++;
4016 /* nbytes sets 1 length since this is surrogate pair. */
4017 nbytes--;
4018 if (c >= 0xDC00 || chars == end)
4019 goto bad_surrogate;
4020 c2 = *chars;
4021 if (c2 < 0xDC00 || c2 > 0xDFFF)
4022 goto bad_surrogate;
4023 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4025 c >>= 11;
4026 nbytes++;
4027 while (c) {
4028 c >>= 5;
4029 nbytes++;
4032 return nbytes;
4034 bad_surrogate:
4035 if (cx) {
4036 JS_snprintf(buffer, 10, "0x%x", c);
4037 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
4038 NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
4040 return (size_t) -1;
4043 JSBool
4044 js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
4045 char *dst, size_t *dstlenp)
4047 size_t dstlen, i;
4049 dstlen = *dstlenp;
4050 if (!js_CStringsAreUTF8) {
4051 if (srclen > dstlen) {
4052 for (i = 0; i < dstlen; i++)
4053 dst[i] = (char) src[i];
4054 if (cx) {
4055 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4056 JSMSG_BUFFER_TOO_SMALL);
4058 return JS_FALSE;
4060 for (i = 0; i < srclen; i++)
4061 dst[i] = (char) src[i];
4062 *dstlenp = srclen;
4063 return JS_TRUE;
4066 return js_DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
4069 JSBool
4070 js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
4071 char *dst, size_t *dstlenp)
4073 size_t dstlen, i, origDstlen, utf8Len;
4074 jschar c, c2;
4075 uint32 v;
4076 uint8 utf8buf[6];
4078 dstlen = *dstlenp;
4079 origDstlen = dstlen;
4080 while (srclen) {
4081 c = *src++;
4082 srclen--;
4083 if ((c >= 0xDC00) && (c <= 0xDFFF))
4084 goto badSurrogate;
4085 if (c < 0xD800 || c > 0xDBFF) {
4086 v = c;
4087 } else {
4088 if (srclen < 1)
4089 goto badSurrogate;
4090 c2 = *src;
4091 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
4092 goto badSurrogate;
4093 src++;
4094 srclen--;
4095 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4097 if (v < 0x0080) {
4098 /* no encoding necessary - performance hack */
4099 if (dstlen == 0)
4100 goto bufferTooSmall;
4101 *dst++ = (char) v;
4102 utf8Len = 1;
4103 } else {
4104 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
4105 if (utf8Len > dstlen)
4106 goto bufferTooSmall;
4107 for (i = 0; i < utf8Len; i++)
4108 *dst++ = (char) utf8buf[i];
4110 dstlen -= utf8Len;
4112 *dstlenp = (origDstlen - dstlen);
4113 return JS_TRUE;
4115 badSurrogate:
4116 *dstlenp = (origDstlen - dstlen);
4117 /* Delegate error reporting to the measurement function. */
4118 if (cx)
4119 js_GetDeflatedStringLength(cx, src - 1, srclen + 1);
4120 return JS_FALSE;
4122 bufferTooSmall:
4123 *dstlenp = (origDstlen - dstlen);
4124 if (cx) {
4125 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4126 JSMSG_BUFFER_TOO_SMALL);
4128 return JS_FALSE;
4131 JSBool
4132 js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
4133 jschar *dst, size_t *dstlenp)
4135 size_t dstlen, i;
4137 if (!js_CStringsAreUTF8) {
4138 if (dst) {
4139 dstlen = *dstlenp;
4140 if (srclen > dstlen) {
4141 for (i = 0; i < dstlen; i++)
4142 dst[i] = (unsigned char) src[i];
4143 if (cx) {
4144 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4145 JSMSG_BUFFER_TOO_SMALL);
4147 return JS_FALSE;
4149 for (i = 0; i < srclen; i++)
4150 dst[i] = (unsigned char) src[i];
4152 *dstlenp = srclen;
4153 return JS_TRUE;
4156 return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
4159 JSBool
4160 js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
4161 jschar *dst, size_t *dstlenp)
4163 size_t dstlen, origDstlen, offset, j, n;
4164 uint32 v;
4166 dstlen = dst ? *dstlenp : (size_t) -1;
4167 origDstlen = dstlen;
4168 offset = 0;
4170 while (srclen) {
4171 v = (uint8) *src;
4172 n = 1;
4173 if (v & 0x80) {
4174 while (v & (0x80 >> n))
4175 n++;
4176 if (n > srclen)
4177 goto bufferTooSmall;
4178 if (n == 1 || n > 4)
4179 goto badCharacter;
4180 for (j = 1; j < n; j++) {
4181 if ((src[j] & 0xC0) != 0x80)
4182 goto badCharacter;
4184 v = Utf8ToOneUcs4Char((uint8 *)src, n);
4185 if (v >= 0x10000) {
4186 v -= 0x10000;
4187 if (v > 0xFFFFF || dstlen < 2) {
4188 *dstlenp = (origDstlen - dstlen);
4189 if (cx) {
4190 char buffer[10];
4191 JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
4192 JS_ReportErrorFlagsAndNumber(cx,
4193 JSREPORT_ERROR,
4194 js_GetErrorMessage, NULL,
4195 JSMSG_UTF8_CHAR_TOO_LARGE,
4196 buffer);
4198 return JS_FALSE;
4200 if (dst) {
4201 *dst++ = (jschar)((v >> 10) + 0xD800);
4202 v = (jschar)((v & 0x3FF) + 0xDC00);
4204 dstlen--;
4207 if (!dstlen)
4208 goto bufferTooSmall;
4209 if (dst)
4210 *dst++ = (jschar) v;
4211 dstlen--;
4212 offset += n;
4213 src += n;
4214 srclen -= n;
4216 *dstlenp = (origDstlen - dstlen);
4217 return JS_TRUE;
4219 badCharacter:
4220 *dstlenp = (origDstlen - dstlen);
4221 if (cx) {
4222 char buffer[10];
4223 JS_snprintf(buffer, 10, "%d", offset);
4224 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
4225 js_GetErrorMessage, NULL,
4226 JSMSG_MALFORMED_UTF8_CHAR,
4227 buffer);
4229 return JS_FALSE;
4231 bufferTooSmall:
4232 *dstlenp = (origDstlen - dstlen);
4233 if (cx) {
4234 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4235 JSMSG_BUFFER_TOO_SMALL);
4237 return JS_FALSE;
4241 * From java.lang.Character.java:
4243 * The character properties are currently encoded into 32 bits in the
4244 * following manner:
4246 * 10 bits signed offset used for converting case
4247 * 1 bit if 1, adding the signed offset converts the character to
4248 * lowercase
4249 * 1 bit if 1, subtracting the signed offset converts the character to
4250 * uppercase
4251 * 1 bit if 1, character has a titlecase equivalent (possibly itself)
4252 * 3 bits 0 may not be part of an identifier
4253 * 1 ignorable control; may continue a Unicode identifier or JS
4254 * identifier
4255 * 2 may continue a JS identifier but not a Unicode identifier
4256 * (unused)
4257 * 3 may continue a Unicode identifier or JS identifier
4258 * 4 is a JS whitespace character
4259 * 5 may start or continue a JS identifier;
4260 * may continue but not start a Unicode identifier (_)
4261 * 6 may start or continue a JS identifier but not a Unicode
4262 * identifier ($)
4263 * 7 may start or continue a Unicode identifier or JS identifier
4264 * Thus:
4265 * 5, 6, 7 may start a JS identifier
4266 * 1, 2, 3, 5, 6, 7 may continue a JS identifier
4267 * 7 may start a Unicode identifier
4268 * 1, 3, 5, 7 may continue a Unicode identifier
4269 * 1 is ignorable within an identifier
4270 * 4 is JS whitespace
4271 * 2 bits 0 this character has no numeric property
4272 * 1 adding the digit offset to the character code and then
4273 * masking with 0x1F will produce the desired numeric value
4274 * 2 this character has a "strange" numeric value
4275 * 3 a JS supradecimal digit: adding the digit offset to the
4276 * character code, then masking with 0x1F, then adding 10
4277 * will produce the desired numeric value
4278 * 5 bits digit offset
4279 * 1 bit XML 1.0 name start character
4280 * 1 bit XML 1.0 name character
4281 * 2 bits reserved for future use
4282 * 5 bits character type
4285 /* The X table has 1024 entries for a total of 1024 bytes. */
4287 const uint8 js_X[] = {
4288 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */
4289 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */
4290 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */
4291 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */
4292 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */
4293 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */
4294 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */
4295 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */
4296 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */
4297 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */
4298 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */
4299 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */
4300 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */
4301 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */
4302 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */
4303 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */
4304 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */
4305 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */
4306 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */
4307 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */
4308 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */
4309 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */
4310 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */
4311 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */
4312 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */
4313 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */
4314 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */
4315 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */
4316 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */
4317 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */
4318 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */
4319 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */
4320 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */
4321 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */
4322 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */
4323 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */
4324 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */
4325 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */
4326 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */
4327 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */
4328 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */
4329 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */
4330 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */
4331 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */
4332 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */
4333 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */
4334 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */
4335 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */
4336 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */
4337 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */
4338 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */
4339 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */
4340 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */
4341 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */
4342 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */
4343 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */
4344 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */
4345 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */
4346 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */
4347 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */
4348 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */
4349 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */
4350 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */
4351 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */
4352 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */
4353 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */
4354 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */
4355 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */
4356 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */
4357 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */
4358 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */
4359 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */
4360 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */
4361 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */
4362 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */
4363 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */
4364 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */
4365 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */
4366 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */
4367 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */
4368 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */
4369 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */
4370 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */
4371 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */
4372 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */
4373 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */
4374 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */
4375 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */
4376 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */
4377 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */
4378 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */
4379 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */
4380 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */
4381 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */
4382 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */
4383 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */
4384 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */
4385 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */
4386 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */
4387 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */
4388 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */
4389 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */
4390 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */
4391 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */
4392 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */
4393 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */
4394 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */
4395 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */
4396 104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */
4397 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */
4398 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */
4399 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */
4400 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */
4401 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */
4402 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */
4403 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */
4404 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */
4405 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */
4406 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */
4407 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */
4408 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */
4409 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */
4410 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */
4411 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */
4412 105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */
4413 106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */
4414 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */
4415 115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */
4418 /* The Y table has 7808 entries for a total of 7808 bytes. */
4420 const uint8 js_Y[] = {
4421 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4422 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */
4423 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4424 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4425 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */
4426 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */
4427 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */
4428 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */
4429 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4430 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4431 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4432 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */
4433 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4434 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4435 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4436 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */
4437 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4438 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4439 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4440 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4441 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */
4442 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */
4443 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */
4444 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */
4445 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4446 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4447 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */
4448 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */
4449 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4450 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4451 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */
4452 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */
4453 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4454 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4455 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4456 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4457 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4458 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4459 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */
4460 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */
4461 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */
4462 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */
4463 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4464 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4465 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4466 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4467 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4468 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */
4469 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */
4470 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */
4471 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */
4472 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */
4473 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */
4474 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */
4475 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */
4476 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */
4477 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */
4478 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */
4479 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */
4480 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */
4481 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4482 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4483 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */
4484 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */
4485 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4486 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4487 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4488 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4489 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4490 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4491 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4492 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4493 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4494 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4495 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */
4496 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */
4497 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */
4498 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */
4499 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */
4500 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */
4501 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */
4502 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */
4503 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */
4504 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4505 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4506 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */
4507 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */
4508 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */
4509 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4510 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */
4511 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4512 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */
4513 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */
4514 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */
4515 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4516 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4517 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4518 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4519 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4520 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4521 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4522 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4523 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4524 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4525 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */
4526 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4527 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4528 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4529 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */
4530 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4531 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */
4532 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */
4533 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */
4534 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */
4535 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4536 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4537 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */
4538 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */
4539 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4540 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4541 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */
4542 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */
4543 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */
4544 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */
4545 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */
4546 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */
4547 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */
4548 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */
4549 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */
4550 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */
4551 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4552 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4553 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4554 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4555 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4556 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4557 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4558 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4559 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */
4560 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */
4561 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4562 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4563 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4564 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4565 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */
4566 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */
4567 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4568 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4569 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4570 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4571 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4572 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4573 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */
4574 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */
4575 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4576 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4577 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4578 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */
4579 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */
4580 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */
4581 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4582 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4583 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4584 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4585 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4586 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4587 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4588 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4589 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4590 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4591 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */
4592 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */
4593 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4594 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4595 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4596 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4597 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */
4598 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */
4599 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4600 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4601 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */
4602 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4603 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4604 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */
4605 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */
4606 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4607 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4608 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4609 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4610 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */
4611 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */
4612 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4613 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4614 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */
4615 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4616 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */
4617 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4618 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4619 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4620 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */
4621 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4622 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */
4623 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */
4624 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */
4625 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */
4626 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */
4627 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4628 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4629 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4630 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4631 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4632 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4633 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4634 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4635 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4636 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */
4637 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */
4638 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */
4639 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */
4640 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */
4641 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */
4642 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */
4643 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */
4644 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */
4645 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4646 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4647 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4648 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4649 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4650 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4651 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4652 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4653 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */
4654 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4655 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4656 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4657 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4658 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4659 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4660 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */
4661 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */
4662 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */
4663 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */
4664 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */
4665 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */
4666 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */
4667 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4668 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4669 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */
4670 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */
4671 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */
4672 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4673 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4674 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */
4675 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */
4676 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */
4677 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */
4678 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */
4679 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */
4680 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */
4681 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */
4682 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */
4683 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */
4684 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */
4685 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */
4686 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */
4687 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */
4688 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4689 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4690 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */
4691 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */
4692 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */
4693 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */
4694 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */
4695 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4696 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */
4697 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */
4698 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */
4699 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */
4700 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4701 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */
4702 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */
4703 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */
4704 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4705 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4706 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */
4707 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */
4708 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */
4709 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */
4710 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */
4711 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4712 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4713 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */
4714 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */
4715 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4716 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4717 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */
4718 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */
4719 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */
4720 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4721 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4722 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */
4723 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */
4724 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */
4725 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */
4726 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */
4727 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */
4728 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */
4729 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */
4730 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */
4731 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4732 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4733 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */
4734 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4735 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */
4736 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */
4737 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */
4738 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4739 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */
4740 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */
4741 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */
4742 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */
4743 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */
4744 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4745 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */
4746 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */
4747 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */
4748 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4749 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */
4750 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */
4751 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4752 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4753 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4754 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4755 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */
4756 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */
4757 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */
4758 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */
4759 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */
4760 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4761 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */
4762 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */
4763 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4764 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4765 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */
4766 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */
4767 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4768 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4769 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4770 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4771 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */
4772 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */
4773 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */
4774 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */
4775 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */
4776 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */
4777 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */
4778 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */
4779 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4780 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4781 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */
4782 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */
4783 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4784 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4785 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4786 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4787 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4788 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */
4789 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */
4790 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */
4791 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */
4792 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4793 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */
4794 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */
4795 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4796 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4797 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4798 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4799 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4800 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4801 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4802 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */
4803 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */
4804 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */
4805 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */
4806 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */
4807 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */
4808 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */
4809 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4810 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4811 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4812 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4813 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */
4814 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */
4815 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */
4816 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */
4817 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */
4818 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */
4819 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */
4820 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */
4821 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */
4822 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */
4823 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */
4824 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */
4825 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4826 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4827 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4828 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4829 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */
4830 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */
4831 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */
4832 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */
4833 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */
4834 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */
4835 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */
4836 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */
4837 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4838 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4839 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4840 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4841 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4842 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */
4843 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */
4844 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */
4845 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */
4846 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */
4847 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */
4848 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4849 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4850 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */
4851 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4852 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */
4853 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4854 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4855 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4856 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4857 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4858 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4859 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4860 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4861 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */
4862 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */
4863 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4864 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4865 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4866 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4867 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */
4868 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */
4869 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4870 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4871 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4872 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4873 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4874 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4875 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4876 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4877 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4878 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4879 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4880 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */
4881 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4882 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4883 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4884 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4885 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4886 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4887 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4888 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4889 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */
4890 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4891 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4892 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4893 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4894 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4895 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4896 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4897 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4898 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4899 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4900 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */
4901 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4902 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4903 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4904 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4905 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4906 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4907 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4908 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4909 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4910 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4911 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */
4912 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */
4913 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4914 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4915 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4916 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4917 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4918 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4919 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4920 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4921 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4922 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4923 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4924 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */
4925 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4926 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4927 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */
4928 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */
4929 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4930 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4931 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4932 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4933 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */
4934 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */
4935 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */
4936 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */
4937 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */
4938 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */
4939 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */
4940 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */
4941 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4942 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4943 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4944 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4945 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4946 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4947 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */
4948 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */
4949 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */
4950 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */
4951 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */
4952 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */
4953 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */
4954 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */
4955 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */
4956 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */
4957 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */
4958 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */
4959 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */
4960 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */
4961 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
4962 105, 106, 104, 104, 104, 104, 104, 46, /* 67 */
4963 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
4964 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */
4965 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */
4966 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4967 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4968 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4969 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4970 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */
4971 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */
4972 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */
4973 107, 107, 107, 107, 107, 107, 107, 107, /* 69 */
4974 107, 107, 7, 7, 7, 5, 6, 46, /* 69 */
4975 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4976 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4977 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */
4978 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */
4979 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4980 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4981 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4982 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4983 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */
4984 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */
4985 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */
4986 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4987 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4988 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4989 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */
4990 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */
4991 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */
4992 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */
4993 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */
4994 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */
4995 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */
4996 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */
4997 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
4998 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
4999 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */
5000 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */
5001 109, 109, 109, 109, 109, 109, 109, 109, /* 72 */
5002 109, 109, 109, 109, 110, 110, 110, 110, /* 72 */
5003 111, 111, 111, 111, 111, 111, 111, 111, /* 72 */
5004 111, 111, 111, 111, 112, 112, 112, 112, /* 72 */
5005 113, 113, 113, 46, 46, 46, 46, 46, /* 73 */
5006 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */
5007 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */
5008 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5009 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5010 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5011 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5012 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5013 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5014 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5015 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */
5016 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5017 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5018 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */
5019 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5020 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5021 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5022 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5023 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5024 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5025 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5026 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5027 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5028 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5029 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5030 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5031 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5032 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5033 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5034 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5035 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */
5036 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */
5037 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */
5038 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */
5039 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5040 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5041 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */
5042 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */
5043 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5044 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5045 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5046 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5047 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5048 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5049 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5050 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5051 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5052 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */
5053 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5054 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5055 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5056 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5057 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */
5058 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5059 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5060 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5061 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */
5062 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */
5063 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5064 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5065 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5066 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5067 114, 114, 114, 114, 82, 82, 82, 82, /* 80 */
5068 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */
5069 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */
5070 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5071 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5072 115, 115, 115, 115, 15, 15, 15, 15, /* 81 */
5073 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5074 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5075 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */
5076 116, 116, 116, 116, 116, 116, 116, 116, /* 81 */
5077 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5078 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5079 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5080 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5081 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5082 117, 117, 118, 46, 46, 46, 46, 46, /* 82 */
5083 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5084 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5085 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5086 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5087 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5088 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5089 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5090 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5091 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5092 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5093 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5094 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5095 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */
5096 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */
5097 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5098 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5099 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5100 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5101 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5102 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5103 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5104 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5105 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5106 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5107 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5108 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5109 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5110 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5111 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */
5112 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */
5113 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5114 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5115 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5116 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5117 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */
5118 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */
5119 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5120 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5121 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5122 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5123 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5124 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5125 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5126 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */
5127 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */
5128 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */
5129 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5130 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */
5131 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */
5132 119, 119, 119, 119, 119, 119, 119, 119, /* 88 */
5133 114, 114, 114, 114, 114, 114, 114, 114, /* 89 */
5134 114, 114, 83, 83, 83, 83, 83, 83, /* 89 */
5135 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */
5136 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5137 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5138 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5139 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5140 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */
5141 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */
5142 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */
5143 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */
5144 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */
5145 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */
5146 121, 121, 60, 60, 60, 60, 60, 60, /* 90 */
5147 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */
5148 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */
5149 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5150 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5151 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5152 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5153 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5154 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5155 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5156 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5157 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5158 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5159 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */
5160 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */
5161 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5162 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5163 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5164 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5165 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5166 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5167 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5168 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5169 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5170 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5171 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5172 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */
5173 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */
5174 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5175 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5176 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5177 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5178 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */
5179 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5180 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5181 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */
5182 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */
5183 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */
5184 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */
5185 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5186 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5187 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5188 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5189 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5190 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5191 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5192 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */
5193 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */
5194 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */
5195 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5196 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5197 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */
5198 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5199 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5200 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5201 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5202 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5203 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5204 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */
5205 114, 114, 114, 114, 114, 114, 114, 114, /* 98 */
5206 114, 114, 15, 15, 15, 15, 15, 15, /* 98 */
5207 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5208 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5209 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5210 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5211 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5212 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5213 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5214 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */
5215 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5216 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5217 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5218 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5219 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5220 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */
5221 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5222 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5223 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5224 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5225 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5226 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5227 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */
5228 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */
5229 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5230 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5231 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5232 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */
5233 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5234 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5235 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5236 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */
5237 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5238 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5239 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5240 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5241 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */
5242 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5243 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5244 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5245 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5246 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5247 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5248 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5249 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */
5250 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5251 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5252 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5253 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5254 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5255 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5256 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5257 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5258 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5259 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5260 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5261 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5262 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5263 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5264 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5265 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5266 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5267 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5268 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5269 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5270 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5271 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5272 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5273 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5274 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */
5275 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5276 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5277 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */
5278 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */
5279 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */
5280 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */
5281 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */
5282 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */
5283 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */
5284 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */
5285 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */
5286 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5287 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5288 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5289 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5290 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5291 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5292 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5293 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5294 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5295 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5296 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5297 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5298 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5299 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */
5300 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */
5301 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5302 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5303 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */
5304 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5305 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5306 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5307 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5308 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5309 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5310 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5311 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5312 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5313 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5314 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5315 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5316 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */
5317 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5318 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5319 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5320 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5321 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5322 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5323 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5324 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5325 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5326 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5327 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */
5328 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5329 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5330 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5331 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5332 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5333 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5334 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5335 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5336 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5337 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5338 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5339 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5340 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */
5341 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5342 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5343 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5344 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5345 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */
5346 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5347 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */
5348 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */
5349 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */
5350 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */
5351 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */
5352 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */
5353 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */
5354 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */
5355 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */
5356 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */
5357 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5358 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5359 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5360 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5361 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5362 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5363 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5364 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */
5365 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */
5366 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */
5367 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */
5368 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */
5369 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5370 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5371 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5372 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */
5373 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5374 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5375 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5376 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */
5377 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */
5378 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5379 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5380 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5381 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5382 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5383 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5384 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */
5385 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5386 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5387 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5388 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */
5389 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5390 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5391 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5392 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */
5393 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */
5394 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */
5395 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */
5396 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */
5399 /* The A table has 124 entries for a total of 496 bytes. */
5401 const uint32 js_A[] = {
5402 0x0001000F, /* 0 Cc, ignorable */
5403 0x0004000F, /* 1 Cc, whitespace */
5404 0x0004000C, /* 2 Zs, whitespace */
5405 0x00000018, /* 3 Po */
5406 0x0006001A, /* 4 Sc, currency */
5407 0x00000015, /* 5 Ps */
5408 0x00000016, /* 6 Pe */
5409 0x00000019, /* 7 Sm */
5410 0x00000014, /* 8 Pd */
5411 0x00036089, /* 9 Nd, identifier part, decimal 16 */
5412 0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */
5413 0x0000001B, /* 11 Sk */
5414 0x00050017, /* 12 Pc, underscore */
5415 0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */
5416 0x0000000C, /* 14 Zs */
5417 0x0000001C, /* 15 So */
5418 0x00070182, /* 16 Ll, identifier start */
5419 0x0000600B, /* 17 No, decimal 16 */
5420 0x0000500B, /* 18 No, decimal 8 */
5421 0x0000800B, /* 19 No, strange */
5422 0x08270181, /* 20 Lu, hasLower (add 32), identifier start */
5423 0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */
5424 0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */
5425 0x00670181, /* 23 Lu, hasLower (add 1), identifier start */
5426 0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */
5427 0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */
5428 0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */
5429 0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */
5430 0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */
5431 0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */
5432 0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */
5433 0x33670181, /* 31 Lu, hasLower (add 205), identifier start */
5434 0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */
5435 0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */
5436 0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */
5437 0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */
5438 0x34670181, /* 36 Lu, hasLower (add 209), identifier start */
5439 0x35670181, /* 37 Lu, hasLower (add 213), identifier start */
5440 0x00070181, /* 38 Lu, identifier start */
5441 0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */
5442 0x00070185, /* 40 Lo, identifier start */
5443 0x36670181, /* 41 Lu, hasLower (add 217), identifier start */
5444 0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */
5445 0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */
5446 0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */
5447 0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */
5448 0x00000000, /* 46 unassigned */
5449 0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */
5450 0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */
5451 0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */
5452 0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */
5453 0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */
5454 0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */
5455 0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */
5456 0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */
5457 0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */
5458 0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */
5459 0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */
5460 0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */
5461 0x00070084, /* 59 Lm, identifier start */
5462 0x00030086, /* 60 Mn, identifier part */
5463 0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */
5464 0x09670181, /* 62 Lu, hasLower (add 37), identifier start */
5465 0x10270181, /* 63 Lu, hasLower (add 64), identifier start */
5466 0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */
5467 0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */
5468 0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */
5469 0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */
5470 0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */
5471 0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */
5472 0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */
5473 0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */
5474 0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */
5475 0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */
5476 0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */
5477 0x14270181, /* 75 Lu, hasLower (add 80), identifier start */
5478 0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */
5479 0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */
5480 0x00034089, /* 78 Nd, identifier part, decimal 0 */
5481 0x00000087, /* 79 Me */
5482 0x00030088, /* 80 Mc, identifier part */
5483 0x00037489, /* 81 Nd, identifier part, decimal 26 */
5484 0x00005A0B, /* 82 No, decimal 13 */
5485 0x00006E0B, /* 83 No, decimal 23 */
5486 0x0000740B, /* 84 No, decimal 26 */
5487 0x0000000B, /* 85 No */
5488 0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */
5489 0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */
5490 0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */
5491 0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */
5492 0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */
5493 0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */
5494 0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */
5495 0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */
5496 0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */
5497 0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */
5498 0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */
5499 0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */
5500 0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */
5501 0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */
5502 0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */
5503 0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */
5504 0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */
5505 0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */
5506 0x00010010, /* 104 Cf, ignorable */
5507 0x0004000D, /* 105 Zl, whitespace */
5508 0x0004000E, /* 106 Zp, whitespace */
5509 0x0000400B, /* 107 No, decimal 0 */
5510 0x0000440B, /* 108 No, decimal 2 */
5511 0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */
5512 0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */
5513 0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */
5514 0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */
5515 0x0007818A, /* 113 Nl, identifier start, strange */
5516 0x0000420B, /* 114 No, decimal 1 */
5517 0x0000720B, /* 115 No, decimal 25 */
5518 0x06A0001C, /* 116 So, hasLower (add 26) */
5519 0x0690001C, /* 117 So, hasUpper (subtract 26) */
5520 0x00006C0B, /* 118 No, decimal 22 */
5521 0x0000560B, /* 119 No, decimal 11 */
5522 0x0007738A, /* 120 Nl, identifier start, decimal 25 */
5523 0x0007418A, /* 121 Nl, identifier start, decimal 0 */
5524 0x00000013, /* 122 Cs */
5525 0x00000012 /* 123 Co */
5528 const jschar js_uriReservedPlusPound_ucstr[] =
5529 {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
5530 const jschar js_uriUnescaped_ucstr[] =
5531 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
5532 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
5533 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
5534 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
5535 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
5536 '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
5539 * This table allows efficient testing for the regular expression \w which is
5540 * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z].
5542 const bool js_alnum[] = {
5543 /* 0 1 2 3 4 5 5 7 8 9 */
5544 /* 0 */ false, false, false, false, false, false, false, false, false, false,
5545 /* 1 */ false, false, false, false, false, false, false, false, false, false,
5546 /* 2 */ false, false, false, false, false, false, false, false, false, false,
5547 /* 3 */ false, false, false, false, false, false, false, false, false, false,
5548 /* 4 */ false, false, false, false, false, false, false, false, true, true,
5549 /* 5 */ true, true, true, true, true, true, true, true, false, false,
5550 /* 6 */ false, false, false, false, false, true, true, true, true, true,
5551 /* 7 */ true, true, true, true, true, true, true, true, true, true,
5552 /* 8 */ true, true, true, true, true, true, true, true, true, true,
5553 /* 9 */ true, false, false, false, false, true, false, true, true, true,
5554 /* 10 */ true, true, true, true, true, true, true, true, true, true,
5555 /* 11 */ true, true, true, true, true, true, true, true, true, true,
5556 /* 12 */ true, true, true, false, false, false, false, false
5559 #define URI_CHUNK 64U
5561 static inline bool
5562 TransferBufferToString(JSContext *cx, JSCharBuffer &cb, Value *rval)
5564 JSString *str = js_NewStringFromCharBuffer(cx, cb);
5565 if (!str)
5566 return false;
5567 rval->setString(str);
5568 return true;;
5572 * ECMA 3, 15.1.3 URI Handling Function Properties
5574 * The following are implementations of the algorithms
5575 * given in the ECMA specification for the hidden functions
5576 * 'Encode' and 'Decode'.
5578 static JSBool
5579 Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
5580 const jschar *unescapedSet2, Value *rval)
5582 static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
5584 size_t length = str->length();
5585 const jschar *chars = str->getChars(cx);
5586 if (!chars)
5587 return JS_FALSE;
5589 if (length == 0) {
5590 rval->setString(cx->runtime->emptyString);
5591 return JS_TRUE;
5594 JSCharBuffer cb(cx);
5595 jschar hexBuf[4];
5596 hexBuf[0] = '%';
5597 hexBuf[3] = 0;
5598 for (size_t k = 0; k < length; k++) {
5599 jschar c = chars[k];
5600 if (js_strchr(unescapedSet, c) ||
5601 (unescapedSet2 && js_strchr(unescapedSet2, c))) {
5602 if (!cb.append(c))
5603 return JS_FALSE;
5604 } else {
5605 if ((c >= 0xDC00) && (c <= 0xDFFF)) {
5606 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5607 JSMSG_BAD_URI, NULL);
5608 return JS_FALSE;
5610 uint32 v;
5611 if (c < 0xD800 || c > 0xDBFF) {
5612 v = c;
5613 } else {
5614 k++;
5615 if (k == length) {
5616 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5617 JSMSG_BAD_URI, NULL);
5618 return JS_FALSE;
5620 jschar c2 = chars[k];
5621 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
5622 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5623 JSMSG_BAD_URI, NULL);
5624 return JS_FALSE;
5626 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
5628 uint8 utf8buf[4];
5629 size_t L = js_OneUcs4ToUtf8Char(utf8buf, v);
5630 for (size_t j = 0; j < L; j++) {
5631 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
5632 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
5633 if (!cb.append(hexBuf, 3))
5634 return JS_FALSE;
5639 return TransferBufferToString(cx, cb, rval);
5642 static JSBool
5643 Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
5645 size_t length = str->length();
5646 const jschar *chars = str->getChars(cx);
5647 if (!chars)
5648 return JS_FALSE;
5650 if (length == 0) {
5651 rval->setString(cx->runtime->emptyString);
5652 return JS_TRUE;
5655 JSCharBuffer cb(cx);
5656 for (size_t k = 0; k < length; k++) {
5657 jschar c = chars[k];
5658 if (c == '%') {
5659 size_t start = k;
5660 if ((k + 2) >= length)
5661 goto report_bad_uri;
5662 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5663 goto report_bad_uri;
5664 jsuint B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5665 k += 2;
5666 if (!(B & 0x80)) {
5667 c = (jschar)B;
5668 } else {
5669 intN n = 1;
5670 while (B & (0x80 >> n))
5671 n++;
5672 if (n == 1 || n > 4)
5673 goto report_bad_uri;
5674 uint8 octets[4];
5675 octets[0] = (uint8)B;
5676 if (k + 3 * (n - 1) >= length)
5677 goto report_bad_uri;
5678 for (intN j = 1; j < n; j++) {
5679 k++;
5680 if (chars[k] != '%')
5681 goto report_bad_uri;
5682 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5683 goto report_bad_uri;
5684 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5685 if ((B & 0xC0) != 0x80)
5686 goto report_bad_uri;
5687 k += 2;
5688 octets[j] = (char)B;
5690 uint32 v = Utf8ToOneUcs4Char(octets, n);
5691 if (v >= 0x10000) {
5692 v -= 0x10000;
5693 if (v > 0xFFFFF)
5694 goto report_bad_uri;
5695 c = (jschar)((v & 0x3FF) + 0xDC00);
5696 jschar H = (jschar)((v >> 10) + 0xD800);
5697 if (!cb.append(H))
5698 return JS_FALSE;
5699 } else {
5700 c = (jschar)v;
5703 if (js_strchr(reservedSet, c)) {
5704 if (!cb.append(chars + start, k - start + 1))
5705 return JS_FALSE;
5706 } else {
5707 if (!cb.append(c))
5708 return JS_FALSE;
5710 } else {
5711 if (!cb.append(c))
5712 return JS_FALSE;
5716 return TransferBufferToString(cx, cb, rval);
5718 report_bad_uri:
5719 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
5720 /* FALL THROUGH */
5722 return JS_FALSE;
5725 static JSBool
5726 str_decodeURI(JSContext *cx, uintN argc, Value *vp)
5728 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5729 if (!str)
5730 return JS_FALSE;
5731 return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp);
5734 static JSBool
5735 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5737 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5738 if (!str)
5739 return JS_FALSE;
5740 return Decode(cx, str, js_empty_ucstr, vp);
5743 static JSBool
5744 str_encodeURI(JSContext *cx, uintN argc, Value *vp)
5746 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5747 if (!str)
5748 return JS_FALSE;
5749 return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr,
5750 vp);
5753 static JSBool
5754 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5756 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5757 if (!str)
5758 return JS_FALSE;
5759 return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp);
5763 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
5764 * least 4 bytes long. Return the number of UTF-8 bytes of data written.
5767 js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)
5769 int utf8Length = 1;
5771 JS_ASSERT(ucs4Char <= 0x10FFFF);
5772 if (ucs4Char < 0x80) {
5773 *utf8Buffer = (uint8)ucs4Char;
5774 } else {
5775 int i;
5776 uint32 a = ucs4Char >> 11;
5777 utf8Length = 2;
5778 while (a) {
5779 a >>= 5;
5780 utf8Length++;
5782 i = utf8Length;
5783 while (--i) {
5784 utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
5785 ucs4Char >>= 6;
5787 *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
5789 return utf8Length;
5793 * Convert a utf8 character sequence into a UCS-4 character and return that
5794 * character. It is assumed that the caller already checked that the sequence
5795 * is valid.
5797 static uint32
5798 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
5800 uint32 ucs4Char;
5801 uint32 minucs4Char;
5802 /* from Unicode 3.1, non-shortest form is illegal */
5803 static const uint32 minucs4Table[] = {
5804 0x00000080, 0x00000800, 0x00010000
5807 JS_ASSERT(utf8Length >= 1 && utf8Length <= 4);
5808 if (utf8Length == 1) {
5809 ucs4Char = *utf8Buffer;
5810 JS_ASSERT(!(ucs4Char & 0x80));
5811 } else {
5812 JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) ==
5813 (0x100 - (1 << (8-utf8Length))));
5814 ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
5815 minucs4Char = minucs4Table[utf8Length-2];
5816 while (--utf8Length) {
5817 JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
5818 ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
5820 if (JS_UNLIKELY(ucs4Char < minucs4Char)) {
5821 ucs4Char = OVERLONG_UTF8;
5822 } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
5823 ucs4Char = 0xFFFD;
5826 return ucs4Char;
5829 namespace js {
5831 size_t
5832 PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32 quote)
5834 enum {
5835 STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
5836 } state;
5838 JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
5839 JS_ASSERT_IF(!buffer, bufferSize == 0);
5840 JS_ASSERT_IF(fp, !buffer);
5842 if (bufferSize == 0)
5843 buffer = NULL;
5844 else
5845 bufferSize--;
5847 const jschar *chars = str->chars();
5848 const jschar *charsEnd = chars + str->length();
5849 size_t n = 0;
5850 state = FIRST_QUOTE;
5851 uintN shift = 0;
5852 uintN hex = 0;
5853 uintN u = 0;
5854 char c = 0; /* to quell GCC warnings */
5856 for (;;) {
5857 switch (state) {
5858 case STOP:
5859 goto stop;
5860 case FIRST_QUOTE:
5861 state = CHARS;
5862 goto do_quote;
5863 case LAST_QUOTE:
5864 state = STOP;
5865 do_quote:
5866 if (quote == 0)
5867 continue;
5868 c = (char)quote;
5869 break;
5870 case CHARS:
5871 if (chars == charsEnd) {
5872 state = LAST_QUOTE;
5873 continue;
5875 u = *chars++;
5876 if (u < ' ') {
5877 if (u != 0) {
5878 const char *escape = strchr(js_EscapeMap, (int)u);
5879 if (escape) {
5880 u = escape[1];
5881 goto do_escape;
5884 goto do_hex_escape;
5886 if (u < 127) {
5887 if (u == quote || u == '\\')
5888 goto do_escape;
5889 c = (char)u;
5890 } else if (u < 0x100) {
5891 goto do_hex_escape;
5892 } else {
5893 shift = 16;
5894 hex = u;
5895 u = 'u';
5896 goto do_escape;
5898 break;
5899 do_hex_escape:
5900 shift = 8;
5901 hex = u;
5902 u = 'x';
5903 do_escape:
5904 c = '\\';
5905 state = ESCAPE_START;
5906 break;
5907 case ESCAPE_START:
5908 JS_ASSERT(' ' <= u && u < 127);
5909 c = (char)u;
5910 state = ESCAPE_MORE;
5911 break;
5912 case ESCAPE_MORE:
5913 if (shift == 0) {
5914 state = CHARS;
5915 continue;
5917 shift -= 4;
5918 u = 0xF & (hex >> shift);
5919 c = (char)(u + (u < 10 ? '0' : 'A' - 10));
5920 break;
5922 if (buffer) {
5923 JS_ASSERT(n <= bufferSize);
5924 if (n != bufferSize) {
5925 buffer[n] = c;
5926 } else {
5927 buffer[n] = '\0';
5928 buffer = NULL;
5930 } else if (fp) {
5931 if (fputc(c, fp) < 0)
5932 return size_t(-1);
5934 n++;
5936 stop:
5937 if (buffer)
5938 buffer[n] = '\0';
5939 return n;
5942 } /* namespace js */