📄 jsstr.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *//* * JS string type implementation. * * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these * native methods store strings (possibly newborn) converted from their 'this' * parameter and arguments on the stack: 'this' conversions at argv[-1], arg * conversions at their index (argv[0], argv[1]). This is a legitimate method * of rooting things that might lose their newborn root due to subsequent GC * allocations in the same native method. */#include "jsstddef.h"#include <stdlib.h>#include <string.h>#include "jstypes.h"#include "jsutil.h" /* Added by JSIFY */#include "jshash.h" /* Added by JSIFY */#include "jsprf.h"#include "jsapi.h"#include "jsarray.h"#include "jsatom.h"#include "jsbool.h"#include "jscntxt.h"#include "jsconfig.h"#include "jsgc.h"#include "jsinterp.h"#include "jslock.h"#include "jsnum.h"#include "jsobj.h"#include "jsopcode.h"#include "jsregexp.h"#include "jsstr.h"#define JSSTRDEP_RECURSION_LIMIT 100size_tjs_MinimizeDependentStrings(JSString *str, int level, JSString **basep){ JSString *base; size_t start, length; JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); base = JSSTRDEP_BASE(str); start = JSSTRDEP_START(str); if (JSSTRING_IS_DEPENDENT(base)) { if (level < JSSTRDEP_RECURSION_LIMIT) { start += js_MinimizeDependentStrings(base, level + 1, &base); } else { do { start += JSSTRDEP_START(base); base = JSSTRDEP_BASE(base); } while (JSSTRING_IS_DEPENDENT(base)); } if (start == 0) { JS_ASSERT(JSSTRING_IS_PREFIX(str)); JSPREFIX_SET_BASE(str, base); } else if (start <= JSSTRDEP_START_MASK) { length = JSSTRDEP_LENGTH(str); JSSTRDEP_SET_START_AND_LENGTH(str, start, length); JSSTRDEP_SET_BASE(str, base); } } *basep = base; return start;}jschar *js_GetDependentStringChars(JSString *str){ size_t start; JSString *base; start = js_MinimizeDependentStrings(str, 0, &base); JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); JS_ASSERT(start < base->length); return base->chars + start;}jschar *js_GetStringChars(JSString *str){ if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) return NULL; *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; return str->chars;}JSString *js_ConcatStrings(JSContext *cx, JSString *left, JSString *right){ size_t rn, ln, lrdist, n; jschar *rs, *ls, *s; JSDependentString *ldep; /* non-null if left should become dependent */ JSString *str; if (JSSTRING_IS_DEPENDENT(right)) { rn = JSSTRDEP_LENGTH(right); rs = JSSTRDEP_CHARS(right); } else { rn = right->length; rs = right->chars; } if (rn == 0) return left; if (JSSTRING_IS_DEPENDENT(left) || !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { /* We must copy if left does not own a buffer to realloc. */ ln = JSSTRING_LENGTH(left); if (ln == 0) return right; ls = JSSTRING_CHARS(left); s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); if (!s) return NULL; js_strncpy(s, ls, ln); ldep = NULL; } else { /* We can realloc left's space and make it depend on our result. */ ln = left->length; if (ln == 0) return right; ls = left->chars; s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); if (!s) return NULL; /* Take care: right could depend on left! */ lrdist = (size_t)(rs - ls); if (lrdist < ln) rs = s + lrdist; left->chars = ls = s; ldep = JSSTRDEP(left); } js_strncpy(s + ln, rs, rn); n = ln + rn; s[n] = 0; str = js_NewString(cx, s, n, GCF_MUTABLE); if (!str) { /* Out of memory: clean up any space we (re-)allocated. */ if (!ldep) { JS_free(cx, s); } else { s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); if (s) left->chars = s; } } else { /* Morph left into a dependent prefix if we realloc'd its buffer. */ if (ldep) { JSPREFIX_SET_LENGTH(ldep, ln); JSPREFIX_SET_BASE(ldep, str);#ifdef DEBUG { JSRuntime *rt = cx->runtime; JS_RUNTIME_METER(rt, liveDependentStrings); JS_RUNTIME_METER(rt, totalDependentStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->strdepLengthSum += (double)ln, rt->strdepLengthSquaredSum += (double)ln * (double)ln)); }#endif } } return str;}/* * May be called with null cx by js_GetStringChars, above; and by the jslock.c * MAKE_STRING_IMMUTABLE file-local macro. */const jschar *js_UndependString(JSContext *cx, JSString *str){ size_t n, size; jschar *s; if (JSSTRING_IS_DEPENDENT(str)) { n = JSSTRDEP_LENGTH(str); size = (n + 1) * sizeof(jschar); s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); if (!s) return NULL; js_strncpy(s, JSSTRDEP_CHARS(str), n); s[n] = 0; str->length = n; str->chars = s;#ifdef DEBUG if (cx) { JSRuntime *rt = cx->runtime; JS_RUNTIME_UNMETER(rt, liveDependentStrings); JS_RUNTIME_UNMETER(rt, totalDependentStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->strdepLengthSum -= (double)n, rt->strdepLengthSquaredSum -= (double)n * (double)n)); }#endif } return str->chars;}/* * Forward declarations for URI encode/decode and helper routines */static JSBoolstr_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);static JSBoolstr_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);static JSBoolstr_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);static JSBoolstr_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);static uint32Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);/* * Contributions from the String class to the set of methods defined for the * global object. escape and unescape used to be defined in the Mocha library, * but as ECMA decided to spec them, they've been moved to the core engine * and made ECMA-compliant. (Incomplete escapes are interpreted as literal * characters by unescape.) *//* * Stuff to emulate the old libmocha escape, which took a second argument * giving the type of escape to perform. Retained for compatibility, and * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. */#define URL_XALPHAS ((uint8) 1)#define URL_XPALPHAS ((uint8) 2)#define URL_PATH ((uint8) 4)static const uint8 urlCharType[256] =/* Bit 0 xalpha -- the alphas * Bit 1 xpalpha -- as xalpha but * converts spaces to plus and plus to %20 * Bit 2 ... path -- as xalphas but doesn't escape '/' */ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ 0, };/* This matches the ECMA escape set when mask is 7 (default.) */#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))/* See ECMA-262 15.1.2.4. */JSBooljs_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSString *str; size_t i, ni, length, newlength; const jschar *chars; jschar *newchars; jschar ch; jsint mask; jsdouble d; const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; if (argc > 1) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(d) || (mask = (jsint)d) != d || mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_STRING_MASK, numBuf); return JS_FALSE; } } str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); chars = JSSTRING_CHARS(str); length = newlength = JSSTRING_LENGTH(str); /* Take a first pass and see how big the result string will need to be. */ for (i = 0; i < length; i++) { if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) continue; if (ch < 256) { if (mask == URL_XPALPHAS && ch == ' ') continue; /* The character will be encoded as '+' */ newlength += 2; /* The character will be encoded as %XX */ } else { newlength += 5; /* The character will be encoded as %uXXXX */ } /* * This overflow test works because newlength is incremented by at * most 5 on each iteration. */ if (newlength < length) { JS_ReportOutOfMemory(cx); return JS_FALSE; } } if (newlength >= ~(size_t)0 / sizeof(jschar)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; for (i = 0, ni = 0; i < length; i++) { if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { newchars[ni++] = ch; } else if (ch < 256) { if (mask == URL_XPALPHAS && ch == ' ') { newchars[ni++] = '+'; /* convert spaces to pluses */ } else { newchars[ni++] = '%'; newchars[ni++] = digits[ch >> 4]; newchars[ni++] = digits[ch & 0xF]; } } else { newchars[ni++] = '%'; newchars[ni++] = 'u'; newchars[ni++] = digits[ch >> 12]; newchars[ni++] = digits[(ch & 0xF00) >> 8]; newchars[ni++] = digits[(ch & 0xF0) >> 4]; newchars[ni++] = digits[ch & 0xF]; } } JS_ASSERT(ni == newlength); newchars[newlength] = 0; str = js_NewString(cx, newchars, newlength, 0); if (!str) { JS_free(cx, newchars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE;}#undef IS_OK/* See ECMA-262 15.1.2.5 */static JSBoolstr_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSString *str; size_t i, ni, length; const jschar *chars; jschar *newchars; jschar ch; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); chars = JSSTRING_CHARS(str); length = JSSTRING_LENGTH(str); /* Don't bother allocating less space for the new string. */ newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; ni = i = 0; while (i < length) { ch = chars[i++]; if (ch == '%') { if (i + 1 < length && JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) { ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -