Bug 617935: Check string lengths using StringBuffer. (r=lw)
[mozilla-central.git] / js / src / jsstr.cpp
blobb7ed46fa0b3ae340225c18685ea56cdd7c1d6316
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 * N.B. This optimization can create chains of dependent strings.
163 const size_t wholeLength = length();
164 size_t wholeCapacity;
165 jschar *wholeChars;
166 JSString *str = this;
167 jschar *pos;
169 if (u.left->isExtensible() && u.left->s.capacity >= wholeLength) {
170 wholeCapacity = u.left->s.capacity;
171 wholeChars = const_cast<jschar *>(u.left->u.chars);
172 pos = wholeChars + u.left->length();
173 u.left->finishTraversalConversion(this, wholeChars, pos);
174 goto visit_right_child;
177 wholeCapacity = RopeCapacityFor(wholeLength);
178 wholeChars = AllocChars(maybecx, wholeCapacity);
179 if (!wholeChars)
180 return NULL;
182 if (maybecx)
183 maybecx->runtime->stringMemoryUsed += wholeLength * 2;
184 pos = wholeChars;
185 first_visit_node: {
186 JSString *left = str->u.left; /* Read before clobbered. */
187 str->u.chars = pos;
188 if (left->isRope()) {
189 left->s.parent = str; /* Return to this when 'left' done, */
190 left->lengthAndFlags = 0x200; /* but goto visit_right_child. */
191 str = left;
192 goto first_visit_node;
194 size_t len = left->length();
195 PodCopy(pos, left->u.chars, len);
196 pos += len;
198 visit_right_child: {
199 JSString *right = str->s.right;
200 if (right->isRope()) {
201 right->s.parent = str; /* Return to this node when 'right' done, */
202 right->lengthAndFlags = 0x300; /* but goto finish_node. */
203 str = right;
204 goto first_visit_node;
206 size_t len = right->length();
207 PodCopy(pos, right->u.chars, len);
208 pos += len;
210 finish_node: {
211 if (str == this) {
212 JS_ASSERT(pos == wholeChars + wholeLength);
213 *pos = '\0';
214 initFlatExtensible(wholeChars, wholeLength, wholeCapacity);
215 return wholeChars;
217 size_t progress = str->lengthAndFlags; /* Read before clobbered. */
218 JSString *parent = str->s.parent;
219 str->finishTraversalConversion(this, wholeChars, pos);
220 str = parent;
221 if (progress == 0x200)
222 goto visit_right_child;
223 goto finish_node;
227 JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8);
228 JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
229 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
232 #ifdef JS_TRACER
234 JSBool JS_FASTCALL
235 js_Flatten(JSContext *cx, JSString* str)
237 return !!str->flatten(cx);
239 JS_DEFINE_CALLINFO_2(extern, BOOL, js_Flatten, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY)
241 #endif /* !JS_TRACER */
243 JSString * JS_FASTCALL
244 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
246 size_t leftLen = left->length();
247 if (leftLen == 0)
248 return right;
250 size_t rightLen = right->length();
251 if (rightLen == 0)
252 return left;
254 size_t wholeLength = leftLen + rightLen;
256 if (JSShortString::fitsIntoShortString(wholeLength)) {
257 JSShortString *shortStr = js_NewGCShortString(cx);
258 if (!shortStr)
259 return NULL;
260 const jschar *leftChars = left->getChars(cx);
261 if (!leftChars)
262 return NULL;
263 const jschar *rightChars = right->getChars(cx);
264 if (!rightChars)
265 return NULL;
267 jschar *buf = shortStr->init(wholeLength);
268 js_short_strncpy(buf, leftChars, leftLen);
269 js_short_strncpy(buf + leftLen, rightChars, rightLen);
270 buf[wholeLength] = 0;
271 return shortStr->header();
274 if (wholeLength > JSString::MAX_LENGTH) {
275 if (JS_ON_TRACE(cx)) {
276 if (!CanLeaveTrace(cx))
277 return NULL;
278 LeaveTrace(cx);
280 js_ReportAllocationOverflow(cx);
281 return NULL;
284 JSString *newRoot = js_NewGCString(cx);
285 if (!newRoot)
286 return NULL;
288 newRoot->initRopeNode(left, right, wholeLength);
289 return newRoot;
292 const jschar *
293 JSString::undepend(JSContext *cx)
295 size_t n, size;
296 jschar *s;
298 if (!ensureLinear(cx))
299 return NULL;
301 if (isDependent()) {
302 n = dependentLength();
303 size = (n + 1) * sizeof(jschar);
304 s = (jschar *) cx->malloc(size);
305 if (!s)
306 return NULL;
308 cx->runtime->stringMemoryUsed += size;
309 js_strncpy(s, dependentChars(), n);
310 s[n] = 0;
311 initFlat(s, n);
313 #ifdef DEBUG
315 JSRuntime *rt = cx->runtime;
316 JS_RUNTIME_UNMETER(rt, liveDependentStrings);
317 JS_RUNTIME_UNMETER(rt, totalDependentStrings);
318 JS_LOCK_RUNTIME_VOID(rt,
319 (rt->strdepLengthSum -= (double)n,
320 rt->strdepLengthSquaredSum -= (double)n * (double)n));
322 #endif
325 return flatChars();
328 JSBool
329 js_MakeStringImmutable(JSContext *cx, JSString *str)
332 * Flattening a rope may result in a dependent string, so we need to flatten
333 * before undepending the string.
335 if (!str->isFlat() && !str->undepend(cx)) {
336 JS_RUNTIME_METER(cx->runtime, badUndependStrings);
337 return JS_FALSE;
339 str->flatClearExtensible();
340 return JS_TRUE;
343 static JSLinearString *
344 ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg)
346 if (arg >= argc)
347 return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
348 vp += 2 + arg;
350 if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp))
351 return NULL;
353 JSLinearString *str;
354 if (vp->isString()) {
355 str = vp->toString()->ensureLinear(cx);
356 } else if (vp->isBoolean()) {
357 str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
358 (int)vp->toBoolean()]);
359 } else if (vp->isNull()) {
360 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
361 } else if (vp->isUndefined()) {
362 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
364 else {
365 str = NumberToString(cx, vp->toNumber());
366 if (!str)
367 return NULL;
368 vp->setString(str);
370 return str;
374 * Forward declarations for URI encode/decode and helper routines
376 static JSBool
377 str_decodeURI(JSContext *cx, uintN argc, Value *vp);
379 static JSBool
380 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp);
382 static JSBool
383 str_encodeURI(JSContext *cx, uintN argc, Value *vp);
385 static JSBool
386 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp);
388 static const uint32 OVERLONG_UTF8 = UINT32_MAX;
390 static uint32
391 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
394 * Contributions from the String class to the set of methods defined for the
395 * global object. escape and unescape used to be defined in the Mocha library,
396 * but as ECMA decided to spec them, they've been moved to the core engine
397 * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
398 * characters by unescape.)
402 * Stuff to emulate the old libmocha escape, which took a second argument
403 * giving the type of escape to perform. Retained for compatibility, and
404 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
407 #define URL_XALPHAS ((uint8) 1)
408 #define URL_XPALPHAS ((uint8) 2)
409 #define URL_PATH ((uint8) 4)
411 static const uint8 urlCharType[256] =
412 /* Bit 0 xalpha -- the alphas
413 * Bit 1 xpalpha -- as xalpha but
414 * converts spaces to plus and plus to %20
415 * Bit 2 ... path -- as xalphas but doesn't escape '/'
417 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
418 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
419 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
420 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
421 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
422 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
423 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
424 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
425 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
426 0, };
428 /* This matches the ECMA escape set when mask is 7 (default.) */
430 #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
432 /* See ECMA-262 Edition 3 B.2.1 */
433 JSBool
434 js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
436 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
437 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
439 jsint mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
440 if (argc > 1) {
441 double d;
442 if (!ValueToNumber(cx, argv[1], &d))
443 return JS_FALSE;
444 if (!JSDOUBLE_IS_FINITE(d) ||
445 (mask = (jsint)d) != d ||
446 mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
448 char numBuf[12];
449 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
450 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
451 JSMSG_BAD_STRING_MASK, numBuf);
452 return JS_FALSE;
456 JSLinearString *str = ArgToRootedString(cx, argc, argv - 2, 0);
457 if (!str)
458 return JS_FALSE;
460 size_t length = str->length();
461 const jschar *chars = str->chars();
463 /* Take a first pass and see how big the result string will need to be. */
464 size_t newlength = length;
465 for (size_t i = 0; i < length; i++) {
466 jschar ch;
467 if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
468 continue;
469 if (ch < 256) {
470 if (mask == URL_XPALPHAS && ch == ' ')
471 continue; /* The character will be encoded as '+' */
472 newlength += 2; /* The character will be encoded as %XX */
473 } else {
474 newlength += 5; /* The character will be encoded as %uXXXX */
478 * This overflow test works because newlength is incremented by at
479 * most 5 on each iteration.
481 if (newlength < length) {
482 js_ReportAllocationOverflow(cx);
483 return JS_FALSE;
487 if (newlength >= ~(size_t)0 / sizeof(jschar)) {
488 js_ReportAllocationOverflow(cx);
489 return JS_FALSE;
492 jschar *newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar));
493 if (!newchars)
494 return JS_FALSE;
495 size_t i, ni;
496 for (i = 0, ni = 0; i < length; i++) {
497 jschar ch;
498 if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
499 newchars[ni++] = ch;
500 } else if (ch < 256) {
501 if (mask == URL_XPALPHAS && ch == ' ') {
502 newchars[ni++] = '+'; /* convert spaces to pluses */
503 } else {
504 newchars[ni++] = '%';
505 newchars[ni++] = digits[ch >> 4];
506 newchars[ni++] = digits[ch & 0xF];
508 } else {
509 newchars[ni++] = '%';
510 newchars[ni++] = 'u';
511 newchars[ni++] = digits[ch >> 12];
512 newchars[ni++] = digits[(ch & 0xF00) >> 8];
513 newchars[ni++] = digits[(ch & 0xF0) >> 4];
514 newchars[ni++] = digits[ch & 0xF];
517 JS_ASSERT(ni == newlength);
518 newchars[newlength] = 0;
520 JSString *retstr = js_NewString(cx, newchars, newlength);
521 if (!retstr) {
522 cx->free(newchars);
523 return JS_FALSE;
525 rval->setString(retstr);
526 return JS_TRUE;
528 #undef IS_OK
530 static JSBool
531 str_escape(JSContext *cx, uintN argc, Value *vp)
533 JSObject *obj = ComputeThisFromVp(cx, vp);
534 return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
537 /* See ECMA-262 Edition 3 B.2.2 */
538 static JSBool
539 str_unescape(JSContext *cx, uintN argc, Value *vp)
541 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
542 if (!str)
543 return false;
545 size_t length = str->length();
546 const jschar *chars = str->chars();
548 /* Don't bother allocating less space for the new string. */
549 jschar *newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
550 if (!newchars)
551 return false;
552 size_t ni = 0, i = 0;
553 while (i < length) {
554 jschar ch = chars[i++];
555 if (ch == '%') {
556 if (i + 1 < length &&
557 JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
559 ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
560 i += 2;
561 } else if (i + 4 < length && chars[i] == 'u' &&
562 JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
563 JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
565 ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
566 + JS7_UNHEX(chars[i + 2])) << 4)
567 + JS7_UNHEX(chars[i + 3])) << 4)
568 + JS7_UNHEX(chars[i + 4]);
569 i += 5;
572 newchars[ni++] = ch;
574 newchars[ni] = 0;
576 JSString *retstr = js_NewString(cx, newchars, ni);
577 if (!retstr) {
578 cx->free(newchars);
579 return JS_FALSE;
581 vp->setString(retstr);
582 return JS_TRUE;
585 #if JS_HAS_UNEVAL
586 static JSBool
587 str_uneval(JSContext *cx, uintN argc, Value *vp)
589 JSString *str;
591 str = js_ValueToSource(cx, argc != 0 ? vp[2] : UndefinedValue());
592 if (!str)
593 return JS_FALSE;
594 vp->setString(str);
595 return JS_TRUE;
597 #endif
599 const char js_escape_str[] = "escape";
600 const char js_unescape_str[] = "unescape";
601 #if JS_HAS_UNEVAL
602 const char js_uneval_str[] = "uneval";
603 #endif
604 const char js_decodeURI_str[] = "decodeURI";
605 const char js_encodeURI_str[] = "encodeURI";
606 const char js_decodeURIComponent_str[] = "decodeURIComponent";
607 const char js_encodeURIComponent_str[] = "encodeURIComponent";
609 static JSFunctionSpec string_functions[] = {
610 JS_FN(js_escape_str, str_escape, 1,0),
611 JS_FN(js_unescape_str, str_unescape, 1,0),
612 #if JS_HAS_UNEVAL
613 JS_FN(js_uneval_str, str_uneval, 1,0),
614 #endif
615 JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
616 JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
617 JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
618 JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
620 JS_FS_END
623 jschar js_empty_ucstr[] = {0};
624 JSSubString js_EmptySubString = {0, js_empty_ucstr};
626 static JSBool
627 str_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
629 JSString *str;
631 if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
632 if (obj->getClass() == &js_StringClass) {
633 /* Follow ECMA-262 by fetching intrinsic length of our string. */
634 str = obj->getPrimitiveThis().toString();
635 } else {
636 /* Preserve compatibility: convert obj to a string primitive. */
637 str = js_ValueToString(cx, ObjectValue(*obj));
638 if (!str)
639 return JS_FALSE;
642 vp->setInt32(str->length());
645 return JS_TRUE;
648 #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
650 static JSBool
651 str_enumerate(JSContext *cx, JSObject *obj)
653 JSString *str, *str1;
654 size_t i, length;
656 str = obj->getPrimitiveThis().toString();
658 length = str->length();
659 for (i = 0; i < length; i++) {
660 str1 = js_NewDependentString(cx, str, i, 1);
661 if (!str1)
662 return JS_FALSE;
663 if (!obj->defineProperty(cx, INT_TO_JSID(i), StringValue(str1),
664 PropertyStub, PropertyStub,
665 STRING_ELEMENT_ATTRS)) {
666 return JS_FALSE;
670 return obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
671 UndefinedValue(), NULL, NULL,
672 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED);
675 static JSBool
676 str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
677 JSObject **objp)
679 if (!JSID_IS_INT(id))
680 return JS_TRUE;
682 JSString *str = obj->getPrimitiveThis().toString();
684 jsint slot = JSID_TO_INT(id);
685 if ((size_t)slot < str->length()) {
686 JSString *str1 = JSString::getUnitString(cx, str, size_t(slot));
687 if (!str1)
688 return JS_FALSE;
689 if (!obj->defineProperty(cx, id, StringValue(str1), NULL, NULL,
690 STRING_ELEMENT_ATTRS)) {
691 return JS_FALSE;
693 *objp = obj;
695 return JS_TRUE;
698 Class js_StringClass = {
699 js_String_str,
700 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
701 JSCLASS_HAS_CACHED_PROTO(JSProto_String),
702 PropertyStub, /* addProperty */
703 PropertyStub, /* delProperty */
704 str_getProperty,
705 PropertyStub, /* setProperty */
706 str_enumerate,
707 (JSResolveOp)str_resolve,
708 ConvertStub
711 #define NORMALIZE_THIS(cx,vp,str) \
712 JS_BEGIN_MACRO \
713 if (vp[1].isString()) { \
714 str = vp[1].toString(); \
715 } else { \
716 str = NormalizeThis(cx, vp); \
717 if (!str) \
718 return JS_FALSE; \
720 JS_END_MACRO
722 static JSString *
723 NormalizeThis(JSContext *cx, Value *vp)
725 if (vp[1].isNullOrUndefined() && !ComputeThisFromVp(cx, vp))
726 return NULL;
729 * String.prototype.{toString,toSource,valueOf} throw a TypeError if the
730 * this-argument is not a string or a String object. So those methods use
731 * js::GetPrimitiveThis which provides that behavior.
733 * By standard, the rest of the String methods must ToString the
734 * this-argument rather than throw a TypeError. So those methods use
735 * NORMALIZE_THIS (and thus NormalizeThis) instead.
737 if (vp[1].isObject()) {
738 JSObject *obj = &vp[1].toObject();
739 if (obj->getClass() == &js_StringClass) {
740 vp[1] = obj->getPrimitiveThis();
741 return vp[1].toString();
745 JSString *str = js_ValueToString(cx, vp[1]);
746 if (!str)
747 return NULL;
748 vp[1].setString(str);
749 return str;
752 #if JS_HAS_TOSOURCE
755 * String.prototype.quote is generic (as are most string methods), unlike
756 * toSource, toString, and valueOf.
758 static JSBool
759 str_quote(JSContext *cx, uintN argc, Value *vp)
761 JSString *str;
763 NORMALIZE_THIS(cx, vp, str);
764 str = js_QuoteString(cx, str, '"');
765 if (!str)
766 return JS_FALSE;
767 vp->setString(str);
768 return JS_TRUE;
771 static JSBool
772 str_toSource(JSContext *cx, uintN argc, Value *vp)
774 JSString *str;
775 if (!GetPrimitiveThis(cx, vp, &str))
776 return false;
778 str = js_QuoteString(cx, str, '"');
779 if (!str)
780 return false;
782 char buf[16];
783 size_t j = JS_snprintf(buf, sizeof buf, "(new String(");
785 size_t k = str->length();
786 const jschar *s = str->getChars(cx);
787 if (!s)
788 return false;
790 size_t n = j + k + 2;
791 jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
792 if (!t)
793 return false;
795 size_t i;
796 for (i = 0; i < j; i++)
797 t[i] = buf[i];
798 for (j = 0; j < k; i++, j++)
799 t[i] = s[j];
800 t[i++] = ')';
801 t[i++] = ')';
802 t[i] = 0;
804 str = js_NewString(cx, t, n);
805 if (!str) {
806 cx->free(t);
807 return false;
809 vp->setString(str);
810 return true;
813 #endif /* JS_HAS_TOSOURCE */
815 JSBool
816 js_str_toString(JSContext *cx, uintN argc, Value *vp)
818 JSString *str;
819 if (!GetPrimitiveThis(cx, vp, &str))
820 return false;
821 vp->setString(str);
822 return true;
826 * Java-like string native methods.
829 JS_ALWAYS_INLINE bool
830 ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
832 if (v.isInt32()) {
833 *out = v.toInt32();
834 } else {
835 double d;
837 if (!ValueToNumber(cx, v, &d))
838 return false;
840 d = js_DoubleToInteger(d);
841 if (d > INT32_MAX)
842 *out = INT32_MAX;
843 else if (d < INT32_MIN)
844 *out = INT32_MIN;
845 else
846 *out = int32(d);
849 return true;
852 static JSBool
853 str_substring(JSContext *cx, uintN argc, Value *vp)
855 JSString *str;
856 int32 length, begin, end;
858 NORMALIZE_THIS(cx, vp, str);
860 if (argc > 0) {
861 end = length = int32(str->length());
863 if (!ValueToIntegerRange(cx, vp[2], &begin))
864 return false;
866 if (begin < 0)
867 begin = 0;
868 else if (begin > length)
869 begin = length;
871 if (argc > 1 && !vp[3].isUndefined()) {
872 if (!ValueToIntegerRange(cx, vp[3], &end))
873 return false;
875 if (end > length) {
876 end = length;
877 } else {
878 if (end < 0)
879 end = 0;
880 if (end < begin) {
881 int32_t tmp = begin;
882 begin = end;
883 end = tmp;
888 str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
889 if (!str)
890 return false;
893 vp->setString(str);
894 return true;
897 JSString* JS_FASTCALL
898 js_toLowerCase(JSContext *cx, JSString *str)
900 size_t n = str->length();
901 const jschar *s = str->getChars(cx);
902 if (!s)
903 return NULL;
905 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
906 if (!news)
907 return NULL;
908 for (size_t i = 0; i < n; i++)
909 news[i] = JS_TOLOWER(s[i]);
910 news[n] = 0;
911 str = js_NewString(cx, news, n);
912 if (!str) {
913 cx->free(news);
914 return NULL;
916 return str;
919 static JSBool
920 str_toLowerCase(JSContext *cx, uintN argc, Value *vp)
922 JSString *str;
924 NORMALIZE_THIS(cx, vp, str);
925 str = js_toLowerCase(cx, str);
926 if (!str)
927 return JS_FALSE;
928 vp->setString(str);
929 return JS_TRUE;
932 static JSBool
933 str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp)
935 JSString *str;
938 * Forcefully ignore the first (or any) argument and return toLowerCase(),
939 * ECMA has reserved that argument, presumably for defining the locale.
941 if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
942 NORMALIZE_THIS(cx, vp, str);
943 return cx->localeCallbacks->localeToLowerCase(cx, str, Jsvalify(vp));
945 return str_toLowerCase(cx, 0, vp);
948 JSString* JS_FASTCALL
949 js_toUpperCase(JSContext *cx, JSString *str)
951 size_t n = str->length();
952 const jschar *s = str->getChars(cx);
953 if (!s)
954 return NULL;
955 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
956 if (!news)
957 return NULL;
958 for (size_t i = 0; i < n; i++)
959 news[i] = JS_TOUPPER(s[i]);
960 news[n] = 0;
961 str = js_NewString(cx, news, n);
962 if (!str) {
963 cx->free(news);
964 return NULL;
966 return str;
969 static JSBool
970 str_toUpperCase(JSContext *cx, uintN argc, Value *vp)
972 JSString *str;
974 NORMALIZE_THIS(cx, vp, str);
975 str = js_toUpperCase(cx, str);
976 if (!str)
977 return JS_FALSE;
978 vp->setString(str);
979 return JS_TRUE;
982 static JSBool
983 str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp)
985 JSString *str;
988 * Forcefully ignore the first (or any) argument and return toUpperCase(),
989 * ECMA has reserved that argument, presumably for defining the locale.
991 if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
992 NORMALIZE_THIS(cx, vp, str);
993 return cx->localeCallbacks->localeToUpperCase(cx, str, Jsvalify(vp));
995 return str_toUpperCase(cx, 0, vp);
998 static JSBool
999 str_localeCompare(JSContext *cx, uintN argc, Value *vp)
1001 JSString *str, *thatStr;
1003 NORMALIZE_THIS(cx, vp, str);
1004 if (argc == 0) {
1005 vp->setInt32(0);
1006 } else {
1007 thatStr = js_ValueToString(cx, vp[2]);
1008 if (!thatStr)
1009 return JS_FALSE;
1010 if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
1011 vp[2].setString(thatStr);
1012 return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp));
1014 int32 result;
1015 if (!CompareStrings(cx, str, thatStr, &result))
1016 return JS_FALSE;
1017 vp->setInt32(result);
1019 return JS_TRUE;
1022 JSBool
1023 js_str_charAt(JSContext *cx, uintN argc, Value *vp)
1025 JSString *str;
1026 jsint i;
1027 jsdouble d;
1029 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1030 str = vp[1].toString();
1031 i = vp[2].toInt32();
1032 if ((size_t)i >= str->length())
1033 goto out_of_range;
1034 } else {
1035 NORMALIZE_THIS(cx, vp, str);
1037 if (argc == 0) {
1038 d = 0.0;
1039 } else {
1040 if (!ValueToNumber(cx, vp[2], &d))
1041 return JS_FALSE;
1042 d = js_DoubleToInteger(d);
1045 if (d < 0 || str->length() <= d)
1046 goto out_of_range;
1047 i = (jsint) d;
1050 str = JSString::getUnitString(cx, str, size_t(i));
1051 if (!str)
1052 return JS_FALSE;
1053 vp->setString(str);
1054 return JS_TRUE;
1056 out_of_range:
1057 vp->setString(cx->runtime->emptyString);
1058 return JS_TRUE;
1061 JSBool
1062 js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp)
1064 JSString *str;
1065 jsint i;
1066 if (vp[1].isString() && argc != 0 && vp[2].isInt32()) {
1067 str = vp[1].toString();
1068 i = vp[2].toInt32();
1069 if ((size_t)i >= str->length())
1070 goto out_of_range;
1071 } else {
1072 NORMALIZE_THIS(cx, vp, str);
1074 double d;
1075 if (argc == 0) {
1076 d = 0.0;
1077 } else {
1078 if (!ValueToNumber(cx, vp[2], &d))
1079 return false;
1080 d = js_DoubleToInteger(d);
1083 if (d < 0 || str->length() <= d)
1084 goto out_of_range;
1085 i = (jsint) d;
1088 const jschar *chars;
1089 chars = str->getChars(cx);
1090 if (!chars)
1091 return false;
1093 vp->setInt32(chars[i]);
1094 return true;
1096 out_of_range:
1097 vp->setDouble(js_NaN);
1098 return true;
1101 jsint
1102 js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
1103 const jschar *pat, jsuint patlen)
1105 uint8 skip[sBMHCharSetSize];
1107 JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
1108 for (jsuint i = 0; i < sBMHCharSetSize; i++)
1109 skip[i] = (uint8)patlen;
1110 jsuint m = patlen - 1;
1111 for (jsuint i = 0; i < m; i++) {
1112 jschar c = pat[i];
1113 if (c >= sBMHCharSetSize)
1114 return sBMHBadPattern;
1115 skip[c] = (uint8)(m - i);
1117 jschar c;
1118 for (jsuint k = m;
1119 k < textlen;
1120 k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
1121 for (jsuint i = k, j = m; ; i--, j--) {
1122 if (text[i] != pat[j])
1123 break;
1124 if (j == 0)
1125 return static_cast<jsint>(i); /* safe: max string size */
1128 return -1;
1131 struct MemCmp {
1132 typedef jsuint Extent;
1133 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) {
1134 return (patlen - 1) * sizeof(jschar);
1136 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1137 return memcmp(p, t, extent) == 0;
1141 struct ManualCmp {
1142 typedef const jschar *Extent;
1143 static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) {
1144 return pat + patlen;
1146 static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
1147 for (; p != extent; ++p, ++t) {
1148 if (*p != *t)
1149 return false;
1151 return true;
1155 template <class InnerMatch>
1156 static jsint
1157 UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
1159 JS_ASSERT(patlen > 0 && textlen > 0);
1160 const jschar *textend = text + textlen - (patlen - 1);
1161 const jschar p0 = *pat;
1162 const jschar *const patNext = pat + 1;
1163 const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
1164 uint8 fixup;
1166 const jschar *t = text;
1167 switch ((textend - t) & 7) {
1168 case 0: if (*t++ == p0) { fixup = 8; goto match; }
1169 case 7: if (*t++ == p0) { fixup = 7; goto match; }
1170 case 6: if (*t++ == p0) { fixup = 6; goto match; }
1171 case 5: if (*t++ == p0) { fixup = 5; goto match; }
1172 case 4: if (*t++ == p0) { fixup = 4; goto match; }
1173 case 3: if (*t++ == p0) { fixup = 3; goto match; }
1174 case 2: if (*t++ == p0) { fixup = 2; goto match; }
1175 case 1: if (*t++ == p0) { fixup = 1; goto match; }
1177 while (t != textend) {
1178 if (t[0] == p0) { t += 1; fixup = 8; goto match; }
1179 if (t[1] == p0) { t += 2; fixup = 7; goto match; }
1180 if (t[2] == p0) { t += 3; fixup = 6; goto match; }
1181 if (t[3] == p0) { t += 4; fixup = 5; goto match; }
1182 if (t[4] == p0) { t += 5; fixup = 4; goto match; }
1183 if (t[5] == p0) { t += 6; fixup = 3; goto match; }
1184 if (t[6] == p0) { t += 7; fixup = 2; goto match; }
1185 if (t[7] == p0) { t += 8; fixup = 1; goto match; }
1186 t += 8;
1187 continue;
1188 do {
1189 if (*t++ == p0) {
1190 match:
1191 if (!InnerMatch::match(patNext, t, extent))
1192 goto failed_match;
1193 return t - text - 1;
1195 failed_match:;
1196 } while (--fixup > 0);
1198 return -1;
1201 static JS_ALWAYS_INLINE jsint
1202 StringMatch(const jschar *text, jsuint textlen,
1203 const jschar *pat, jsuint patlen)
1205 if (patlen == 0)
1206 return 0;
1207 if (textlen < patlen)
1208 return -1;
1210 #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
1212 * Given enough registers, the unrolled loop below is faster than the
1213 * following loop. 32-bit x86 does not have enough registers.
1215 if (patlen == 1) {
1216 const jschar p0 = *pat;
1217 for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
1218 if (*c == p0)
1219 return c - text;
1221 return -1;
1223 #endif
1226 * If the text or pattern string is short, BMH will be more expensive than
1227 * the basic linear scan due to initialization cost and a more complex loop
1228 * body. While the correct threshold is input-dependent, we can make a few
1229 * conservative observations:
1230 * - When |textlen| is "big enough", the initialization time will be
1231 * proportionally small, so the worst-case slowdown is minimized.
1232 * - When |patlen| is "too small", even the best case for BMH will be
1233 * slower than a simple scan for large |textlen| due to the more complex
1234 * loop body of BMH.
1235 * From this, the values for "big enough" and "too small" are determined
1236 * empirically. See bug 526348.
1238 if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
1239 jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
1240 if (index != sBMHBadPattern)
1241 return index;
1245 * For big patterns with large potential overlap we want the SIMD-optimized
1246 * speed of memcmp. For small patterns, a simple loop is faster.
1248 * FIXME: Linux memcmp performance is sad and the manual loop is faster.
1250 return
1251 #if !defined(__linux__)
1252 patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
1254 #endif
1255 UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
1258 static const size_t sRopeMatchThresholdRatioLog2 = 5;
1261 * RopeMatch takes the text to search, the patern to search for in the text.
1262 * RopeMatch returns false on OOM and otherwise returns the match index through
1263 * the 'match' outparam (-1 for not found).
1265 static bool
1266 RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, jsint *match)
1268 JS_ASSERT(textstr->isRope());
1270 if (patlen == 0) {
1271 *match = 0;
1272 return true;
1274 if (textstr->length() < patlen) {
1275 *match = -1;
1276 return true;
1280 * List of leaf nodes in the rope. If we run out of memory when trying to
1281 * append to this list, we can still fall back to StringMatch, so use the
1282 * system allocator so we don't report OOM in that case.
1284 Vector<JSString *, 16, SystemAllocPolicy> strs;
1287 * We don't want to do rope matching if there is a poor node-to-char ratio,
1288 * since this means spending a lot of time in the match loop below. We also
1289 * need to build the list of leaf nodes. Do both here: iterate over the
1290 * nodes so long as there are not too many.
1293 size_t textstrlen = textstr->length();
1294 size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
1295 StringSegmentRange r(cx);
1296 if (!r.init(textstr))
1297 return false;
1298 while (!r.empty()) {
1299 if (threshold-- == 0 || !strs.append(r.front())) {
1300 const jschar *chars = textstr->getChars(cx);
1301 if (!chars)
1302 return false;
1303 *match = StringMatch(chars, textstrlen, pat, patlen);
1304 return true;
1306 if (!r.popFront())
1307 return false;
1311 /* Absolute offset from the beginning of the logical string textstr. */
1312 jsint pos = 0;
1314 // TODO: consider branching to a simple loop if patlen == 1
1316 for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
1317 /* First try to match without spanning two nodes. */
1318 JSString *outer = *outerp;
1319 const jschar *chars = outer->nonRopeChars();
1320 size_t len = outer->length();
1321 jsint matchResult = StringMatch(chars, len, pat, patlen);
1322 if (matchResult != -1) {
1323 *match = pos + matchResult;
1324 return true;
1327 /* Test the overlap. */
1328 JSString **innerp = outerp;
1331 * Start searching at the first place where StringMatch wouldn't have
1332 * found the match.
1334 const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
1335 const jschar *const textend = chars + len;
1336 const jschar p0 = *pat;
1337 const jschar *const p1 = pat + 1;
1338 const jschar *const patend = pat + patlen;
1339 for (const jschar *t = text; t != textend; ) {
1340 if (*t++ != p0)
1341 continue;
1342 const jschar *ttend = textend;
1343 for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
1344 while (tt == ttend) {
1345 if (++innerp == strs.end()) {
1346 *match = -1;
1347 return true;
1349 JSString *inner = *innerp;
1350 tt = inner->nonRopeChars();
1351 ttend = tt + inner->length();
1353 if (*pp != *tt)
1354 goto break_continue;
1357 /* Matched! */
1358 *match = pos + (t - chars) - 1; /* -1 because of *t++ above */
1359 return true;
1361 break_continue:;
1364 pos += len;
1367 *match = -1;
1368 return true;
1371 static JSBool
1372 str_indexOf(JSContext *cx, uintN argc, Value *vp)
1375 JSString *str;
1376 NORMALIZE_THIS(cx, vp, str);
1378 JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0);
1379 if (!patstr)
1380 return false;
1382 jsuint textlen = str->length();
1383 const jschar *text = str->getChars(cx);
1384 if (!text)
1385 return false;
1387 jsuint patlen = patstr->length();
1388 const jschar *pat = patstr->chars();
1390 jsuint start;
1391 if (argc > 1) {
1392 if (vp[3].isInt32()) {
1393 jsint i = vp[3].toInt32();
1394 if (i <= 0) {
1395 start = 0;
1396 } else if (jsuint(i) > textlen) {
1397 start = textlen;
1398 textlen = 0;
1399 } else {
1400 start = i;
1401 text += start;
1402 textlen -= start;
1404 } else {
1405 jsdouble d;
1406 if (!ValueToNumber(cx, vp[3], &d))
1407 return JS_FALSE;
1408 d = js_DoubleToInteger(d);
1409 if (d <= 0) {
1410 start = 0;
1411 } else if (d > textlen) {
1412 start = textlen;
1413 textlen = 0;
1414 } else {
1415 start = (jsint)d;
1416 text += start;
1417 textlen -= start;
1420 } else {
1421 start = 0;
1424 jsint match = StringMatch(text, textlen, pat, patlen);
1425 vp->setInt32((match == -1) ? -1 : start + match);
1426 return true;
1429 static JSBool
1430 str_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
1432 JSString *textstr;
1433 NORMALIZE_THIS(cx, vp, textstr);
1434 size_t textlen = textstr->length();
1435 const jschar *text = textstr->getChars(cx);
1436 if (!text)
1437 return false;
1439 JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0);
1440 if (!patstr)
1441 return false;
1443 size_t patlen = patstr->length();
1444 const jschar *pat = patstr->chars();
1446 jsint i = textlen - patlen; // Start searching here
1447 if (i < 0) {
1448 vp->setInt32(-1);
1449 return true;
1452 if (argc > 1) {
1453 if (vp[3].isInt32()) {
1454 jsint j = vp[3].toInt32();
1455 if (j <= 0)
1456 i = 0;
1457 else if (j < i)
1458 i = j;
1459 } else {
1460 double d;
1461 if (!ValueToNumber(cx, vp[3], &d))
1462 return false;
1463 if (!JSDOUBLE_IS_NaN(d)) {
1464 d = js_DoubleToInteger(d);
1465 if (d <= 0)
1466 i = 0;
1467 else if (d < i)
1468 i = (jsint)d;
1473 if (patlen == 0) {
1474 vp->setInt32(i);
1475 return true;
1478 const jschar *t = text + i;
1479 const jschar *textend = text - 1;
1480 const jschar p0 = *pat;
1481 const jschar *patNext = pat + 1;
1482 const jschar *patEnd = pat + patlen;
1484 for (; t != textend; --t) {
1485 if (*t == p0) {
1486 const jschar *t1 = t + 1;
1487 for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
1488 if (*t1 != *p1)
1489 goto break_continue;
1491 vp->setInt32(t - text);
1492 return true;
1494 break_continue:;
1497 vp->setInt32(-1);
1498 return true;
1501 static JSBool
1502 js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
1504 JSString *str;
1505 NORMALIZE_THIS(cx, vp, str);
1506 size_t length = str->length();
1507 const jschar *chars = str->getChars(cx);
1508 if (!chars)
1509 return false;
1511 size_t begin = 0;
1512 size_t end = length;
1514 if (trimLeft) {
1515 while (begin < length && JS_ISSPACE(chars[begin]))
1516 ++begin;
1519 if (trimRight) {
1520 while (end > begin && JS_ISSPACE(chars[end-1]))
1521 --end;
1524 str = js_NewDependentString(cx, str, begin, end - begin);
1525 if (!str)
1526 return false;
1528 vp->setString(str);
1529 return true;
1532 static JSBool
1533 str_trim(JSContext *cx, uintN argc, Value *vp)
1535 return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
1538 static JSBool
1539 str_trimLeft(JSContext *cx, uintN argc, Value *vp)
1541 return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
1544 static JSBool
1545 str_trimRight(JSContext *cx, uintN argc, Value *vp)
1547 return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
1551 * Perl-inspired string functions.
1554 /* Result of a successfully performed flat match. */
1555 class FlatMatch
1557 JSLinearString *patstr;
1558 const jschar *pat;
1559 size_t patlen;
1560 int32 match_;
1562 friend class RegExpGuard;
1564 public:
1565 FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
1566 JSString *pattern() const { return patstr; }
1567 size_t patternLength() const { return patlen; }
1570 * Note: The match is -1 when the match is performed successfully,
1571 * but no match is found.
1573 int32 match() const { return match_; }
1576 /* A regexp and optional associated object. */
1577 class RegExpPair
1579 AutoRefCount<RegExp> arc;
1580 JSObject *reobj_;
1581 RegExp *re_;
1583 explicit RegExpPair(RegExpPair &);
1585 public:
1586 explicit RegExpPair(JSContext *cx, RegExp *re) : arc(cx, re), re_(re) {}
1588 void reset(JSObject &reobj) {
1589 reobj_ = &reobj;
1590 re_ = RegExp::extractFrom(&reobj);
1591 JS_ASSERT(re_);
1592 arc.reset(re_);
1595 void reset(RegExp &re) {
1596 re_ = &re;
1597 reobj_ = NULL;
1598 arc.reset(re_);
1601 /* Note: May be null. */
1602 JSObject *reobj() const { return reobj_; }
1603 RegExp &re() const { JS_ASSERT(re_); return *re_; }
1604 bool hasRegExp() const { return !!re_; }
1608 * RegExpGuard factors logic out of String regexp operations.
1610 * @param optarg Indicates in which argument position RegExp
1611 * flags will be found, if present. This is a Mozilla
1612 * extension and not part of any ECMA spec.
1614 class RegExpGuard
1616 RegExpGuard(const RegExpGuard &);
1617 void operator=(const RegExpGuard &);
1619 JSContext *cx;
1620 RegExpPair rep;
1621 FlatMatch fm;
1624 * Upper bound on the number of characters we are willing to potentially
1625 * waste on searching for RegExp meta-characters.
1627 static const size_t MAX_FLAT_PAT_LEN = 256;
1629 static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
1630 StringBuffer sb(cx);
1631 if (!sb.reserve(patstr->length()))
1632 return NULL;
1634 static const jschar ESCAPE_CHAR = '\\';
1635 const jschar *chars = patstr->chars();
1636 size_t len = patstr->length();
1637 for (const jschar *it = chars; it != chars + len; ++it) {
1638 if (RegExp::isMetaChar(*it)) {
1639 if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
1640 return NULL;
1641 } else {
1642 if (!sb.append(*it))
1643 return NULL;
1646 return sb.finishString();
1649 public:
1650 explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx, NULL) {}
1651 ~RegExpGuard() {}
1653 /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
1654 bool
1655 init(uintN argc, Value *vp)
1657 if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
1658 rep.reset(vp[2].toObject());
1659 } else {
1660 fm.patstr = ArgToRootedString(cx, argc, vp, 0);
1661 if (!fm.patstr)
1662 return false;
1664 return true;
1668 * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
1669 * pattern string, or a lengthy pattern string can thwart this process.
1671 * @param checkMetaChars Look for regexp metachars in the pattern string.
1672 * @return Whether flat matching could be used.
1674 * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
1676 const FlatMatch *
1677 tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
1678 bool checkMetaChars = true)
1680 if (rep.hasRegExp())
1681 return NULL;
1683 fm.pat = fm.patstr->chars();
1684 fm.patlen = fm.patstr->length();
1686 if (optarg < argc)
1687 return NULL;
1689 if (checkMetaChars &&
1690 (fm.patlen > MAX_FLAT_PAT_LEN || RegExp::hasMetaChars(fm.pat, fm.patlen))) {
1691 return NULL;
1695 * textstr could be a rope, so we want to avoid flattening it for as
1696 * long as possible.
1698 if (textstr->isRope()) {
1699 if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_))
1700 return NULL;
1701 } else {
1702 const jschar *text = textstr->nonRopeChars();
1703 size_t textlen = textstr->length();
1704 fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
1706 return &fm;
1709 /* If the pattern is not already a regular expression, make it so. */
1710 const RegExpPair *
1711 normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
1713 if (rep.hasRegExp())
1714 return &rep;
1716 /* Build RegExp from pattern string. */
1717 JSString *opt;
1718 if (optarg < argc) {
1719 opt = js_ValueToString(cx, vp[2 + optarg]);
1720 if (!opt)
1721 return NULL;
1722 } else {
1723 opt = NULL;
1726 JSString *patstr;
1727 if (flat) {
1728 patstr = flattenPattern(cx, fm.patstr);
1729 if (!patstr)
1730 return false;
1731 } else {
1732 patstr = fm.patstr;
1734 JS_ASSERT(patstr);
1736 RegExp *re = RegExp::createFlagged(cx, patstr, opt);
1737 if (!re)
1738 return NULL;
1739 rep.reset(*re);
1740 return &rep;
1743 #if DEBUG
1744 bool hasRegExpPair() const { return rep.hasRegExp(); }
1745 #endif
1748 /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
1749 static JS_ALWAYS_INLINE bool
1750 Matched(bool test, const Value &v)
1752 return test ? v.isTrue() : !v.isNull();
1755 typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
1758 * BitOR-ing these flags allows the DoMatch caller to control when how the
1759 * RegExp engine is called and when callbacks are fired.
1761 enum MatchControlFlags {
1762 TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
1763 TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
1764 CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
1766 MATCH_ARGS = TEST_GLOBAL_BIT,
1767 MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
1768 REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
1771 /* Factor out looping and matching logic. */
1772 static bool
1773 DoMatch(JSContext *cx, RegExpStatics *res, Value *vp, JSString *str, const RegExpPair &rep,
1774 DoMatchCallback callback, void *data, MatchControlFlags flags)
1776 RegExp &re = rep.re();
1777 if (re.global()) {
1778 /* global matching ('g') */
1779 bool testGlobal = flags & TEST_GLOBAL_BIT;
1780 if (rep.reobj())
1781 rep.reobj()->zeroRegExpLastIndex();
1782 for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
1783 if (!re.execute(cx, res, str, &i, testGlobal, vp))
1784 return false;
1785 if (!Matched(testGlobal, *vp))
1786 break;
1787 if (!callback(cx, res, count, data))
1788 return false;
1789 if (!res->matched())
1790 ++i;
1792 } else {
1793 /* single match */
1794 bool testSingle = !!(flags & TEST_SINGLE_BIT),
1795 callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
1796 size_t i = 0;
1797 if (!re.execute(cx, res, str, &i, testSingle, vp))
1798 return false;
1799 if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, res, 0, data))
1800 return false;
1802 return true;
1805 static bool
1806 BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value *vp)
1808 if (fm.match() < 0) {
1809 vp->setNull();
1810 return true;
1813 /* For this non-global match, produce a RegExp.exec-style array. */
1814 JSObject *obj = NewSlowEmptyArray(cx);
1815 if (!obj)
1816 return false;
1817 vp->setObject(*obj);
1819 return obj->defineProperty(cx, INT_TO_JSID(0), StringValue(fm.pattern())) &&
1820 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
1821 Int32Value(fm.match())) &&
1822 obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
1823 StringValue(textstr));
1826 typedef JSObject **MatchArgType;
1829 * DoMatch will only callback on global matches, hence this function builds
1830 * only the "array of matches" returned by match on global regexps.
1832 static bool
1833 MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
1835 JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */
1837 JSObject *&arrayobj = *static_cast<MatchArgType>(p);
1838 if (!arrayobj) {
1839 arrayobj = NewDenseEmptyArray(cx);
1840 if (!arrayobj)
1841 return false;
1844 Value v;
1845 if (!res->createLastMatch(cx, &v))
1846 return false;
1848 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
1849 return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false);
1852 static JSBool
1853 str_match(JSContext *cx, uintN argc, Value *vp)
1855 JSString *str;
1856 NORMALIZE_THIS(cx, vp, str);
1858 RegExpGuard g(cx);
1859 if (!g.init(argc, vp))
1860 return false;
1861 if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc))
1862 return BuildFlatMatchArray(cx, str, *fm, vp);
1863 if (cx->isExceptionPending()) /* from tryFlatMatch */
1864 return false;
1866 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1867 if (!rep)
1868 return false;
1870 AutoObjectRooter array(cx);
1871 MatchArgType arg = array.addr();
1872 RegExpStatics *res = cx->regExpStatics();
1873 if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS))
1874 return false;
1876 /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */
1877 if (rep->re().global())
1878 vp->setObjectOrNull(array.object());
1879 return true;
1882 static JSBool
1883 str_search(JSContext *cx, uintN argc, Value *vp)
1885 JSString *str;
1886 NORMALIZE_THIS(cx, vp, str);
1888 RegExpGuard g(cx);
1889 if (!g.init(argc, vp))
1890 return false;
1891 if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, argc)) {
1892 vp->setInt32(fm->match());
1893 return true;
1895 if (cx->isExceptionPending()) /* from tryFlatMatch */
1896 return false;
1897 const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
1898 if (!rep)
1899 return false;
1901 RegExpStatics *res = cx->regExpStatics();
1902 size_t i = 0;
1903 if (!rep->re().execute(cx, res, str, &i, true, vp))
1904 return false;
1906 if (vp->isTrue())
1907 vp->setInt32(res->matchStart());
1908 else
1909 vp->setInt32(-1);
1910 return true;
1913 struct ReplaceData
1915 ReplaceData(JSContext *cx)
1916 : g(cx), sb(cx)
1919 JSString *str; /* 'this' parameter object as a string */
1920 RegExpGuard g; /* regexp parameter object and private data */
1921 JSObject *lambda; /* replacement function object or null */
1922 JSObject *elembase; /* object for function(a){return b[a]} replace */
1923 JSLinearString *repstr; /* replacement string */
1924 const jschar *dollar; /* null or pointer to first $ in repstr */
1925 const jschar *dollarEnd; /* limit pointer for js_strchr_limit */
1926 jsint leftIndex; /* left context index in str->chars */
1927 JSSubString dollarStr; /* for "$$" InterpretDollar result */
1928 bool calledBack; /* record whether callback has been called */
1929 InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
1930 InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
1931 StringBuffer sb; /* buffer built during DoMatch */
1934 static bool
1935 InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep,
1936 ReplaceData &rdata, JSSubString *out, size_t *skip)
1938 JS_ASSERT(*dp == '$');
1940 /* If there is only a dollar, bail now */
1941 if (dp + 1 >= ep)
1942 return false;
1944 /* Interpret all Perl match-induced dollar variables. */
1945 jschar dc = dp[1];
1946 if (JS7_ISDEC(dc)) {
1947 /* ECMA-262 Edition 3: 1-9 or 01-99 */
1948 uintN num = JS7_UNDEC(dc);
1949 if (num > res->parenCount())
1950 return false;
1952 const jschar *cp = dp + 2;
1953 if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
1954 uintN tmp = 10 * num + JS7_UNDEC(dc);
1955 if (tmp <= res->parenCount()) {
1956 cp++;
1957 num = tmp;
1960 if (num == 0)
1961 return false;
1963 *skip = cp - dp;
1965 JS_ASSERT(num <= res->parenCount());
1968 * Note: we index to get the paren with the (1-indexed) pair
1969 * number, as opposed to a (0-indexed) paren number.
1971 res->getParen(num, out);
1972 return true;
1975 *skip = 2;
1976 switch (dc) {
1977 case '$':
1978 rdata.dollarStr.chars = dp;
1979 rdata.dollarStr.length = 1;
1980 *out = rdata.dollarStr;
1981 return true;
1982 case '&':
1983 res->getLastMatch(out);
1984 return true;
1985 case '+':
1986 res->getLastParen(out);
1987 return true;
1988 case '`':
1989 res->getLeftContext(out);
1990 return true;
1991 case '\'':
1992 res->getRightContext(out);
1993 return true;
1995 return false;
1998 static bool
1999 FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t *sizep)
2001 JSObject *base = rdata.elembase;
2002 if (base) {
2004 * The base object is used when replace was passed a lambda which looks like
2005 * 'function(a) { return b[a]; }' for the base object b. b will not change
2006 * in the course of the replace unless we end up making a scripted call due
2007 * to accessing a scripted getter or a value with a scripted toString.
2009 JS_ASSERT(rdata.lambda);
2010 JS_ASSERT(!base->getOps()->lookupProperty);
2011 JS_ASSERT(!base->getOps()->getProperty);
2013 Value match;
2014 if (!res->createLastMatch(cx, &match))
2015 return false;
2016 JSString *str = match.toString();
2018 JSAtom *atom;
2019 if (str->isAtomized()) {
2020 atom = STRING_TO_ATOM(str);
2021 } else {
2022 atom = js_AtomizeString(cx, str, 0);
2023 if (!atom)
2024 return false;
2026 jsid id = ATOM_TO_JSID(atom);
2028 JSObject *holder;
2029 JSProperty *prop = NULL;
2030 if (js_LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop) < 0)
2031 return false;
2033 /* Only handle the case where the property exists and is on this object. */
2034 if (prop && holder == base) {
2035 Shape *shape = (Shape *) prop;
2036 if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) {
2037 Value value = base->getSlot(shape->slot);
2038 if (value.isString()) {
2039 rdata.repstr = value.toString()->ensureLinear(cx);
2040 if (!rdata.repstr)
2041 return false;
2042 *sizep = rdata.repstr->length();
2043 return true;
2049 * Couldn't handle this property, fall through and despecialize to the
2050 * general lambda case.
2052 rdata.elembase = NULL;
2055 JSObject *lambda = rdata.lambda;
2056 if (lambda) {
2058 * In the lambda case, not only do we find the replacement string's
2059 * length, we compute repstr and return it via rdata for use within
2060 * DoReplace. The lambda is called with arguments ($&, $1, $2, ...,
2061 * index, input), i.e., all the properties of a regexp match array.
2062 * For $&, etc., we must create string jsvals from cx->regExpStatics.
2063 * We grab up stack space to keep the newborn strings GC-rooted.
2065 uintN p = res->parenCount();
2066 uintN argc = 1 + p + 2;
2068 InvokeSessionGuard &session = rdata.session;
2069 if (!session.started()) {
2070 Value lambdav = ObjectValue(*lambda);
2071 if (!session.start(cx, lambdav, UndefinedValue(), argc))
2072 return false;
2075 PreserveRegExpStatics staticsGuard(res);
2076 if (!staticsGuard.init(cx))
2077 return false;
2079 /* Push $&, $1, $2, ... */
2080 uintN argi = 0;
2081 if (!res->createLastMatch(cx, &session[argi++]))
2082 return false;
2084 for (size_t i = 0; i < res->parenCount(); ++i) {
2085 if (!res->createParen(cx, i + 1, &session[argi++]))
2086 return false;
2089 /* Push match index and input string. */
2090 session[argi++].setInt32(res->matchStart());
2091 session[argi].setString(rdata.str);
2093 if (!session.invoke(cx))
2094 return false;
2096 /* root repstr: rdata is on the stack, so scanned by conservative gc. */
2097 JSString *repstr = ValueToString_TestForStringInline(cx, session.rval());
2098 if (!repstr)
2099 return false;
2100 rdata.repstr = repstr->ensureLinear(cx);
2101 if (!rdata.repstr)
2102 return false;
2103 *sizep = rdata.repstr->length();
2104 return true;
2107 JSString *repstr = rdata.repstr;
2108 size_t replen = repstr->length();
2109 for (const jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp;
2110 dp = js_strchr_limit(dp, '$', ep)) {
2111 JSSubString sub;
2112 size_t skip;
2113 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2114 replen += sub.length - skip;
2115 dp += skip;
2116 } else {
2117 dp++;
2120 *sizep = replen;
2121 return true;
2125 * Precondition: |rdata.sb| already has necessary growth space reserved (as
2126 * derived from FindReplaceLength).
2128 static void
2129 DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
2131 JSLinearString *repstr = rdata.repstr;
2132 const jschar *cp;
2133 const jschar *bp = cp = repstr->chars();
2135 const jschar *dp = rdata.dollar;
2136 const jschar *ep = rdata.dollarEnd;
2137 for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
2138 /* Move one of the constant portions of the replacement value. */
2139 size_t len = dp - cp;
2140 JS_ALWAYS_TRUE(rdata.sb.append(cp, len));
2141 cp = dp;
2143 JSSubString sub;
2144 size_t skip;
2145 if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
2146 len = sub.length;
2147 JS_ALWAYS_TRUE(rdata.sb.append(sub.chars, len));
2148 cp += skip;
2149 dp += skip;
2150 } else {
2151 dp++;
2154 JS_ALWAYS_TRUE(rdata.sb.append(cp, repstr->length() - (cp - bp)));
2157 static bool
2158 ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
2160 ReplaceData &rdata = *static_cast<ReplaceData *>(p);
2162 rdata.calledBack = true;
2163 JSLinearString *str = rdata.str->assertIsLinear(); /* flattened for regexp */
2164 size_t leftoff = rdata.leftIndex;
2165 const jschar *left = str->chars() + leftoff;
2166 size_t leftlen = res->matchStart() - leftoff;
2167 rdata.leftIndex = res->matchLimit();
2169 size_t replen = 0; /* silence 'unused' warning */
2170 if (!FindReplaceLength(cx, res, rdata, &replen))
2171 return false;
2173 size_t growth = leftlen + replen;
2174 if (!rdata.sb.reserve(rdata.sb.length() + growth))
2175 return false;
2176 JS_ALWAYS_TRUE(rdata.sb.append(left, leftlen)); /* skipped-over portion of the search value */
2177 DoReplace(cx, res, rdata);
2178 return true;
2181 static bool
2182 BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
2183 const FlatMatch &fm, Value *vp)
2185 RopeBuilder builder(cx);
2186 size_t match = fm.match();
2187 size_t matchEnd = match + fm.patternLength();
2189 if (textstr->isRope()) {
2191 * If we are replacing over a rope, avoid flattening it by iterating
2192 * through it, building a new rope.
2194 StringSegmentRange r(cx);
2195 if (!r.init(textstr))
2196 return false;
2197 size_t pos = 0;
2198 while (!r.empty()) {
2199 JSString *str = r.front();
2200 size_t len = str->length();
2201 size_t strEnd = pos + len;
2202 if (pos < matchEnd && strEnd > match) {
2204 * We need to special-case any part of the rope that overlaps
2205 * with the replacement string.
2207 if (match >= pos) {
2209 * If this part of the rope overlaps with the left side of
2210 * the pattern, then it must be the only one to overlap with
2211 * the first character in the pattern, so we include the
2212 * replacement string here.
2214 JSString *leftSide = js_NewDependentString(cx, str, 0, match - pos);
2215 if (!leftSide ||
2216 !builder.append(leftSide) ||
2217 !builder.append(repstr)) {
2218 return false;
2223 * If str runs off the end of the matched string, append the
2224 * last part of str.
2226 if (strEnd > matchEnd) {
2227 JSString *rightSide = js_NewDependentString(cx, str, matchEnd - pos,
2228 strEnd - matchEnd);
2229 if (!rightSide || !builder.append(rightSide))
2230 return false;
2232 } else {
2233 if (!builder.append(str))
2234 return false;
2236 pos += str->length();
2237 if (!r.popFront())
2238 return false;
2240 } else {
2241 JSString *leftSide = js_NewDependentString(cx, textstr, 0, match);
2242 if (!leftSide)
2243 return false;
2244 JSString *rightSide = js_NewDependentString(cx, textstr, match + fm.patternLength(),
2245 textstr->length() - match - fm.patternLength());
2246 if (!rightSide ||
2247 !builder.append(leftSide) ||
2248 !builder.append(repstr) ||
2249 !builder.append(rightSide)) {
2250 return false;
2254 vp->setString(builder.result());
2255 return true;
2259 * Perform a linear-scan dollar substitution on the replacement text,
2260 * constructing a result string that looks like:
2262 * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
2264 static inline bool
2265 BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
2266 const jschar *firstDollar, const FlatMatch &fm, Value *vp)
2268 JSLinearString *textstr = textstrArg->ensureLinear(cx);
2269 if (!textstr)
2270 return NULL;
2272 JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length());
2273 size_t matchStart = fm.match();
2274 size_t matchLimit = matchStart + fm.patternLength();
2277 * Most probably:
2279 * len(newstr) >= len(orig) - len(match) + len(replacement)
2281 * Note that dollar vars _could_ make the resulting text smaller than this.
2283 StringBuffer newReplaceChars(cx);
2284 if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
2285 return false;
2287 /* Move the pre-dollar chunk in bulk. */
2288 JS_ALWAYS_TRUE(newReplaceChars.append(repstr->chars(), firstDollar));
2290 /* Move the rest char-by-char, interpreting dollars as we encounter them. */
2291 #define ENSURE(__cond) if (!(__cond)) return false;
2292 const jschar *repstrLimit = repstr->chars() + repstr->length();
2293 for (const jschar *it = firstDollar; it < repstrLimit; ++it) {
2294 if (*it != '$' || it == repstrLimit - 1) {
2295 ENSURE(newReplaceChars.append(*it));
2296 continue;
2299 switch (*(it + 1)) {
2300 case '$': /* Eat one of the dollars. */
2301 ENSURE(newReplaceChars.append(*it));
2302 break;
2303 case '&':
2304 ENSURE(newReplaceChars.append(textstr->chars() + matchStart,
2305 textstr->chars() + matchLimit));
2306 break;
2307 case '`':
2308 ENSURE(newReplaceChars.append(textstr->chars(), textstr->chars() + matchStart));
2309 break;
2310 case '\'':
2311 ENSURE(newReplaceChars.append(textstr->chars() + matchLimit,
2312 textstr->chars() + textstr->length()));
2313 break;
2314 default: /* The dollar we saw was not special (no matter what its mother told it). */
2315 ENSURE(newReplaceChars.append(*it));
2316 continue;
2318 ++it; /* We always eat an extra char in the above switch. */
2321 JSString *leftSide = js_NewDependentString(cx, textstr, 0, matchStart);
2322 ENSURE(leftSide);
2324 JSString *newReplace = newReplaceChars.finishString();
2325 ENSURE(newReplace);
2327 JS_ASSERT(textstr->length() >= matchLimit);
2328 JSString *rightSide = js_NewDependentString(cx, textstr, matchLimit,
2329 textstr->length() - matchLimit);
2330 ENSURE(rightSide);
2332 RopeBuilder builder(cx);
2333 ENSURE(builder.append(leftSide) &&
2334 builder.append(newReplace) &&
2335 builder.append(rightSide));
2336 #undef ENSURE
2338 vp->setString(builder.result());
2339 return true;
2342 static inline bool
2343 str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata)
2345 const RegExpPair *rep = rdata.g.normalizeRegExp(true, 2, argc, vp);
2346 if (!rep)
2347 return false;
2349 rdata.leftIndex = 0;
2350 rdata.calledBack = false;
2352 RegExpStatics *res = cx->regExpStatics();
2353 if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS))
2354 return false;
2356 if (!rdata.calledBack) {
2357 /* Didn't match, so the string is unmodified. */
2358 vp->setString(rdata.str);
2359 return true;
2362 JSSubString sub;
2363 res->getRightContext(&sub);
2364 if (!rdata.sb.append(sub.chars, sub.length))
2365 return false;
2367 JSString *retstr = rdata.sb.finishString();
2368 if (!retstr)
2369 return false;
2371 vp->setString(retstr);
2372 return true;
2375 static inline bool
2376 str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata,
2377 const FlatMatch &fm)
2379 JS_ASSERT(fm.match() >= 0);
2380 LeaveTrace(cx);
2382 JSString *matchStr = js_NewDependentString(cx, rdata.str, fm.match(), fm.patternLength());
2383 if (!matchStr)
2384 return false;
2386 /* lambda(matchStr, matchStart, textstr) */
2387 static const uint32 lambdaArgc = 3;
2388 if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
2389 return false;
2391 CallArgs &args = rdata.singleShot;
2392 args.callee().setObject(*rdata.lambda);
2393 args.thisv().setUndefined();
2395 Value *sp = args.argv();
2396 sp[0].setString(matchStr);
2397 sp[1].setInt32(fm.match());
2398 sp[2].setString(rdata.str);
2400 if (!Invoke(cx, rdata.singleShot, 0))
2401 return false;
2403 JSString *repstr = js_ValueToString(cx, args.rval());
2404 if (!repstr)
2405 return false;
2407 JSString *leftSide = js_NewDependentString(cx, rdata.str, 0, fm.match());
2408 if (!leftSide)
2409 return false;
2411 size_t matchLimit = fm.match() + fm.patternLength();
2412 JSString *rightSide = js_NewDependentString(cx, rdata.str, matchLimit,
2413 rdata.str->length() - matchLimit);
2414 if (!rightSide)
2415 return false;
2417 RopeBuilder builder(cx);
2418 if (!(builder.append(leftSide) &&
2419 builder.append(repstr) &&
2420 builder.append(rightSide))) {
2421 return false;
2424 vp->setString(builder.result());
2425 return true;
2428 JSBool
2429 js::str_replace(JSContext *cx, uintN argc, Value *vp)
2431 ReplaceData rdata(cx);
2432 NORMALIZE_THIS(cx, vp, rdata.str);
2433 static const uint32 optarg = 2;
2435 /* Extract replacement string/function. */
2436 if (argc >= optarg && js_IsCallable(vp[3])) {
2437 rdata.lambda = &vp[3].toObject();
2438 rdata.elembase = NULL;
2439 rdata.repstr = NULL;
2440 rdata.dollar = rdata.dollarEnd = NULL;
2442 if (rdata.lambda->isFunction()) {
2443 JSFunction *fun = rdata.lambda->getFunctionPrivate();
2444 if (fun->isInterpreted()) {
2446 * Pattern match the script to check if it is is indexing into a
2447 * particular object, e.g. 'function(a) { return b[a]; }'. Avoid
2448 * calling the script in such cases, which are used by javascript
2449 * packers (particularly the popular Dean Edwards packer) to efficiently
2450 * encode large scripts. We only handle the code patterns generated
2451 * by such packers here.
2453 JSScript *script = fun->u.i.script;
2454 jsbytecode *pc = script->code;
2456 Value table = UndefinedValue();
2457 if (JSOp(*pc) == JSOP_GETFCSLOT) {
2458 table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc));
2459 pc += JSOP_GETFCSLOT_LENGTH;
2462 if (table.isObject() &&
2463 JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
2464 JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM &&
2465 JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) {
2466 Class *clasp = table.toObject().getClass();
2467 if (clasp->isNative() &&
2468 !clasp->ops.lookupProperty &&
2469 !clasp->ops.getProperty) {
2470 rdata.elembase = &table.toObject();
2475 } else {
2476 rdata.lambda = NULL;
2477 rdata.elembase = NULL;
2478 rdata.repstr = ArgToRootedString(cx, argc, vp, 1);
2479 if (!rdata.repstr)
2480 return false;
2482 /* We're about to store pointers into the middle of our string. */
2483 if (!js_MakeStringImmutable(cx, rdata.repstr))
2484 return false;
2485 rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length();
2486 rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$',
2487 rdata.dollarEnd);
2490 if (!rdata.g.init(argc, vp))
2491 return false;
2494 * Unlike its |String.prototype| brethren, |replace| doesn't convert
2495 * its input to a regular expression. (Even if it contains metachars.)
2497 * However, if the user invokes our (non-standard) |flags| argument
2498 * extension then we revert to creating a regular expression. Note that
2499 * this is observable behavior through the side-effect mutation of the
2500 * |RegExp| statics.
2503 const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false);
2504 if (!fm) {
2505 if (cx->isExceptionPending()) /* oom in RopeMatch in tryFlatMatch */
2506 return false;
2507 JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg);
2508 return str_replace_regexp(cx, argc, vp, rdata);
2511 if (fm->match() < 0) {
2512 vp->setString(rdata.str);
2513 return true;
2516 if (rdata.lambda)
2517 return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
2520 * Note: we could optimize the text.length == pattern.length case if we wanted,
2521 * even in the presence of dollar metachars.
2523 if (rdata.dollar)
2524 return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollar, *fm, vp);
2526 return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
2530 * Subroutine used by str_split to find the next split point in str, starting
2531 * at offset *ip and looking either for the separator substring given by sep, or
2532 * for the next re match. In the re case, return the matched separator in *sep,
2533 * and the possibly updated offset in *ip.
2535 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
2536 * separator occurrence if found, or str->length if no separator is found.
2538 static jsint
2539 find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
2540 JSSubString *sep)
2543 * Stop if past end of string. If at end of string, we will compare the
2544 * null char stored there (by js_NewString*) to sep->chars[j] in the while
2545 * loop at the end of this function, so that
2547 * "ab,".split(',') => ["ab", ""]
2549 * and the resulting array converts back to the string "ab," for symmetry.
2550 * However, we ape Perl and do this only if there is a sufficiently large
2551 * limit argument (see str_split).
2553 jsint i = *ip;
2554 size_t length = str->length();
2555 if ((size_t)i > length)
2556 return -1;
2558 const jschar *chars = str->getChars(cx);
2559 if (!chars)
2560 return -2;
2563 * Match a regular expression against the separator at or above index i.
2564 * Call js_ExecuteRegExp with true for the test argument. On successful
2565 * match, get the separator from cx->regExpStatics.lastMatch.
2567 if (re) {
2568 size_t index;
2569 Value rval;
2571 again:
2572 /* JS1.2 deviated from Perl by never matching at end of string. */
2573 index = (size_t)i;
2574 if (!re->execute(cx, res, str, &index, true, &rval))
2575 return -2;
2576 if (!rval.isTrue()) {
2577 /* Mismatch: ensure our caller advances i past end of string. */
2578 sep->length = 1;
2579 return length;
2581 i = (jsint)index;
2582 JS_ASSERT(sep);
2583 res->getLastMatch(sep);
2584 if (sep->length == 0) {
2586 * Empty string match: never split on an empty match at the start
2587 * of a find_split cycle. Same rule as for an empty global match
2588 * in DoMatch.
2590 if (i == *ip) {
2592 * "Bump-along" to avoid sticking at an empty match, but don't
2593 * bump past end of string -- our caller must do that by adding
2594 * sep->length to our return value.
2596 if ((size_t)i == length)
2597 return -1;
2598 i++;
2599 goto again;
2601 if ((size_t)i == length) {
2603 * If there was a trivial zero-length match at the end of the
2604 * split, then we shouldn't output the matched string at the end
2605 * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
2607 sep->chars = NULL;
2610 JS_ASSERT((size_t)i >= sep->length);
2611 return i - sep->length;
2615 * Special case: if sep is the empty string, split str into one character
2616 * substrings. Let our caller worry about whether to split once at end of
2617 * string into an empty substring.
2619 if (sep->length == 0)
2620 return ((size_t)i == length) ? -1 : i + 1;
2623 * Now that we know sep is non-empty, search starting at i in str for an
2624 * occurrence of all of sep's chars. If we find them, return the index of
2625 * the first separator char. Otherwise, return length.
2627 jsint match = StringMatch(chars + i, length - i, sep->chars, sep->length);
2628 return match == -1 ? length : match + i;
2631 static JSBool
2632 str_split(JSContext *cx, uintN argc, Value *vp)
2634 JSString *str;
2635 NORMALIZE_THIS(cx, vp, str);
2637 if (argc == 0) {
2638 Value v = StringValue(str);
2639 JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
2640 if (!aobj)
2641 return false;
2642 vp->setObject(*aobj);
2643 return true;
2646 RegExp *re;
2647 JSSubString *sep, tmp;
2648 if (VALUE_IS_REGEXP(cx, vp[2])) {
2649 re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
2650 sep = &tmp;
2652 /* Set a magic value so we can detect a successful re match. */
2653 sep->chars = NULL;
2654 sep->length = 0;
2655 } else {
2656 JSString *sepstr = js_ValueToString(cx, vp[2]);
2657 if (!sepstr)
2658 return false;
2659 vp[2].setString(sepstr);
2662 * Point sep at a local copy of sepstr's header because find_split
2663 * will modify sep->length.
2665 tmp.length = sepstr->length();
2666 tmp.chars = sepstr->getChars(cx);
2667 if (!tmp.chars)
2668 return false;
2669 re = NULL;
2670 sep = &tmp;
2673 /* Use the second argument as the split limit, if given. */
2674 uint32 limit = 0; /* Avoid warning. */
2675 bool limited = (argc > 1) && !vp[3].isUndefined();
2676 if (limited) {
2677 jsdouble d;
2678 if (!ValueToNumber(cx, vp[3], &d))
2679 return false;
2681 /* Clamp limit between 0 and 1 + string length. */
2682 limit = js_DoubleToECMAUint32(d);
2683 if (limit > str->length())
2684 limit = 1 + str->length();
2687 AutoValueVector splits(cx);
2689 RegExpStatics *res = cx->regExpStatics();
2690 jsint i, j;
2691 uint32 len = i = 0;
2692 while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
2693 if (limited && len >= limit)
2694 break;
2696 JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
2697 if (!sub || !splits.append(StringValue(sub)))
2698 return false;
2699 len++;
2702 * Imitate perl's feature of including parenthesized substrings that
2703 * matched part of the delimiter in the new array, after the split
2704 * substring that was delimited.
2706 if (re && sep->chars) {
2707 for (uintN num = 0; num < res->parenCount(); num++) {
2708 if (limited && len >= limit)
2709 break;
2710 JSSubString parsub;
2711 res->getParen(num + 1, &parsub);
2712 sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
2713 if (!sub || !splits.append(StringValue(sub)))
2714 return false;
2715 len++;
2717 sep->chars = NULL;
2719 i = j + sep->length;
2722 if (j == -2)
2723 return false;
2725 JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
2726 if (!aobj)
2727 return false;
2728 vp->setObject(*aobj);
2729 return true;
2732 #if JS_HAS_PERL_SUBSTR
2733 static JSBool
2734 str_substr(JSContext *cx, uintN argc, Value *vp)
2736 JSString *str;
2737 int32 length, len, begin;
2739 NORMALIZE_THIS(cx, vp, str);
2741 if (argc > 0) {
2742 length = int32(str->length());
2743 if (!ValueToIntegerRange(cx, vp[2], &begin))
2744 return false;
2746 if (begin >= length) {
2747 str = cx->runtime->emptyString;
2748 goto out;
2750 if (begin < 0) {
2751 begin += length; /* length + INT_MIN will always be less then 0 */
2752 if (begin < 0)
2753 begin = 0;
2756 if (argc == 1 || vp[3].isUndefined()) {
2757 len = length - begin;
2758 } else {
2759 if (!ValueToIntegerRange(cx, vp[3], &len))
2760 return false;
2762 if (len <= 0) {
2763 str = cx->runtime->emptyString;
2764 goto out;
2767 if (uint32(length) < uint32(begin + len))
2768 len = length - begin;
2771 str = js_NewDependentString(cx, str, size_t(begin), size_t(len));
2772 if (!str)
2773 return false;
2776 out:
2777 vp->setString(str);
2778 return true;
2780 #endif /* JS_HAS_PERL_SUBSTR */
2783 * Python-esque sequence operations.
2785 static JSBool
2786 str_concat(JSContext *cx, uintN argc, Value *vp)
2788 JSString *str, *str2;
2789 Value *argv;
2790 uintN i;
2792 NORMALIZE_THIS(cx, vp, str);
2794 /* Set vp (aka rval) early to handle the argc == 0 case. */
2795 vp->setString(str);
2797 for (i = 0, argv = vp + 2; i < argc; i++) {
2798 str2 = js_ValueToString(cx, argv[i]);
2799 if (!str2)
2800 return JS_FALSE;
2801 argv[i].setString(str2);
2803 str = js_ConcatStrings(cx, str, str2);
2804 if (!str)
2805 return JS_FALSE;
2806 vp->setString(str);
2809 return JS_TRUE;
2812 static JSBool
2813 str_slice(JSContext *cx, uintN argc, Value *vp)
2815 if (argc == 1 && vp[1].isString() && vp[2].isInt32()) {
2816 size_t begin, end, length;
2818 JSString *str = vp[1].toString();
2819 begin = vp[2].toInt32();
2820 end = str->length();
2821 if (begin <= end) {
2822 length = end - begin;
2823 if (length == 0) {
2824 str = cx->runtime->emptyString;
2825 } else {
2826 str = (length == 1)
2827 ? JSString::getUnitString(cx, str, begin)
2828 : js_NewDependentString(cx, str, begin, length);
2829 if (!str)
2830 return JS_FALSE;
2832 vp->setString(str);
2833 return JS_TRUE;
2837 JSString *str;
2838 NORMALIZE_THIS(cx, vp, str);
2840 if (argc != 0) {
2841 double begin, end, length;
2843 if (!ValueToNumber(cx, vp[2], &begin))
2844 return JS_FALSE;
2845 begin = js_DoubleToInteger(begin);
2846 length = str->length();
2847 if (begin < 0) {
2848 begin += length;
2849 if (begin < 0)
2850 begin = 0;
2851 } else if (begin > length) {
2852 begin = length;
2855 if (argc == 1 || vp[3].isUndefined()) {
2856 end = length;
2857 } else {
2858 if (!ValueToNumber(cx, vp[3], &end))
2859 return JS_FALSE;
2860 end = js_DoubleToInteger(end);
2861 if (end < 0) {
2862 end += length;
2863 if (end < 0)
2864 end = 0;
2865 } else if (end > length) {
2866 end = length;
2868 if (end < begin)
2869 end = begin;
2872 str = js_NewDependentString(cx, str,
2873 (size_t)begin,
2874 (size_t)(end - begin));
2875 if (!str)
2876 return JS_FALSE;
2878 vp->setString(str);
2879 return JS_TRUE;
2882 #if JS_HAS_STR_HTML_HELPERS
2884 * HTML composition aids.
2886 static bool
2887 tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end,
2888 Value *vp)
2890 JSString *thisstr;
2891 NORMALIZE_THIS(cx, vp, thisstr);
2892 JSLinearString *str = thisstr->ensureLinear(cx);
2893 if (!str)
2894 return false;
2896 if (!end)
2897 end = begin;
2899 size_t beglen = strlen(begin);
2900 size_t taglen = 1 + beglen + 1; /* '<begin' + '>' */
2901 size_t parlen = 0; /* Avoid warning. */
2902 if (param) {
2903 parlen = param->length();
2904 taglen += 2 + parlen + 1; /* '="param"' */
2906 size_t endlen = strlen(end);
2907 taglen += str->length() + 2 + endlen + 1; /* 'str</end>' */
2909 if (taglen >= ~(size_t)0 / sizeof(jschar)) {
2910 js_ReportAllocationOverflow(cx);
2911 return false;
2914 jschar *tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar));
2915 if (!tagbuf)
2916 return false;
2918 size_t j = 0;
2919 tagbuf[j++] = '<';
2920 for (size_t i = 0; i < beglen; i++)
2921 tagbuf[j++] = (jschar)begin[i];
2922 if (param) {
2923 tagbuf[j++] = '=';
2924 tagbuf[j++] = '"';
2925 js_strncpy(&tagbuf[j], param->chars(), parlen);
2926 j += parlen;
2927 tagbuf[j++] = '"';
2929 tagbuf[j++] = '>';
2931 js_strncpy(&tagbuf[j], str->chars(), str->length());
2932 j += str->length();
2933 tagbuf[j++] = '<';
2934 tagbuf[j++] = '/';
2935 for (size_t i = 0; i < endlen; i++)
2936 tagbuf[j++] = (jschar)end[i];
2937 tagbuf[j++] = '>';
2938 JS_ASSERT(j == taglen);
2939 tagbuf[j] = 0;
2941 JSString *retstr = js_NewString(cx, tagbuf, taglen);
2942 if (!retstr) {
2943 js_free((char *)tagbuf);
2944 return false;
2946 vp->setString(retstr);
2947 return true;
2950 static JSBool
2951 tagify_value(JSContext *cx, uintN argc, Value *vp,
2952 const char *begin, const char *end)
2954 JSLinearString *param = ArgToRootedString(cx, argc, vp, 0);
2955 if (!param)
2956 return JS_FALSE;
2957 return tagify(cx, begin, param, end, vp);
2960 static JSBool
2961 str_bold(JSContext *cx, uintN argc, Value *vp)
2963 return tagify(cx, "b", NULL, NULL, vp);
2966 static JSBool
2967 str_italics(JSContext *cx, uintN argc, Value *vp)
2969 return tagify(cx, "i", NULL, NULL, vp);
2972 static JSBool
2973 str_fixed(JSContext *cx, uintN argc, Value *vp)
2975 return tagify(cx, "tt", NULL, NULL, vp);
2978 static JSBool
2979 str_fontsize(JSContext *cx, uintN argc, Value *vp)
2981 return tagify_value(cx, argc, vp, "font size", "font");
2984 static JSBool
2985 str_fontcolor(JSContext *cx, uintN argc, Value *vp)
2987 return tagify_value(cx, argc, vp, "font color", "font");
2990 static JSBool
2991 str_link(JSContext *cx, uintN argc, Value *vp)
2993 return tagify_value(cx, argc, vp, "a href", "a");
2996 static JSBool
2997 str_anchor(JSContext *cx, uintN argc, Value *vp)
2999 return tagify_value(cx, argc, vp, "a name", "a");
3002 static JSBool
3003 str_strike(JSContext *cx, uintN argc, Value *vp)
3005 return tagify(cx, "strike", NULL, NULL, vp);
3008 static JSBool
3009 str_small(JSContext *cx, uintN argc, Value *vp)
3011 return tagify(cx, "small", NULL, NULL, vp);
3014 static JSBool
3015 str_big(JSContext *cx, uintN argc, Value *vp)
3017 return tagify(cx, "big", NULL, NULL, vp);
3020 static JSBool
3021 str_blink(JSContext *cx, uintN argc, Value *vp)
3023 return tagify(cx, "blink", NULL, NULL, vp);
3026 static JSBool
3027 str_sup(JSContext *cx, uintN argc, Value *vp)
3029 return tagify(cx, "sup", NULL, NULL, vp);
3032 static JSBool
3033 str_sub(JSContext *cx, uintN argc, Value *vp)
3035 return tagify(cx, "sub", NULL, NULL, vp);
3037 #endif /* JS_HAS_STR_HTML_HELPERS */
3039 #ifdef JS_TRACER
3040 JSString* FASTCALL
3041 js_String_getelem(JSContext* cx, JSString* str, int32 i)
3043 if ((size_t)i >= str->length())
3044 return NULL;
3045 return JSString::getUnitString(cx, str, size_t(i));
3047 #endif
3049 JS_DEFINE_TRCINFO_1(str_concat,
3050 (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING,
3051 1, nanojit::ACCSET_NONE)))
3053 static JSFunctionSpec string_methods[] = {
3054 #if JS_HAS_TOSOURCE
3055 JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE),
3056 JS_FN(js_toSource_str, str_toSource, 0,0),
3057 #endif
3059 /* Java-like methods. */
3060 JS_FN(js_toString_str, js_str_toString, 0,0),
3061 JS_FN(js_valueOf_str, js_str_toString, 0,0),
3062 JS_FN(js_toJSON_str, js_str_toString, 0,0),
3063 JS_FN("substring", str_substring, 2,JSFUN_GENERIC_NATIVE),
3064 JS_FN("toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE),
3065 JS_FN("toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE),
3066 JS_FN("charAt", js_str_charAt, 1,JSFUN_GENERIC_NATIVE),
3067 JS_FN("charCodeAt", js_str_charCodeAt, 1,JSFUN_GENERIC_NATIVE),
3068 JS_FN("indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE),
3069 JS_FN("lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE),
3070 JS_FN("trim", str_trim, 0,JSFUN_GENERIC_NATIVE),
3071 JS_FN("trimLeft", str_trimLeft, 0,JSFUN_GENERIC_NATIVE),
3072 JS_FN("trimRight", str_trimRight, 0,JSFUN_GENERIC_NATIVE),
3073 JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
3074 JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE),
3075 JS_FN("localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE),
3077 /* Perl-ish methods (search is actually Python-esque). */
3078 JS_FN("match", str_match, 1,JSFUN_GENERIC_NATIVE),
3079 JS_FN("search", str_search, 1,JSFUN_GENERIC_NATIVE),
3080 JS_FN("replace", str_replace, 2,JSFUN_GENERIC_NATIVE),
3081 JS_FN("split", str_split, 2,JSFUN_GENERIC_NATIVE),
3082 #if JS_HAS_PERL_SUBSTR
3083 JS_FN("substr", str_substr, 2,JSFUN_GENERIC_NATIVE),
3084 #endif
3086 /* Python-esque sequence methods. */
3087 JS_TN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE, &str_concat_trcinfo),
3088 JS_FN("slice", str_slice, 2,JSFUN_GENERIC_NATIVE),
3090 /* HTML string methods. */
3091 #if JS_HAS_STR_HTML_HELPERS
3092 JS_FN("bold", str_bold, 0,0),
3093 JS_FN("italics", str_italics, 0,0),
3094 JS_FN("fixed", str_fixed, 0,0),
3095 JS_FN("fontsize", str_fontsize, 1,0),
3096 JS_FN("fontcolor", str_fontcolor, 1,0),
3097 JS_FN("link", str_link, 1,0),
3098 JS_FN("anchor", str_anchor, 1,0),
3099 JS_FN("strike", str_strike, 0,0),
3100 JS_FN("small", str_small, 0,0),
3101 JS_FN("big", str_big, 0,0),
3102 JS_FN("blink", str_blink, 0,0),
3103 JS_FN("sup", str_sup, 0,0),
3104 JS_FN("sub", str_sub, 0,0),
3105 #endif
3107 JS_FS_END
3111 * Set up some tools to make it easier to generate large tables. After constant
3112 * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
3113 * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
3114 * To use this, define R appropriately, then use Rn(0) (for some value of n), then
3115 * undefine R.
3117 #define R2(n) R(n), R((n) + (1 << 0)), R((n) + (2 << 0)), R((n) + (3 << 0))
3118 #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
3119 #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
3120 #define R8(n) R6(n), R6((n) + (1 << 6)), R6((n) + (2 << 6)), R6((n) + (3 << 6))
3121 #define R10(n) R8(n), R8((n) + (1 << 8)), R8((n) + (2 << 8)), R8((n) + (3 << 8))
3122 #define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10))
3124 #define R3(n) R2(n), R2((n) + (1 << 2))
3125 #define R7(n) R6(n), R6((n) + (1 << 6))
3127 #define BUILD_LENGTH_AND_FLAGS(length, flags) \
3128 (((length) << JSString::LENGTH_SHIFT) | (flags))
3131 * Declare unit strings. Pack the string data itself into the mInlineChars
3132 * place in the header.
3134 #define R(c) { \
3135 BUILD_LENGTH_AND_FLAGS(1, JSString::FLAT | JSString::ATOMIZED), \
3136 { (jschar *)(((char *)(unitStringTable + (c))) + \
3137 offsetof(JSString, inlineStorage)) }, \
3138 { {(c), 0x00} } }
3140 #ifdef __SUNPRO_CC
3141 #pragma pack(8)
3142 #else
3143 #pragma pack(push, 8)
3144 #endif
3146 const JSString JSString::unitStringTable[]
3147 #ifdef __GNUC__
3148 __attribute__ ((aligned (8)))
3149 #endif
3150 = { R8(0) };
3152 #ifdef __SUNPRO_CC
3153 #pragma pack(0)
3154 #else
3155 #pragma pack(pop)
3156 #endif
3158 #undef R
3161 * Declare length-2 strings. We only store strings where both characters are
3162 * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
3163 * the lowercase letters, and the next 26 are the uppercase letters.
3165 #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
3166 (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 : \
3167 (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 : \
3168 JSString::INVALID_SMALL_CHAR)
3170 #define R TO_SMALL_CHAR
3172 const JSString::SmallChar JSString::toSmallChar[] = { R7(0) };
3174 #undef R
3177 * This is used when we generate our table of short strings, so the compiler is
3178 * happier if we use |c| as few times as possible.
3180 #define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' : \
3181 (c) < 36 ? 'a' - 10 : \
3182 'A' - 36))
3183 #define R FROM_SMALL_CHAR
3185 const jschar JSString::fromSmallChar[] = { R6(0) };
3187 #undef R
3190 * For code-generation ease, length-2 strings are encoded as 12-bit int values,
3191 * where the upper 6 bits is the first character and the lower 6 bits is the
3192 * second character.
3194 #define R(c) { \
3195 BUILD_LENGTH_AND_FLAGS(2, JSString::FLAT | JSString::ATOMIZED), \
3196 { (jschar *)(((char *)(length2StringTable + (c))) + \
3197 offsetof(JSString, inlineStorage)) }, \
3198 { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
3200 #ifdef __SUNPRO_CC
3201 #pragma pack(8)
3202 #else
3203 #pragma pack(push, 8)
3204 #endif
3206 const JSString JSString::length2StringTable[]
3207 #ifdef __GNUC__
3208 __attribute__ ((aligned (8)))
3209 #endif
3210 = { R12(0) };
3212 #ifdef __SUNPRO_CC
3213 #pragma pack(0)
3214 #else
3215 #pragma pack(pop)
3216 #endif
3218 #undef R
3221 * Declare int strings. Only int strings from 100 to 255 actually have to be
3222 * generated, since the rest are either unit strings or length-2 strings. To
3223 * avoid the runtime cost of figuring out where to look for the string for a
3224 * particular integer, we precompute a table of JSString*s which refer to the
3225 * correct location of the int string.
3227 #define R(c) { \
3228 BUILD_LENGTH_AND_FLAGS(3, JSString::FLAT | JSString::ATOMIZED), \
3229 { (jschar *)(((char *)(hundredStringTable + ((c) - 100))) + \
3230 offsetof(JSString, inlineStorage)) }, \
3231 { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
3234 JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256);
3236 #ifdef __SUNPRO_CC
3237 #pragma pack(8)
3238 #else
3239 #pragma pack(push, 8)
3240 #endif
3242 const JSString JSString::hundredStringTable[]
3243 #ifdef __GNUC__
3244 __attribute__ ((aligned (8)))
3245 #endif
3246 = { R7(100), /* 100 through 227 */
3247 R4(100 + (1 << 7)), /* 228 through 243 */
3248 R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
3249 R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
3252 #undef R
3254 #define R(c) ((c) < 10 ? JSString::unitStringTable + ((c) + '0') : \
3255 (c) < 100 ? JSString::length2StringTable + \
3256 ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) + \
3257 TO_SMALL_CHAR(((c) % 10) + '0') : \
3258 JSString::hundredStringTable + ((c) - 100))
3260 const JSString *const JSString::intStringTable[] = { R8(0) };
3262 #undef R
3264 #ifdef __SUNPRO_CC
3265 #pragma pack(0)
3266 #else
3267 #pragma pack(pop)
3268 #endif
3270 #undef R2
3271 #undef R4
3272 #undef R6
3273 #undef R8
3274 #undef R10
3275 #undef R12
3277 #undef R3
3278 #undef R7
3280 JSBool
3281 js_String(JSContext *cx, uintN argc, Value *vp)
3283 Value *argv = vp + 2;
3285 JSString *str;
3286 if (argc > 0) {
3287 str = js_ValueToString(cx, argv[0]);
3288 if (!str)
3289 return false;
3290 } else {
3291 str = cx->runtime->emptyString;
3294 if (IsConstructing(vp)) {
3295 JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass);
3296 if (!obj)
3297 return false;
3298 obj->setPrimitiveThis(StringValue(str));
3299 vp->setObject(*obj);
3300 } else {
3301 vp->setString(str);
3303 return true;
3306 static JSBool
3307 str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
3309 Value *argv;
3310 uintN i;
3311 jschar *chars;
3312 JSString *str;
3314 argv = vp + 2;
3315 JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
3316 if (argc == 1) {
3317 uint16_t code;
3318 if (!ValueToUint16(cx, argv[0], &code))
3319 return JS_FALSE;
3320 if (code < UNIT_STRING_LIMIT) {
3321 str = JSString::unitString(code);
3322 if (!str)
3323 return JS_FALSE;
3324 vp->setString(str);
3325 return JS_TRUE;
3327 argv[0].setInt32(code);
3329 chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar));
3330 if (!chars)
3331 return JS_FALSE;
3332 for (i = 0; i < argc; i++) {
3333 uint16_t code;
3334 if (!ValueToUint16(cx, argv[i], &code)) {
3335 cx->free(chars);
3336 return JS_FALSE;
3338 chars[i] = (jschar)code;
3340 chars[i] = 0;
3341 str = js_NewString(cx, chars, argc);
3342 if (!str) {
3343 cx->free(chars);
3344 return JS_FALSE;
3346 vp->setString(str);
3347 return JS_TRUE;
3350 #ifdef JS_TRACER
3351 static JSString* FASTCALL
3352 String_fromCharCode(JSContext* cx, int32 i)
3354 JS_ASSERT(JS_ON_TRACE(cx));
3355 jschar c = (jschar)i;
3356 if (c < UNIT_STRING_LIMIT)
3357 return JSString::unitString(c);
3358 return js_NewStringCopyN(cx, &c, 1);
3360 #endif
3362 JS_DEFINE_TRCINFO_1(str_fromCharCode,
3363 (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE)))
3365 static JSFunctionSpec string_static_methods[] = {
3366 JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo),
3367 JS_FS_END
3370 JSObject *
3371 js_InitStringClass(JSContext *cx, JSObject *obj)
3373 JSObject *proto;
3375 /* Define the escape, unescape functions in the global object. */
3376 if (!JS_DefineFunctions(cx, obj, string_functions))
3377 return NULL;
3379 proto = js_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
3380 NULL, string_methods,
3381 NULL, string_static_methods);
3382 if (!proto)
3383 return NULL;
3384 proto->setPrimitiveThis(StringValue(cx->runtime->emptyString));
3385 if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
3386 UndefinedValue(), NULL, NULL,
3387 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
3388 NULL)) {
3389 return JS_FALSE;
3392 return proto;
3395 JSFlatString *
3396 js_NewString(JSContext *cx, jschar *chars, size_t length)
3398 JSString *str;
3400 if (length > JSString::MAX_LENGTH) {
3401 if (JS_ON_TRACE(cx)) {
3403 * If we can't leave the trace, signal OOM condition, otherwise
3404 * exit from trace before throwing.
3406 if (!CanLeaveTrace(cx))
3407 return NULL;
3409 LeaveTrace(cx);
3411 js_ReportAllocationOverflow(cx);
3412 return NULL;
3415 str = js_NewGCString(cx);
3416 if (!str)
3417 return NULL;
3418 str->initFlat(chars, length);
3419 cx->runtime->stringMemoryUsed += length * 2;
3420 #ifdef DEBUG
3422 JSRuntime *rt = cx->runtime;
3423 JS_RUNTIME_METER(rt, liveStrings);
3424 JS_RUNTIME_METER(rt, totalStrings);
3425 JS_LOCK_RUNTIME_VOID(rt,
3426 (rt->lengthSum += (double)length,
3427 rt->lengthSquaredSum += (double)length * (double)length));
3429 #endif
3430 return str->assertIsFlat();
3433 static JS_ALWAYS_INLINE JSFlatString *
3434 NewShortString(JSContext *cx, const jschar *chars, size_t length)
3436 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3437 JSShortString *str = js_NewGCShortString(cx);
3438 if (!str)
3439 return NULL;
3440 jschar *storage = str->init(length);
3441 js_short_strncpy(storage, chars, length);
3442 storage[length] = 0;
3443 return str->header()->assertIsFlat();
3446 static JSFlatString *
3447 NewShortString(JSContext *cx, const char *chars, size_t length)
3449 JS_ASSERT(JSShortString::fitsIntoShortString(length));
3450 JSShortString *str = js_NewGCShortString(cx);
3451 if (!str)
3452 return NULL;
3453 jschar *storage = str->init(length);
3455 if (js_CStringsAreUTF8) {
3456 #ifdef DEBUG
3457 size_t oldLength = length;
3458 #endif
3459 if (!js_InflateStringToBuffer(cx, chars, length, storage, &length))
3460 return NULL;
3461 JS_ASSERT(length <= oldLength);
3462 storage[length] = 0;
3463 str->resetLength(length);
3464 } else {
3465 size_t n = length;
3466 jschar *p = storage;
3467 while (n--)
3468 *p++ = (unsigned char)*chars++;
3469 *p = 0;
3471 return str->header()->assertIsFlat();
3474 static const size_t sMinWasteSize = 16;
3476 JSFlatString *
3477 StringBuffer::finishString()
3479 JSContext *cx = context();
3480 if (cb.empty())
3481 return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
3483 size_t length = cb.length();
3484 if (!checkLength(length))
3485 return NULL;
3487 JS_STATIC_ASSERT(JSShortString::MAX_SHORT_STRING_LENGTH < CharBuffer::InlineLength);
3488 if (JSShortString::fitsIntoShortString(length))
3489 return NewShortString(cx, cb.begin(), length);
3491 if (!cb.append('\0'))
3492 return NULL;
3494 size_t capacity = cb.capacity();
3496 jschar *buf = cb.extractRawBuffer();
3497 if (!buf)
3498 return NULL;
3500 /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
3501 JS_ASSERT(capacity >= length);
3502 if (capacity > sMinWasteSize && capacity - length > (length >> 2)) {
3503 size_t bytes = sizeof(jschar) * (length + 1);
3504 jschar *tmp = (jschar *)cx->realloc(buf, bytes);
3505 if (!tmp) {
3506 cx->free(buf);
3507 return NULL;
3509 buf = tmp;
3512 JSFlatString *str = js_NewString(cx, buf, length);
3513 if (!str)
3514 cx->free(buf);
3515 return str;
3518 JSLinearString *
3519 js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start,
3520 size_t length)
3522 JSString *ds;
3524 if (length == 0)
3525 return cx->runtime->emptyString;
3527 JSLinearString *base = baseArg->ensureLinear(cx);
3528 if (!base)
3529 return NULL;
3531 if (start == 0 && length == base->length())
3532 return base;
3534 const jschar *chars = base->chars() + start;
3536 JSLinearString *staticStr = JSString::lookupStaticString(chars, length);
3537 if (staticStr)
3538 return staticStr;
3540 /* Try to avoid long chains of dependent strings. */
3541 while (base->isDependent())
3542 base = base->dependentBase();
3544 JS_ASSERT(base->isFlat());
3546 ds = js_NewGCString(cx);
3547 if (!ds)
3548 return NULL;
3549 ds->initDependent(base, chars, length);
3550 #ifdef DEBUG
3552 JSRuntime *rt = cx->runtime;
3553 JS_RUNTIME_METER(rt, liveDependentStrings);
3554 JS_RUNTIME_METER(rt, totalDependentStrings);
3555 JS_RUNTIME_METER(rt, liveStrings);
3556 JS_RUNTIME_METER(rt, totalStrings);
3557 JS_LOCK_RUNTIME_VOID(rt,
3558 (rt->strdepLengthSum += (double)length,
3559 rt->strdepLengthSquaredSum += (double)length * (double)length));
3560 JS_LOCK_RUNTIME_VOID(rt,
3561 (rt->lengthSum += (double)length,
3562 rt->lengthSquaredSum += (double)length * (double)length));
3564 #endif
3565 return ds->assertIsLinear();
3568 #ifdef DEBUG
3569 #include <math.h>
3571 void printJSStringStats(JSRuntime *rt)
3573 double mean, sigma;
3575 mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum,
3576 rt->lengthSquaredSum, &sigma);
3578 fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
3579 (unsigned long)rt->totalStrings, mean, sigma);
3581 mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum,
3582 rt->strdepLengthSquaredSum, &sigma);
3584 fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n",
3585 (unsigned long)rt->totalDependentStrings, mean, sigma);
3587 #endif
3589 JSFlatString *
3590 js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
3592 if (JSShortString::fitsIntoShortString(n))
3593 return NewShortString(cx, s, n);
3595 jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
3596 if (!news)
3597 return NULL;
3598 js_strncpy(news, s, n);
3599 news[n] = 0;
3600 JSFlatString *str = js_NewString(cx, news, n);
3601 if (!str)
3602 cx->free(news);
3603 return str;
3606 JSFlatString *
3607 js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
3609 if (JSShortString::fitsIntoShortString(n))
3610 return NewShortString(cx, s, n);
3612 jschar *chars = js_InflateString(cx, s, &n);
3613 if (!chars)
3614 return NULL;
3615 JSFlatString *str = js_NewString(cx, chars, n);
3616 if (!str)
3617 cx->free(chars);
3618 return str;
3621 JSFlatString *
3622 js_NewStringCopyZ(JSContext *cx, const jschar *s)
3624 size_t n = js_strlen(s);
3625 if (JSShortString::fitsIntoShortString(n))
3626 return NewShortString(cx, s, n);
3628 size_t m = (n + 1) * sizeof(jschar);
3629 jschar *news = (jschar *) cx->malloc(m);
3630 if (!news)
3631 return NULL;
3632 memcpy(news, s, m);
3633 JSFlatString *str = js_NewString(cx, news, n);
3634 if (!str)
3635 cx->free(news);
3636 return str;
3639 JSFlatString *
3640 js_NewStringCopyZ(JSContext *cx, const char *s)
3642 return js_NewStringCopyN(cx, s, strlen(s));
3645 const char *
3646 js_ValueToPrintable(JSContext *cx, const Value &v, JSAutoByteString *bytes, bool asSource)
3648 JSString *str;
3650 str = (asSource ? js_ValueToSource : js_ValueToString)(cx, v);
3651 if (!str)
3652 return NULL;
3653 str = js_QuoteString(cx, str, 0);
3654 if (!str)
3655 return NULL;
3656 return bytes->encode(cx, str);
3659 JSString *
3660 js_ValueToString(JSContext *cx, const Value &arg)
3662 Value v = arg;
3663 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3664 return NULL;
3666 JSString *str;
3667 if (v.isString()) {
3668 str = v.toString();
3669 } else if (v.isInt32()) {
3670 str = js_IntToString(cx, v.toInt32());
3671 } else if (v.isDouble()) {
3672 str = js_NumberToString(cx, v.toDouble());
3673 } else if (v.isBoolean()) {
3674 str = js_BooleanToString(cx, v.toBoolean());
3675 } else if (v.isNull()) {
3676 str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3677 } else {
3678 str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3680 return str;
3683 /* This function implements E-262-3 section 9.8, toString. */
3684 bool
3685 js::ValueToStringBuffer(JSContext *cx, const Value &arg, StringBuffer &sb)
3687 Value v = arg;
3688 if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v))
3689 return false;
3691 if (v.isString()) {
3692 JSString *str = v.toString();
3693 size_t length = str->length();
3694 const jschar *chars = str->getChars(cx);
3695 if (!chars)
3696 return false;
3697 return sb.append(chars, length);
3699 if (v.isNumber())
3700 return NumberValueToStringBuffer(cx, v, sb);
3701 if (v.isBoolean())
3702 return BooleanToStringBuffer(cx, v.toBoolean(), sb);
3703 if (v.isNull())
3704 return sb.append(cx->runtime->atomState.nullAtom);
3705 JS_ASSERT(v.isUndefined());
3706 return sb.append(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
3709 JS_FRIEND_API(JSString *)
3710 js_ValueToSource(JSContext *cx, const Value &v)
3712 if (v.isUndefined())
3713 return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
3714 if (v.isString())
3715 return js_QuoteString(cx, v.toString(), '"');
3716 if (v.isPrimitive()) {
3717 /* Special case to preserve negative zero, _contra_ toString. */
3718 if (v.isDouble() && JSDOUBLE_IS_NEGZERO(v.toDouble())) {
3719 /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */
3720 static const jschar js_negzero_ucNstr[] = {'-', '0'};
3722 return js_NewStringCopyN(cx, js_negzero_ucNstr, 2);
3724 return js_ValueToString(cx, v);
3727 JSAtom *atom = cx->runtime->atomState.toSourceAtom;
3728 AutoValueRooter tvr(cx);
3729 if (!js_TryMethod(cx, &v.toObject(), atom, 0, NULL, tvr.addr()))
3730 return NULL;
3731 return js_ValueToString(cx, tvr.value());
3734 namespace js {
3737 * str is not necessarily a GC thing here.
3739 static JS_ALWAYS_INLINE bool
3740 EqualStringsTail(JSLinearString *str1, size_t length1, JSLinearString *str2)
3742 const jschar *s1 = str1->chars();
3743 const jschar *s1end = s1 + length1;
3744 const jschar *s2 = str2->chars();
3745 do {
3746 if (*s1 != *s2) {
3747 return false;
3749 ++s1, ++s2;
3750 } while (s1 != s1end);
3752 return true;
3755 bool
3756 EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result)
3758 if (str1 == str2) {
3759 *result = true;
3760 return true;
3763 size_t length1 = str1->length();
3764 if (length1 != str2->length()) {
3765 *result = false;
3766 return true;
3769 if (length1 == 0) {
3770 *result = true;
3771 return true;
3774 JSLinearString *linear1 = str1->ensureLinear(cx);
3775 if (!linear1)
3776 return false;
3777 JSLinearString *linear2 = str2->ensureLinear(cx);
3778 if (!linear2)
3779 return false;
3781 *result = EqualStringsTail(linear1, length1, linear2);
3782 return true;
3785 bool
3786 EqualStrings(JSLinearString *str1, JSLinearString *str2)
3788 if (str1 == str2)
3789 return true;
3791 size_t length1 = str1->length();
3792 if (length1 != str2->length())
3793 return false;
3795 if (length1 == 0)
3796 return true;
3798 return EqualStringsTail(str1, length1, str2);
3801 } /* namespace js */
3803 JSBool JS_FASTCALL
3804 js_EqualStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2)
3806 JSBool result;
3807 return EqualStrings(cx, str1, str2, &result) ? result : JS_NEITHER;
3809 JS_DEFINE_CALLINFO_3(extern, BOOL, js_EqualStringsOnTrace,
3810 CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE)
3812 namespace js {
3814 static bool
3815 CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
3817 JS_ASSERT(str1);
3818 JS_ASSERT(str2);
3820 if (str1 == str2) {
3821 *result = 0;
3822 return true;
3825 size_t l1 = str1->length();
3826 const jschar *s1 = str1->getChars(cx);
3827 if (!s1)
3828 return false;
3830 size_t l2 = str2->length();
3831 const jschar *s2 = str2->getChars(cx);
3832 if (!s2)
3833 return false;
3835 size_t n = JS_MIN(l1, l2);
3836 for (size_t i = 0; i < n; i++) {
3837 if (int32 cmp = s1[i] - s2[i]) {
3838 *result = cmp;
3839 return true;
3842 *result = (int32)(l1 - l2);
3843 return true;
3846 bool
3847 CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
3849 return CompareStringsImpl(cx, str1, str2, result);
3852 } /* namespace js */
3854 int32 JS_FASTCALL
3855 js_CompareStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2)
3857 int32 result;
3858 if (!CompareStringsImpl(cx, str1, str2, &result))
3859 return INT32_MIN;
3860 JS_ASSERT(result != INT32_MIN);
3861 return result;
3863 JS_DEFINE_CALLINFO_3(extern, INT32, js_CompareStringsOnTrace,
3864 CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE)
3866 namespace js {
3868 bool
3869 StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
3871 size_t length = strlen(asciiBytes);
3872 #ifdef DEBUG
3873 for (size_t i = 0; i != length; ++i)
3874 JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
3875 #endif
3876 if (length != str->length())
3877 return false;
3878 const jschar *chars = str->chars();
3879 for (size_t i = 0; i != length; ++i) {
3880 if (unsigned(asciiBytes[i]) != unsigned(chars[i]))
3881 return false;
3883 return true;
3886 } /* namespacejs */
3888 size_t
3889 js_strlen(const jschar *s)
3891 const jschar *t;
3893 for (t = s; *t != 0; t++)
3894 continue;
3895 return (size_t)(t - s);
3898 jschar *
3899 js_strchr(const jschar *s, jschar c)
3901 while (*s != 0) {
3902 if (*s == c)
3903 return (jschar *)s;
3904 s++;
3906 return NULL;
3909 jschar *
3910 js_strchr_limit(const jschar *s, jschar c, const jschar *limit)
3912 while (s < limit) {
3913 if (*s == c)
3914 return (jschar *)s;
3915 s++;
3917 return NULL;
3920 jschar *
3921 js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp)
3923 size_t nbytes, nchars, i;
3924 jschar *chars;
3925 #ifdef DEBUG
3926 JSBool ok;
3927 #endif
3929 nbytes = *lengthp;
3930 if (js_CStringsAreUTF8) {
3931 if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars))
3932 goto bad;
3933 chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar));
3934 if (!chars)
3935 goto bad;
3936 #ifdef DEBUG
3937 ok =
3938 #endif
3939 js_InflateStringToBuffer(cx, bytes, nbytes, chars, &nchars);
3940 JS_ASSERT(ok);
3941 } else {
3942 nchars = nbytes;
3943 chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
3944 if (!chars)
3945 goto bad;
3946 for (i = 0; i < nchars; i++)
3947 chars[i] = (unsigned char) bytes[i];
3949 *lengthp = nchars;
3950 chars[nchars] = 0;
3951 return chars;
3953 bad:
3955 * For compatibility with callers of JS_DecodeBytes we must zero lengthp
3956 * on errors.
3958 *lengthp = 0;
3959 return NULL;
3963 * May be called with null cx.
3965 char *
3966 js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars)
3968 size_t nbytes, i;
3969 char *bytes;
3970 #ifdef DEBUG
3971 JSBool ok;
3972 #endif
3974 if (js_CStringsAreUTF8) {
3975 nbytes = js_GetDeflatedStringLength(cx, chars, nchars);
3976 if (nbytes == (size_t) -1)
3977 return NULL;
3978 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3979 if (!bytes)
3980 return NULL;
3981 #ifdef DEBUG
3982 ok =
3983 #endif
3984 js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes);
3985 JS_ASSERT(ok);
3986 } else {
3987 nbytes = nchars;
3988 bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1));
3989 if (!bytes)
3990 return NULL;
3991 for (i = 0; i < nbytes; i++)
3992 bytes[i] = (char) chars[i];
3994 bytes[nbytes] = 0;
3995 return bytes;
3998 size_t
3999 js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars)
4001 if (!js_CStringsAreUTF8)
4002 return nchars;
4004 return js_GetDeflatedUTF8StringLength(cx, chars, nchars);
4008 * May be called with null cx through public API, see below.
4010 size_t
4011 js_GetDeflatedUTF8StringLength(JSContext *cx, const jschar *chars, size_t nchars)
4013 size_t nbytes;
4014 const jschar *end;
4015 uintN c, c2;
4016 char buffer[10];
4018 nbytes = nchars;
4019 for (end = chars + nchars; chars != end; chars++) {
4020 c = *chars;
4021 if (c < 0x80)
4022 continue;
4023 if (0xD800 <= c && c <= 0xDFFF) {
4024 /* Surrogate pair. */
4025 chars++;
4027 /* nbytes sets 1 length since this is surrogate pair. */
4028 nbytes--;
4029 if (c >= 0xDC00 || chars == end)
4030 goto bad_surrogate;
4031 c2 = *chars;
4032 if (c2 < 0xDC00 || c2 > 0xDFFF)
4033 goto bad_surrogate;
4034 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4036 c >>= 11;
4037 nbytes++;
4038 while (c) {
4039 c >>= 5;
4040 nbytes++;
4043 return nbytes;
4045 bad_surrogate:
4046 if (cx) {
4047 JS_snprintf(buffer, 10, "0x%x", c);
4048 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
4049 NULL, JSMSG_BAD_SURROGATE_CHAR, buffer);
4051 return (size_t) -1;
4054 JSBool
4055 js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen,
4056 char *dst, size_t *dstlenp)
4058 size_t dstlen, i;
4060 dstlen = *dstlenp;
4061 if (!js_CStringsAreUTF8) {
4062 if (srclen > dstlen) {
4063 for (i = 0; i < dstlen; i++)
4064 dst[i] = (char) src[i];
4065 if (cx) {
4066 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4067 JSMSG_BUFFER_TOO_SMALL);
4069 return JS_FALSE;
4071 for (i = 0; i < srclen; i++)
4072 dst[i] = (char) src[i];
4073 *dstlenp = srclen;
4074 return JS_TRUE;
4077 return js_DeflateStringToUTF8Buffer(cx, src, srclen, dst, dstlenp);
4080 JSBool
4081 js_DeflateStringToUTF8Buffer(JSContext *cx, const jschar *src, size_t srclen,
4082 char *dst, size_t *dstlenp)
4084 size_t dstlen, i, origDstlen, utf8Len;
4085 jschar c, c2;
4086 uint32 v;
4087 uint8 utf8buf[6];
4089 dstlen = *dstlenp;
4090 origDstlen = dstlen;
4091 while (srclen) {
4092 c = *src++;
4093 srclen--;
4094 if ((c >= 0xDC00) && (c <= 0xDFFF))
4095 goto badSurrogate;
4096 if (c < 0xD800 || c > 0xDBFF) {
4097 v = c;
4098 } else {
4099 if (srclen < 1)
4100 goto badSurrogate;
4101 c2 = *src;
4102 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
4103 goto badSurrogate;
4104 src++;
4105 srclen--;
4106 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
4108 if (v < 0x0080) {
4109 /* no encoding necessary - performance hack */
4110 if (dstlen == 0)
4111 goto bufferTooSmall;
4112 *dst++ = (char) v;
4113 utf8Len = 1;
4114 } else {
4115 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
4116 if (utf8Len > dstlen)
4117 goto bufferTooSmall;
4118 for (i = 0; i < utf8Len; i++)
4119 *dst++ = (char) utf8buf[i];
4121 dstlen -= utf8Len;
4123 *dstlenp = (origDstlen - dstlen);
4124 return JS_TRUE;
4126 badSurrogate:
4127 *dstlenp = (origDstlen - dstlen);
4128 /* Delegate error reporting to the measurement function. */
4129 if (cx)
4130 js_GetDeflatedStringLength(cx, src - 1, srclen + 1);
4131 return JS_FALSE;
4133 bufferTooSmall:
4134 *dstlenp = (origDstlen - dstlen);
4135 if (cx) {
4136 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4137 JSMSG_BUFFER_TOO_SMALL);
4139 return JS_FALSE;
4142 JSBool
4143 js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen,
4144 jschar *dst, size_t *dstlenp)
4146 size_t dstlen, i;
4148 if (!js_CStringsAreUTF8) {
4149 if (dst) {
4150 dstlen = *dstlenp;
4151 if (srclen > dstlen) {
4152 for (i = 0; i < dstlen; i++)
4153 dst[i] = (unsigned char) src[i];
4154 if (cx) {
4155 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4156 JSMSG_BUFFER_TOO_SMALL);
4158 return JS_FALSE;
4160 for (i = 0; i < srclen; i++)
4161 dst[i] = (unsigned char) src[i];
4163 *dstlenp = srclen;
4164 return JS_TRUE;
4167 return js_InflateUTF8StringToBuffer(cx, src, srclen, dst, dstlenp);
4170 JSBool
4171 js_InflateUTF8StringToBuffer(JSContext *cx, const char *src, size_t srclen,
4172 jschar *dst, size_t *dstlenp)
4174 size_t dstlen, origDstlen, offset, j, n;
4175 uint32 v;
4177 dstlen = dst ? *dstlenp : (size_t) -1;
4178 origDstlen = dstlen;
4179 offset = 0;
4181 while (srclen) {
4182 v = (uint8) *src;
4183 n = 1;
4184 if (v & 0x80) {
4185 while (v & (0x80 >> n))
4186 n++;
4187 if (n > srclen)
4188 goto bufferTooSmall;
4189 if (n == 1 || n > 4)
4190 goto badCharacter;
4191 for (j = 1; j < n; j++) {
4192 if ((src[j] & 0xC0) != 0x80)
4193 goto badCharacter;
4195 v = Utf8ToOneUcs4Char((uint8 *)src, n);
4196 if (v >= 0x10000) {
4197 v -= 0x10000;
4198 if (v > 0xFFFFF || dstlen < 2) {
4199 *dstlenp = (origDstlen - dstlen);
4200 if (cx) {
4201 char buffer[10];
4202 JS_snprintf(buffer, 10, "0x%x", v + 0x10000);
4203 JS_ReportErrorFlagsAndNumber(cx,
4204 JSREPORT_ERROR,
4205 js_GetErrorMessage, NULL,
4206 JSMSG_UTF8_CHAR_TOO_LARGE,
4207 buffer);
4209 return JS_FALSE;
4211 if (dst) {
4212 *dst++ = (jschar)((v >> 10) + 0xD800);
4213 v = (jschar)((v & 0x3FF) + 0xDC00);
4215 dstlen--;
4218 if (!dstlen)
4219 goto bufferTooSmall;
4220 if (dst)
4221 *dst++ = (jschar) v;
4222 dstlen--;
4223 offset += n;
4224 src += n;
4225 srclen -= n;
4227 *dstlenp = (origDstlen - dstlen);
4228 return JS_TRUE;
4230 badCharacter:
4231 *dstlenp = (origDstlen - dstlen);
4232 if (cx) {
4233 char buffer[10];
4234 JS_snprintf(buffer, 10, "%d", offset);
4235 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
4236 js_GetErrorMessage, NULL,
4237 JSMSG_MALFORMED_UTF8_CHAR,
4238 buffer);
4240 return JS_FALSE;
4242 bufferTooSmall:
4243 *dstlenp = (origDstlen - dstlen);
4244 if (cx) {
4245 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4246 JSMSG_BUFFER_TOO_SMALL);
4248 return JS_FALSE;
4252 * From java.lang.Character.java:
4254 * The character properties are currently encoded into 32 bits in the
4255 * following manner:
4257 * 10 bits signed offset used for converting case
4258 * 1 bit if 1, adding the signed offset converts the character to
4259 * lowercase
4260 * 1 bit if 1, subtracting the signed offset converts the character to
4261 * uppercase
4262 * 1 bit if 1, character has a titlecase equivalent (possibly itself)
4263 * 3 bits 0 may not be part of an identifier
4264 * 1 ignorable control; may continue a Unicode identifier or JS
4265 * identifier
4266 * 2 may continue a JS identifier but not a Unicode identifier
4267 * (unused)
4268 * 3 may continue a Unicode identifier or JS identifier
4269 * 4 is a JS whitespace character
4270 * 5 may start or continue a JS identifier;
4271 * may continue but not start a Unicode identifier (_)
4272 * 6 may start or continue a JS identifier but not a Unicode
4273 * identifier ($)
4274 * 7 may start or continue a Unicode identifier or JS identifier
4275 * Thus:
4276 * 5, 6, 7 may start a JS identifier
4277 * 1, 2, 3, 5, 6, 7 may continue a JS identifier
4278 * 7 may start a Unicode identifier
4279 * 1, 3, 5, 7 may continue a Unicode identifier
4280 * 1 is ignorable within an identifier
4281 * 4 is JS whitespace
4282 * 2 bits 0 this character has no numeric property
4283 * 1 adding the digit offset to the character code and then
4284 * masking with 0x1F will produce the desired numeric value
4285 * 2 this character has a "strange" numeric value
4286 * 3 a JS supradecimal digit: adding the digit offset to the
4287 * character code, then masking with 0x1F, then adding 10
4288 * will produce the desired numeric value
4289 * 5 bits digit offset
4290 * 1 bit XML 1.0 name start character
4291 * 1 bit XML 1.0 name character
4292 * 2 bits reserved for future use
4293 * 5 bits character type
4296 /* The X table has 1024 entries for a total of 1024 bytes. */
4298 const uint8 js_X[] = {
4299 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */
4300 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */
4301 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */
4302 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */
4303 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */
4304 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */
4305 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */
4306 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */
4307 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */
4308 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */
4309 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */
4310 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */
4311 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */
4312 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */
4313 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */
4314 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */
4315 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */
4316 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */
4317 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */
4318 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */
4319 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */
4320 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */
4321 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */
4322 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */
4323 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */
4324 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */
4325 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */
4326 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */
4327 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */
4328 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */
4329 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */
4330 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */
4331 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */
4332 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */
4333 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */
4334 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */
4335 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */
4336 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */
4337 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */
4338 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */
4339 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */
4340 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */
4341 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */
4342 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */
4343 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */
4344 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */
4345 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */
4346 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */
4347 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */
4348 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */
4349 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */
4350 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */
4351 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */
4352 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */
4353 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */
4354 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */
4355 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */
4356 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */
4357 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */
4358 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */
4359 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */
4360 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */
4361 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */
4362 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */
4363 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */
4364 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */
4365 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */
4366 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */
4367 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */
4368 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */
4369 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */
4370 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */
4371 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */
4372 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */
4373 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */
4374 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */
4375 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */
4376 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */
4377 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */
4378 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */
4379 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */
4380 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */
4381 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */
4382 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */
4383 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */
4384 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */
4385 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */
4386 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */
4387 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */
4388 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */
4389 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */
4390 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */
4391 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */
4392 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */
4393 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */
4394 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */
4395 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */
4396 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */
4397 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */
4398 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */
4399 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */
4400 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */
4401 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */
4402 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */
4403 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */
4404 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */
4405 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */
4406 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */
4407 104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */
4408 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */
4409 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */
4410 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */
4411 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */
4412 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */
4413 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */
4414 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */
4415 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */
4416 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */
4417 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */
4418 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */
4419 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */
4420 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */
4421 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */
4422 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */
4423 105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */
4424 106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */
4425 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */
4426 115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */
4429 /* The Y table has 7808 entries for a total of 7808 bytes. */
4431 const uint8 js_Y[] = {
4432 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4433 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */
4434 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4435 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
4436 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */
4437 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */
4438 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */
4439 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */
4440 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4441 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4442 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */
4443 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */
4444 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4445 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4446 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */
4447 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */
4448 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4449 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4450 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4451 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
4452 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */
4453 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */
4454 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */
4455 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */
4456 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4457 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */
4458 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */
4459 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */
4460 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4461 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */
4462 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */
4463 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */
4464 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4465 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4466 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4467 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4468 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4469 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */
4470 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */
4471 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */
4472 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */
4473 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */
4474 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4475 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4476 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4477 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4478 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */
4479 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */
4480 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */
4481 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */
4482 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */
4483 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */
4484 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */
4485 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */
4486 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */
4487 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */
4488 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */
4489 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */
4490 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */
4491 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */
4492 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4493 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */
4494 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */
4495 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */
4496 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4497 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4498 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */
4499 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4500 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4501 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4502 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4503 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */
4504 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4505 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */
4506 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */
4507 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */
4508 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */
4509 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */
4510 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */
4511 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */
4512 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */
4513 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */
4514 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */
4515 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4516 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */
4517 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */
4518 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */
4519 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */
4520 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4521 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */
4522 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */
4523 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */
4524 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */
4525 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */
4526 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4527 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */
4528 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4529 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4530 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4531 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4532 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4533 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4534 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4535 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */
4536 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */
4537 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4538 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4539 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4540 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */
4541 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */
4542 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */
4543 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */
4544 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */
4545 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */
4546 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4547 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */
4548 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */
4549 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */
4550 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4551 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */
4552 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */
4553 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */
4554 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */
4555 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */
4556 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */
4557 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */
4558 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */
4559 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */
4560 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */
4561 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */
4562 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4563 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4564 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4565 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */
4566 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4567 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */
4568 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4569 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */
4570 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */
4571 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */
4572 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4573 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4574 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4575 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */
4576 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */
4577 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */
4578 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4579 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4580 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4581 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4582 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4583 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */
4584 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */
4585 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */
4586 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4587 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4588 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */
4589 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */
4590 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */
4591 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */
4592 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4593 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4594 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4595 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4596 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4597 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */
4598 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4599 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */
4600 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4601 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */
4602 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */
4603 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */
4604 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4605 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4606 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4607 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */
4608 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */
4609 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */
4610 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4611 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4612 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */
4613 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4614 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */
4615 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */
4616 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */
4617 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4618 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4619 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4620 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */
4621 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */
4622 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */
4623 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */
4624 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4625 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */
4626 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */
4627 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */
4628 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4629 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4630 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */
4631 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */
4632 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4633 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */
4634 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */
4635 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */
4636 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */
4637 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */
4638 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4639 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */
4640 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4641 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4642 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4643 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4644 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4645 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4646 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */
4647 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */
4648 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */
4649 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */
4650 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */
4651 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */
4652 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */
4653 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */
4654 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */
4655 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */
4656 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4657 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4658 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4659 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4660 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4661 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4662 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4663 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */
4664 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */
4665 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4666 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4667 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4668 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4669 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4670 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */
4671 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */
4672 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */
4673 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */
4674 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */
4675 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */
4676 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */
4677 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */
4678 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4679 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */
4680 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */
4681 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */
4682 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */
4683 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4684 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */
4685 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */
4686 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */
4687 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */
4688 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */
4689 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */
4690 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */
4691 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */
4692 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */
4693 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */
4694 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */
4695 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */
4696 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */
4697 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */
4698 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */
4699 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4700 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */
4701 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */
4702 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */
4703 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */
4704 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */
4705 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */
4706 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4707 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */
4708 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */
4709 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */
4710 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */
4711 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */
4712 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */
4713 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */
4714 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */
4715 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4716 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */
4717 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */
4718 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */
4719 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */
4720 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */
4721 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */
4722 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4723 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4724 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */
4725 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */
4726 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4727 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */
4728 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */
4729 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */
4730 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */
4731 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4732 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */
4733 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */
4734 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */
4735 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */
4736 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */
4737 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */
4738 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */
4739 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */
4740 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */
4741 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */
4742 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4743 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */
4744 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */
4745 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4746 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */
4747 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */
4748 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */
4749 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */
4750 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */
4751 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */
4752 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */
4753 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */
4754 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */
4755 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4756 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */
4757 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */
4758 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */
4759 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */
4760 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */
4761 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */
4762 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4763 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4764 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */
4765 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */
4766 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */
4767 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */
4768 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */
4769 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */
4770 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */
4771 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4772 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */
4773 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */
4774 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4775 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */
4776 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */
4777 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */
4778 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4779 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4780 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */
4781 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */
4782 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */
4783 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */
4784 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */
4785 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */
4786 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */
4787 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */
4788 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */
4789 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */
4790 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4791 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */
4792 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */
4793 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */
4794 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4795 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4796 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4797 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */
4798 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */
4799 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */
4800 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */
4801 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */
4802 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */
4803 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4804 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */
4805 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */
4806 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4807 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */
4808 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4809 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4810 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4811 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4812 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */
4813 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */
4814 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */
4815 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */
4816 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */
4817 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */
4818 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */
4819 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */
4820 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4821 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4822 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4823 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */
4824 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */
4825 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */
4826 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */
4827 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */
4828 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */
4829 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */
4830 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */
4831 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */
4832 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */
4833 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */
4834 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */
4835 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */
4836 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4837 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4838 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4839 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */
4840 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */
4841 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */
4842 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */
4843 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */
4844 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */
4845 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */
4846 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */
4847 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */
4848 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4849 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4850 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4851 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4852 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */
4853 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */
4854 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */
4855 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */
4856 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */
4857 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */
4858 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */
4859 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4860 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4861 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */
4862 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */
4863 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */
4864 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4865 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4866 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4867 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */
4868 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4869 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4870 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4871 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */
4872 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */
4873 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */
4874 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4875 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4876 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4877 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */
4878 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */
4879 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */
4880 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4881 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4882 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4883 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4884 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4885 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4886 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4887 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */
4888 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4889 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4890 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4891 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */
4892 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4893 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4894 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4895 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */
4896 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4897 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4898 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4899 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4900 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */
4901 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4902 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4903 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */
4904 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4905 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4906 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4907 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4908 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4909 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4910 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */
4911 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */
4912 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4913 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4914 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4915 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4916 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4917 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4918 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4919 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */
4920 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4921 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4922 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */
4923 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */
4924 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4925 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4926 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4927 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */
4928 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4929 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4930 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4931 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4932 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4933 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4934 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */
4935 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */
4936 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4937 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4938 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */
4939 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */
4940 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4941 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4942 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */
4943 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */
4944 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */
4945 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */
4946 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */
4947 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */
4948 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */
4949 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */
4950 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */
4951 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */
4952 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4953 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4954 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4955 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4956 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */
4957 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */
4958 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */
4959 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */
4960 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */
4961 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */
4962 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */
4963 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */
4964 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */
4965 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */
4966 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */
4967 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */
4968 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */
4969 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */
4970 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */
4971 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */
4972 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
4973 105, 106, 104, 104, 104, 104, 104, 46, /* 67 */
4974 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */
4975 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */
4976 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */
4977 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4978 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4979 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4980 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */
4981 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */
4982 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */
4983 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */
4984 107, 107, 107, 107, 107, 107, 107, 107, /* 69 */
4985 107, 107, 7, 7, 7, 5, 6, 46, /* 69 */
4986 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4987 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4988 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */
4989 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */
4990 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4991 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */
4992 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4993 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4994 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */
4995 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */
4996 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */
4997 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4998 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
4999 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */
5000 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */
5001 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */
5002 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */
5003 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */
5004 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */
5005 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */
5006 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */
5007 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */
5008 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5009 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */
5010 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */
5011 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */
5012 109, 109, 109, 109, 109, 109, 109, 109, /* 72 */
5013 109, 109, 109, 109, 110, 110, 110, 110, /* 72 */
5014 111, 111, 111, 111, 111, 111, 111, 111, /* 72 */
5015 111, 111, 111, 111, 112, 112, 112, 112, /* 72 */
5016 113, 113, 113, 46, 46, 46, 46, 46, /* 73 */
5017 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */
5018 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */
5019 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5020 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5021 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5022 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5023 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */
5024 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5025 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5026 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */
5027 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5028 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */
5029 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */
5030 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5031 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */
5032 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5033 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5034 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5035 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5036 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5037 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5038 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5039 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */
5040 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5041 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5042 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5043 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5044 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5045 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */
5046 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */
5047 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */
5048 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */
5049 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */
5050 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5051 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5052 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */
5053 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */
5054 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5055 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */
5056 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5057 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5058 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5059 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5060 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5061 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5062 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */
5063 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */
5064 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5065 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5066 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5067 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */
5068 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */
5069 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5070 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5071 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */
5072 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */
5073 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */
5074 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5075 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */
5076 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5077 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */
5078 114, 114, 114, 114, 82, 82, 82, 82, /* 80 */
5079 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */
5080 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */
5081 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5082 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */
5083 115, 115, 115, 115, 15, 15, 15, 15, /* 81 */
5084 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5085 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */
5086 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */
5087 116, 116, 116, 116, 116, 116, 116, 116, /* 81 */
5088 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5089 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */
5090 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5091 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5092 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */
5093 117, 117, 118, 46, 46, 46, 46, 46, /* 82 */
5094 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5095 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */
5096 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5097 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5098 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5099 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5100 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5101 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5102 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5103 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */
5104 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5105 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5106 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */
5107 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */
5108 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5109 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5110 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5111 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */
5112 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5113 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5114 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5115 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5116 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5117 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */
5118 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5119 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */
5120 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5121 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5122 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */
5123 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */
5124 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5125 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5126 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5127 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */
5128 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */
5129 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */
5130 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5131 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5132 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5133 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5134 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5135 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */
5136 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5137 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */
5138 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */
5139 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */
5140 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */
5141 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */
5142 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */
5143 119, 119, 119, 119, 119, 119, 119, 119, /* 88 */
5144 114, 114, 114, 114, 114, 114, 114, 114, /* 89 */
5145 114, 114, 83, 83, 83, 83, 83, 83, /* 89 */
5146 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */
5147 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5148 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5149 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5150 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */
5151 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */
5152 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */
5153 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */
5154 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */
5155 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */
5156 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */
5157 121, 121, 60, 60, 60, 60, 60, 60, /* 90 */
5158 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */
5159 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */
5160 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5161 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5162 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5163 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5164 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5165 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5166 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5167 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */
5168 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5169 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5170 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */
5171 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */
5172 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5173 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5174 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5175 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */
5176 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5177 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5178 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5179 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5180 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5181 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5182 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */
5183 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */
5184 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */
5185 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5186 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5187 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5188 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5189 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */
5190 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5191 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */
5192 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */
5193 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */
5194 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */
5195 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */
5196 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5197 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5198 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5199 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */
5200 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5201 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5202 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5203 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */
5204 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */
5205 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */
5206 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5207 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */
5208 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */
5209 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5210 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5211 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */
5212 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5213 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5214 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */
5215 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */
5216 114, 114, 114, 114, 114, 114, 114, 114, /* 98 */
5217 114, 114, 15, 15, 15, 15, 15, 15, /* 98 */
5218 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5219 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5220 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5221 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */
5222 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5223 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */
5224 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5225 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */
5226 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5227 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5228 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5229 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5230 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */
5231 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */
5232 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5233 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5234 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5235 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5236 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5237 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */
5238 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */
5239 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */
5240 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5241 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5242 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5243 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */
5244 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5245 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5246 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */
5247 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */
5248 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5249 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5250 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5251 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */
5252 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */
5253 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5254 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5255 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */
5256 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5257 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5258 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5259 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */
5260 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */
5261 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5262 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5263 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */
5264 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5265 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5266 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5267 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5268 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5269 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5270 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5271 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */
5272 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5273 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5274 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5275 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5276 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5277 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5278 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5279 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */
5280 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5281 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5282 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5283 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5284 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */
5285 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */
5286 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5287 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */
5288 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */
5289 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */
5290 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */
5291 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */
5292 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */
5293 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */
5294 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */
5295 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */
5296 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */
5297 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5298 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5299 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5300 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5301 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5302 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5303 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */
5304 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5305 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5306 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5307 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5308 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5309 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */
5310 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */
5311 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */
5312 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5313 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */
5314 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */
5315 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5316 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5317 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5318 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5319 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */
5320 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5321 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5322 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5323 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5324 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5325 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5326 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */
5327 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */
5328 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5329 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */
5330 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5331 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5332 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5333 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5334 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5335 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */
5336 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5337 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5338 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */
5339 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5340 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5341 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5342 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5343 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */
5344 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5345 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5346 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5347 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5348 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5349 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */
5350 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */
5351 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */
5352 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5353 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5354 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5355 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5356 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */
5357 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */
5358 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */
5359 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */
5360 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */
5361 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */
5362 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */
5363 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */
5364 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */
5365 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */
5366 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */
5367 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */
5368 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5369 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5370 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5371 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5372 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5373 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5374 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */
5375 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */
5376 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */
5377 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */
5378 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */
5379 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */
5380 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5381 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5382 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */
5383 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */
5384 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5385 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5386 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */
5387 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */
5388 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */
5389 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5390 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5391 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */
5392 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5393 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5394 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5395 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */
5396 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5397 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5398 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */
5399 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */
5400 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5401 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5402 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */
5403 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */
5404 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */
5405 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */
5406 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */
5407 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */
5410 /* The A table has 124 entries for a total of 496 bytes. */
5412 const uint32 js_A[] = {
5413 0x0001000F, /* 0 Cc, ignorable */
5414 0x0004000F, /* 1 Cc, whitespace */
5415 0x0004000C, /* 2 Zs, whitespace */
5416 0x00000018, /* 3 Po */
5417 0x0006001A, /* 4 Sc, currency */
5418 0x00000015, /* 5 Ps */
5419 0x00000016, /* 6 Pe */
5420 0x00000019, /* 7 Sm */
5421 0x00000014, /* 8 Pd */
5422 0x00036089, /* 9 Nd, identifier part, decimal 16 */
5423 0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */
5424 0x0000001B, /* 11 Sk */
5425 0x00050017, /* 12 Pc, underscore */
5426 0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */
5427 0x0000000C, /* 14 Zs */
5428 0x0000001C, /* 15 So */
5429 0x00070182, /* 16 Ll, identifier start */
5430 0x0000600B, /* 17 No, decimal 16 */
5431 0x0000500B, /* 18 No, decimal 8 */
5432 0x0000800B, /* 19 No, strange */
5433 0x08270181, /* 20 Lu, hasLower (add 32), identifier start */
5434 0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */
5435 0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */
5436 0x00670181, /* 23 Lu, hasLower (add 1), identifier start */
5437 0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */
5438 0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */
5439 0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */
5440 0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */
5441 0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */
5442 0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */
5443 0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */
5444 0x33670181, /* 31 Lu, hasLower (add 205), identifier start */
5445 0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */
5446 0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */
5447 0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */
5448 0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */
5449 0x34670181, /* 36 Lu, hasLower (add 209), identifier start */
5450 0x35670181, /* 37 Lu, hasLower (add 213), identifier start */
5451 0x00070181, /* 38 Lu, identifier start */
5452 0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */
5453 0x00070185, /* 40 Lo, identifier start */
5454 0x36670181, /* 41 Lu, hasLower (add 217), identifier start */
5455 0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */
5456 0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */
5457 0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */
5458 0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */
5459 0x00000000, /* 46 unassigned */
5460 0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */
5461 0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */
5462 0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */
5463 0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */
5464 0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */
5465 0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */
5466 0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */
5467 0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */
5468 0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */
5469 0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */
5470 0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */
5471 0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */
5472 0x00070084, /* 59 Lm, identifier start */
5473 0x00030086, /* 60 Mn, identifier part */
5474 0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */
5475 0x09670181, /* 62 Lu, hasLower (add 37), identifier start */
5476 0x10270181, /* 63 Lu, hasLower (add 64), identifier start */
5477 0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */
5478 0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */
5479 0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */
5480 0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */
5481 0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */
5482 0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */
5483 0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */
5484 0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */
5485 0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */
5486 0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */
5487 0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */
5488 0x14270181, /* 75 Lu, hasLower (add 80), identifier start */
5489 0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */
5490 0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */
5491 0x00034089, /* 78 Nd, identifier part, decimal 0 */
5492 0x00000087, /* 79 Me */
5493 0x00030088, /* 80 Mc, identifier part */
5494 0x00037489, /* 81 Nd, identifier part, decimal 26 */
5495 0x00005A0B, /* 82 No, decimal 13 */
5496 0x00006E0B, /* 83 No, decimal 23 */
5497 0x0000740B, /* 84 No, decimal 26 */
5498 0x0000000B, /* 85 No */
5499 0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */
5500 0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */
5501 0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */
5502 0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */
5503 0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */
5504 0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */
5505 0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */
5506 0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */
5507 0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */
5508 0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */
5509 0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */
5510 0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */
5511 0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */
5512 0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */
5513 0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */
5514 0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */
5515 0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */
5516 0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */
5517 0x00010010, /* 104 Cf, ignorable */
5518 0x0004000D, /* 105 Zl, whitespace */
5519 0x0004000E, /* 106 Zp, whitespace */
5520 0x0000400B, /* 107 No, decimal 0 */
5521 0x0000440B, /* 108 No, decimal 2 */
5522 0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */
5523 0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */
5524 0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */
5525 0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */
5526 0x0007818A, /* 113 Nl, identifier start, strange */
5527 0x0000420B, /* 114 No, decimal 1 */
5528 0x0000720B, /* 115 No, decimal 25 */
5529 0x06A0001C, /* 116 So, hasLower (add 26) */
5530 0x0690001C, /* 117 So, hasUpper (subtract 26) */
5531 0x00006C0B, /* 118 No, decimal 22 */
5532 0x0000560B, /* 119 No, decimal 11 */
5533 0x0007738A, /* 120 Nl, identifier start, decimal 25 */
5534 0x0007418A, /* 121 Nl, identifier start, decimal 0 */
5535 0x00000013, /* 122 Cs */
5536 0x00000012 /* 123 Co */
5539 const jschar js_uriReservedPlusPound_ucstr[] =
5540 {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};
5541 const jschar js_uriUnescaped_ucstr[] =
5542 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
5543 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
5544 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
5545 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
5546 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
5547 '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};
5550 * This table allows efficient testing for the regular expression \w which is
5551 * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z].
5553 const bool js_alnum[] = {
5554 /* 0 1 2 3 4 5 5 7 8 9 */
5555 /* 0 */ false, false, false, false, false, false, false, false, false, false,
5556 /* 1 */ false, false, false, false, false, false, false, false, false, false,
5557 /* 2 */ false, false, false, false, false, false, false, false, false, false,
5558 /* 3 */ false, false, false, false, false, false, false, false, false, false,
5559 /* 4 */ false, false, false, false, false, false, false, false, true, true,
5560 /* 5 */ true, true, true, true, true, true, true, true, false, false,
5561 /* 6 */ false, false, false, false, false, true, true, true, true, true,
5562 /* 7 */ true, true, true, true, true, true, true, true, true, true,
5563 /* 8 */ true, true, true, true, true, true, true, true, true, true,
5564 /* 9 */ true, false, false, false, false, true, false, true, true, true,
5565 /* 10 */ true, true, true, true, true, true, true, true, true, true,
5566 /* 11 */ true, true, true, true, true, true, true, true, true, true,
5567 /* 12 */ true, true, true, false, false, false, false, false
5570 #define URI_CHUNK 64U
5572 static inline bool
5573 TransferBufferToString(JSContext *cx, StringBuffer &sb, Value *rval)
5575 JSString *str = sb.finishString();
5576 if (!str)
5577 return false;
5578 rval->setString(str);
5579 return true;;
5583 * ECMA 3, 15.1.3 URI Handling Function Properties
5585 * The following are implementations of the algorithms
5586 * given in the ECMA specification for the hidden functions
5587 * 'Encode' and 'Decode'.
5589 static JSBool
5590 Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
5591 const jschar *unescapedSet2, Value *rval)
5593 static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
5595 size_t length = str->length();
5596 const jschar *chars = str->getChars(cx);
5597 if (!chars)
5598 return JS_FALSE;
5600 if (length == 0) {
5601 rval->setString(cx->runtime->emptyString);
5602 return JS_TRUE;
5605 StringBuffer sb(cx);
5606 jschar hexBuf[4];
5607 hexBuf[0] = '%';
5608 hexBuf[3] = 0;
5609 for (size_t k = 0; k < length; k++) {
5610 jschar c = chars[k];
5611 if (js_strchr(unescapedSet, c) ||
5612 (unescapedSet2 && js_strchr(unescapedSet2, c))) {
5613 if (!sb.append(c))
5614 return JS_FALSE;
5615 } else {
5616 if ((c >= 0xDC00) && (c <= 0xDFFF)) {
5617 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5618 JSMSG_BAD_URI, NULL);
5619 return JS_FALSE;
5621 uint32 v;
5622 if (c < 0xD800 || c > 0xDBFF) {
5623 v = c;
5624 } else {
5625 k++;
5626 if (k == length) {
5627 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5628 JSMSG_BAD_URI, NULL);
5629 return JS_FALSE;
5631 jschar c2 = chars[k];
5632 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
5633 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5634 JSMSG_BAD_URI, NULL);
5635 return JS_FALSE;
5637 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
5639 uint8 utf8buf[4];
5640 size_t L = js_OneUcs4ToUtf8Char(utf8buf, v);
5641 for (size_t j = 0; j < L; j++) {
5642 hexBuf[1] = HexDigits[utf8buf[j] >> 4];
5643 hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
5644 if (!sb.append(hexBuf, 3))
5645 return JS_FALSE;
5650 return TransferBufferToString(cx, sb, rval);
5653 static JSBool
5654 Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval)
5656 size_t length = str->length();
5657 const jschar *chars = str->getChars(cx);
5658 if (!chars)
5659 return JS_FALSE;
5661 if (length == 0) {
5662 rval->setString(cx->runtime->emptyString);
5663 return JS_TRUE;
5666 StringBuffer sb(cx);
5667 for (size_t k = 0; k < length; k++) {
5668 jschar c = chars[k];
5669 if (c == '%') {
5670 size_t start = k;
5671 if ((k + 2) >= length)
5672 goto report_bad_uri;
5673 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5674 goto report_bad_uri;
5675 jsuint B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5676 k += 2;
5677 if (!(B & 0x80)) {
5678 c = (jschar)B;
5679 } else {
5680 intN n = 1;
5681 while (B & (0x80 >> n))
5682 n++;
5683 if (n == 1 || n > 4)
5684 goto report_bad_uri;
5685 uint8 octets[4];
5686 octets[0] = (uint8)B;
5687 if (k + 3 * (n - 1) >= length)
5688 goto report_bad_uri;
5689 for (intN j = 1; j < n; j++) {
5690 k++;
5691 if (chars[k] != '%')
5692 goto report_bad_uri;
5693 if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
5694 goto report_bad_uri;
5695 B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
5696 if ((B & 0xC0) != 0x80)
5697 goto report_bad_uri;
5698 k += 2;
5699 octets[j] = (char)B;
5701 uint32 v = Utf8ToOneUcs4Char(octets, n);
5702 if (v >= 0x10000) {
5703 v -= 0x10000;
5704 if (v > 0xFFFFF)
5705 goto report_bad_uri;
5706 c = (jschar)((v & 0x3FF) + 0xDC00);
5707 jschar H = (jschar)((v >> 10) + 0xD800);
5708 if (!sb.append(H))
5709 return JS_FALSE;
5710 } else {
5711 c = (jschar)v;
5714 if (js_strchr(reservedSet, c)) {
5715 if (!sb.append(chars + start, k - start + 1))
5716 return JS_FALSE;
5717 } else {
5718 if (!sb.append(c))
5719 return JS_FALSE;
5721 } else {
5722 if (!sb.append(c))
5723 return JS_FALSE;
5727 return TransferBufferToString(cx, sb, rval);
5729 report_bad_uri:
5730 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
5731 /* FALL THROUGH */
5733 return JS_FALSE;
5736 static JSBool
5737 str_decodeURI(JSContext *cx, uintN argc, Value *vp)
5739 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5740 if (!str)
5741 return JS_FALSE;
5742 return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp);
5745 static JSBool
5746 str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5748 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5749 if (!str)
5750 return JS_FALSE;
5751 return Decode(cx, str, js_empty_ucstr, vp);
5754 static JSBool
5755 str_encodeURI(JSContext *cx, uintN argc, Value *vp)
5757 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5758 if (!str)
5759 return JS_FALSE;
5760 return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr,
5761 vp);
5764 static JSBool
5765 str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp)
5767 JSLinearString *str = ArgToRootedString(cx, argc, vp, 0);
5768 if (!str)
5769 return JS_FALSE;
5770 return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp);
5774 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
5775 * least 4 bytes long. Return the number of UTF-8 bytes of data written.
5778 js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)
5780 int utf8Length = 1;
5782 JS_ASSERT(ucs4Char <= 0x10FFFF);
5783 if (ucs4Char < 0x80) {
5784 *utf8Buffer = (uint8)ucs4Char;
5785 } else {
5786 int i;
5787 uint32 a = ucs4Char >> 11;
5788 utf8Length = 2;
5789 while (a) {
5790 a >>= 5;
5791 utf8Length++;
5793 i = utf8Length;
5794 while (--i) {
5795 utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
5796 ucs4Char >>= 6;
5798 *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
5800 return utf8Length;
5804 * Convert a utf8 character sequence into a UCS-4 character and return that
5805 * character. It is assumed that the caller already checked that the sequence
5806 * is valid.
5808 static uint32
5809 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
5811 uint32 ucs4Char;
5812 uint32 minucs4Char;
5813 /* from Unicode 3.1, non-shortest form is illegal */
5814 static const uint32 minucs4Table[] = {
5815 0x00000080, 0x00000800, 0x00010000
5818 JS_ASSERT(utf8Length >= 1 && utf8Length <= 4);
5819 if (utf8Length == 1) {
5820 ucs4Char = *utf8Buffer;
5821 JS_ASSERT(!(ucs4Char & 0x80));
5822 } else {
5823 JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) ==
5824 (0x100 - (1 << (8-utf8Length))));
5825 ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
5826 minucs4Char = minucs4Table[utf8Length-2];
5827 while (--utf8Length) {
5828 JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);
5829 ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
5831 if (JS_UNLIKELY(ucs4Char < minucs4Char)) {
5832 ucs4Char = OVERLONG_UTF8;
5833 } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
5834 ucs4Char = 0xFFFD;
5837 return ucs4Char;
5840 namespace js {
5842 size_t
5843 PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32 quote)
5845 enum {
5846 STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
5847 } state;
5849 JS_ASSERT(quote == 0 || quote == '\'' || quote == '"');
5850 JS_ASSERT_IF(!buffer, bufferSize == 0);
5851 JS_ASSERT_IF(fp, !buffer);
5853 if (bufferSize == 0)
5854 buffer = NULL;
5855 else
5856 bufferSize--;
5858 const jschar *chars = str->chars();
5859 const jschar *charsEnd = chars + str->length();
5860 size_t n = 0;
5861 state = FIRST_QUOTE;
5862 uintN shift = 0;
5863 uintN hex = 0;
5864 uintN u = 0;
5865 char c = 0; /* to quell GCC warnings */
5867 for (;;) {
5868 switch (state) {
5869 case STOP:
5870 goto stop;
5871 case FIRST_QUOTE:
5872 state = CHARS;
5873 goto do_quote;
5874 case LAST_QUOTE:
5875 state = STOP;
5876 do_quote:
5877 if (quote == 0)
5878 continue;
5879 c = (char)quote;
5880 break;
5881 case CHARS:
5882 if (chars == charsEnd) {
5883 state = LAST_QUOTE;
5884 continue;
5886 u = *chars++;
5887 if (u < ' ') {
5888 if (u != 0) {
5889 const char *escape = strchr(js_EscapeMap, (int)u);
5890 if (escape) {
5891 u = escape[1];
5892 goto do_escape;
5895 goto do_hex_escape;
5897 if (u < 127) {
5898 if (u == quote || u == '\\')
5899 goto do_escape;
5900 c = (char)u;
5901 } else if (u < 0x100) {
5902 goto do_hex_escape;
5903 } else {
5904 shift = 16;
5905 hex = u;
5906 u = 'u';
5907 goto do_escape;
5909 break;
5910 do_hex_escape:
5911 shift = 8;
5912 hex = u;
5913 u = 'x';
5914 do_escape:
5915 c = '\\';
5916 state = ESCAPE_START;
5917 break;
5918 case ESCAPE_START:
5919 JS_ASSERT(' ' <= u && u < 127);
5920 c = (char)u;
5921 state = ESCAPE_MORE;
5922 break;
5923 case ESCAPE_MORE:
5924 if (shift == 0) {
5925 state = CHARS;
5926 continue;
5928 shift -= 4;
5929 u = 0xF & (hex >> shift);
5930 c = (char)(u + (u < 10 ? '0' : 'A' - 10));
5931 break;
5933 if (buffer) {
5934 JS_ASSERT(n <= bufferSize);
5935 if (n != bufferSize) {
5936 buffer[n] = c;
5937 } else {
5938 buffer[n] = '\0';
5939 buffer = NULL;
5941 } else if (fp) {
5942 if (fputc(c, fp) < 0)
5943 return size_t(-1);
5945 n++;
5947 stop:
5948 if (buffer)
5949 buffer[n] = '\0';
5950 return n;
5953 } /* namespace js */