зеркало из https://github.com/mozilla/gecko-dev.git
Bug 503952 - JSStringBuffer/JSCharBuffer with JSTempVector, part 1. r=jwalden
This commit is contained in:
Родитель
aaa22d1c75
Коммит
e94edc5f03
|
@ -1331,58 +1331,51 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
|
|||
}
|
||||
|
||||
/* Transfer ownership of buffer to returned string. */
|
||||
static JSBool
|
||||
BufferToString(JSContext *cx, JSTempVector<jschar> &buf, jsval *rval)
|
||||
static inline JSBool
|
||||
BufferToString(JSContext *cx, JSCharVector &buf, jsval *rval)
|
||||
{
|
||||
size_t length = buf.size() - 1;
|
||||
jschar *chars = buf.extractRawBuffer();
|
||||
JSString *str = js_NewString(cx, chars, length);
|
||||
if (!str) {
|
||||
cx->free(chars);
|
||||
return JS_FALSE;
|
||||
}
|
||||
JSString *str = js_NewStringFromCharBuffer(cx, buf);
|
||||
if (!str)
|
||||
return false;
|
||||
*rval = STRING_TO_JSVAL(str);
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static JSBool
|
||||
array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj ||
|
||||
(OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
|
||||
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find joins or cycles in the reachable object graph. */
|
||||
jschar *sharpchars;
|
||||
JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars);
|
||||
if (!he)
|
||||
return JS_FALSE;
|
||||
JSBool initiallySharp = IS_SHARP(he) ? JS_TRUE : JS_FALSE;
|
||||
return false;
|
||||
bool initiallySharp = IS_SHARP(he);
|
||||
|
||||
/* After this point, all paths exit through the 'done' label. */
|
||||
MUST_FLOW_THROUGH("done");
|
||||
JSBool ok = JS_TRUE;
|
||||
/* After this point, all paths exit through the 'out' label. */
|
||||
MUST_FLOW_THROUGH("out");
|
||||
bool ok = false;
|
||||
|
||||
/*
|
||||
* This object will take responsibility for the jschar buffer until the
|
||||
* buffer is transferred to the returned JSString.
|
||||
*/
|
||||
JSTempVector<jschar> buf(cx);
|
||||
if (!(ok = buf.reserve(3)))
|
||||
goto done;
|
||||
JSCharVector buf(cx);
|
||||
|
||||
/* Cycles/joins are indicated by sharp objects. */
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (IS_SHARP(he)) {
|
||||
JS_ASSERT(sharpchars != 0);
|
||||
/* +1 to include the trailing '\0' */
|
||||
buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars) + 1);
|
||||
buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
|
||||
goto make_string;
|
||||
} else if (sharpchars) {
|
||||
MAKE_SHARP(he);
|
||||
|
@ -1390,30 +1383,28 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
|||
}
|
||||
#else
|
||||
if (IS_SHARP(he)) {
|
||||
static const jschar arr[] = { '[', ']', 0 };
|
||||
if (!(ok = buf.pushBack(arr, arr + 3)))
|
||||
goto done;
|
||||
if (!js_AppendLiteral(buf, "[]"))
|
||||
goto out;
|
||||
if (sharpchars)
|
||||
cx->free(sharpchars);
|
||||
goto make_string;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(ok = buf.pushBack('[')))
|
||||
goto done;
|
||||
if (!buf.append('['))
|
||||
goto out;
|
||||
|
||||
jsuint length;
|
||||
ok = js_GetLengthProperty(cx, obj, &length);
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
goto out;
|
||||
|
||||
for (jsuint index = 0; index < length; index++) {
|
||||
/* Use vp to locally root each element value. */
|
||||
JSBool hole;
|
||||
ok = (JS_CHECK_OPERATION_LIMIT(cx) &&
|
||||
GetArrayElement(cx, obj, index, &hole, vp));
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
!GetArrayElement(cx, obj, index, &hole, vp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get element's character string. */
|
||||
JSString *str;
|
||||
|
@ -1421,10 +1412,8 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
|||
str = cx->runtime->emptyString;
|
||||
} else {
|
||||
str = js_ValueToSource(cx, *vp);
|
||||
if (!str) {
|
||||
ok = JS_FALSE;
|
||||
goto done;
|
||||
}
|
||||
if (!str)
|
||||
goto out;
|
||||
}
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
const jschar *chars;
|
||||
|
@ -1432,26 +1421,28 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
|||
str->getCharsAndLength(chars, charlen);
|
||||
|
||||
/* Append element to buffer. */
|
||||
if (!(ok = buf.pushBack(chars, chars + charlen)))
|
||||
goto done;
|
||||
if (!buf.append(chars, charlen))
|
||||
goto out;
|
||||
if (index + 1 != length) {
|
||||
if (!(ok = buf.pushBack(',')) || !(ok = buf.pushBack(' ')))
|
||||
goto done;
|
||||
if (!js_AppendLiteral(buf, ", "))
|
||||
goto out;
|
||||
} else if (hole) {
|
||||
if (!(ok = buf.pushBack(',')))
|
||||
goto done;
|
||||
if (!buf.append(','))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the buffer. */
|
||||
if (!(ok = buf.pushBack(']')) || !(ok = buf.pushBack(0)))
|
||||
goto done;
|
||||
if (!buf.append(']'))
|
||||
goto out;
|
||||
|
||||
make_string:
|
||||
if (!(ok = BufferToString(cx, buf, vp)))
|
||||
goto done;
|
||||
if (!BufferToString(cx, buf, vp))
|
||||
goto out;
|
||||
|
||||
done:
|
||||
ok = true;
|
||||
|
||||
out:
|
||||
if (!initiallySharp)
|
||||
js_LeaveSharpObject(cx, NULL);
|
||||
return ok;
|
||||
|
@ -1464,7 +1455,7 @@ js_hash_array(const void *key)
|
|||
return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
|
||||
}
|
||||
|
||||
JSBool
|
||||
bool
|
||||
js_InitContextBusyArrayTable(JSContext *cx)
|
||||
{
|
||||
cx->busyArrayTable = JS_NewHashTable(4, js_hash_array, JS_CompareValues,
|
||||
|
@ -1476,7 +1467,7 @@ static JSBool
|
|||
array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
JSString *sepstr, jsval *rval)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/*
|
||||
* This hash table is shared between toString invocations and must be empty
|
||||
|
@ -1485,7 +1476,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
|||
JSHashTable *table = cx->busyArrayTable;
|
||||
|
||||
/*
|
||||
* Use HashTable entry as the cycle indicator. On first visit, create the
|
||||
* Use HashTable entry as the cycle indicator. On first visit, create the
|
||||
* entry, and, when leaving, remove the entry.
|
||||
*/
|
||||
JSHashNumber hash = js_hash_array(obj);
|
||||
|
@ -1496,19 +1487,19 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
|||
he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
|
||||
if (!he) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* Cycle, so return empty string. */
|
||||
*rval = ATOM_KEY(cx->runtime->atomState.emptyAtom);
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSAutoTempValueRooter tvr(cx, obj);
|
||||
|
||||
/* After this point, all paths exit through the 'done' label. */
|
||||
MUST_FLOW_THROUGH("done");
|
||||
JSBool ok = JS_TRUE;
|
||||
/* After this point, all paths exit through the 'out' label. */
|
||||
MUST_FLOW_THROUGH("out");
|
||||
bool ok = false;
|
||||
|
||||
/* Get characters to use for the separator. */
|
||||
static const jschar comma = ',';
|
||||
|
@ -1525,64 +1516,55 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
|||
* This object will take responsibility for the jschar buffer until the
|
||||
* buffer is transferred to the returned JSString.
|
||||
*/
|
||||
JSTempVector<jschar> buf(cx);
|
||||
JSCharVector buf(cx);
|
||||
|
||||
jsuint length;
|
||||
ok = js_GetLengthProperty(cx, obj, &length);
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
goto out;
|
||||
|
||||
for (jsuint index = 0; index < length; index++) {
|
||||
/* Use rval to locally root each element value. */
|
||||
JSBool hole;
|
||||
ok = JS_CHECK_OPERATION_LIMIT(cx) &&
|
||||
GetArrayElement(cx, obj, index, &hole, rval);
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
!GetArrayElement(cx, obj, index, &hole, rval)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get element's character string. */
|
||||
if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) {
|
||||
if (locale) {
|
||||
/* Work on obj.toLocalString() instead. */
|
||||
JSObject *robj;
|
||||
|
||||
if (!js_ValueToObject(cx, *rval, &robj))
|
||||
goto out;
|
||||
*rval = OBJECT_TO_JSVAL(robj);
|
||||
JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
|
||||
ok = js_ValueToObject(cx, *rval, &robj);
|
||||
if (ok) {
|
||||
/* Re-use *rval to protect robj temporarily. */
|
||||
*rval = OBJECT_TO_JSVAL(robj);
|
||||
ok = js_TryMethod(cx, robj, atom, 0, NULL, rval);
|
||||
}
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ok = js_ValueToStringBuffer(cx, *rval, buf);
|
||||
if (!ok)
|
||||
goto done;
|
||||
if (!js_ValueToCharBuffer(cx, *rval, buf))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Append the separator. */
|
||||
if (index + 1 != length) {
|
||||
if (!(ok = buf.pushBack(sep, sep + seplen)))
|
||||
goto done;
|
||||
if (!buf.append(sep, seplen))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the buffer. */
|
||||
if (buf.empty()) {
|
||||
*rval = ATOM_KEY(cx->runtime->atomState.emptyAtom);
|
||||
goto done;
|
||||
}
|
||||
if (!BufferToString(cx, buf, rval))
|
||||
goto out;
|
||||
|
||||
ok = buf.pushBack(0) &&
|
||||
BufferToString(cx, buf, rval);
|
||||
if (!ok)
|
||||
goto done;
|
||||
ok = true;
|
||||
|
||||
done:
|
||||
out:
|
||||
/*
|
||||
* It is possible that 'hep' may have been invalidated by subsequent
|
||||
* RawAdd/Remove. Hence, 'RawRemove' must not be used.
|
||||
* RawAdd/Remove. Hence, 'RawRemove' must not be used.
|
||||
*/
|
||||
JS_HashTableRemove(table, obj);
|
||||
return ok;
|
||||
|
|
|
@ -97,8 +97,8 @@ js_GetProtoIfDenseArray(JSContext *cx, JSObject *obj)
|
|||
extern JSObject *
|
||||
js_InitArrayClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSBool
|
||||
js_InitContextBusyArrayTable(JSContext *);
|
||||
extern bool
|
||||
js_InitContextBusyArrayTable(JSContext *cx);
|
||||
|
||||
extern JSObject *
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector,
|
||||
|
|
|
@ -164,12 +164,9 @@ js_BooleanToString(JSContext *cx, JSBool b)
|
|||
|
||||
/* This function implements E-262-3 section 9.8, toString. */
|
||||
JSBool
|
||||
js_BooleanToStringBuffer(JSContext *cx, JSBool b, JSTempVector<jschar> &buf)
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf)
|
||||
{
|
||||
static const jschar trueChars[] = { 't', 'r', 'u', 'e' },
|
||||
falseChars[] = { 'f', 'a', 'l', 's', 'e' };
|
||||
return b ? buf.pushBack(trueChars, trueChars + JS_ARRAY_LENGTH(trueChars))
|
||||
: buf.pushBack(falseChars, falseChars + JS_ARRAY_LENGTH(falseChars));
|
||||
return b ? js_AppendLiteral(buf, "true") : js_AppendLiteral(buf, "false");
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
|
|
@ -81,7 +81,7 @@ extern JSString *
|
|||
js_BooleanToString(JSContext *cx, JSBool b);
|
||||
|
||||
extern JSBool
|
||||
js_BooleanToStringBuffer(JSContext *cx, JSBool b, JSTempVector<jschar> &buf);
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf);
|
||||
|
||||
extern JSBool
|
||||
js_ValueToBoolean(jsval v);
|
||||
|
|
|
@ -862,7 +862,7 @@ js_NumberToString(JSContext *cx, jsdouble d)
|
|||
}
|
||||
|
||||
JSBool JS_FASTCALL
|
||||
js_NumberValueToStringBuffer(JSContext *cx, jsval v, JSTempVector<jschar> &buf)
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
{
|
||||
/* Convert to C-string. */
|
||||
static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE;
|
||||
|
|
|
@ -194,7 +194,7 @@ js_NumberToString(JSContext *cx, jsdouble d);
|
|||
* append to the given buffer.
|
||||
*/
|
||||
extern JSBool JS_FASTCALL
|
||||
js_NumberValueToStringBuffer(JSContext *, jsval, JSTempVector<jschar> &);
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
|
||||
|
||||
/*
|
||||
* Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an
|
||||
|
|
|
@ -137,14 +137,17 @@ typedef struct JSXMLArrayCursor JSXMLArrayCursor;
|
|||
/*
|
||||
* Template declarations.
|
||||
*
|
||||
* jsprvtd.h can be included in both C and C++ translation units. For C++, it
|
||||
* jsprvtd.h can be included in both C and C++ translation units. For C++, it
|
||||
* may possibly be wrapped in an extern "C" block which does not agree with
|
||||
* templates.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C++" {
|
||||
|
||||
template <class T> class JSTempVector;
|
||||
template <class T, size_t MinInlineCapacity = 0> class JSTempVector;
|
||||
|
||||
/* Common JSTempVector instantiations: */
|
||||
typedef JSTempVector<jschar, 32> JSCharVector;
|
||||
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
|
|
@ -2062,8 +2062,8 @@ namespace {
|
|||
|
||||
/*
|
||||
* This table allows efficient testing for the ASCII portion of \s during a
|
||||
* trace. ECMA-262 15.10.2.12 defines the following characters below 128 to be
|
||||
* whitespace: 0x9 (0), 0xA (10), 0xB (11), 0xC (12), 0xD (13), 0x20 (32). The
|
||||
* trace. ECMA-262 15.10.2.12 defines the following characters below 128 to be
|
||||
* whitespace: 0x9 (0), 0xA (10), 0xB (11), 0xC (12), 0xD (13), 0x20 (32). The
|
||||
* index must be <= 32.
|
||||
*/
|
||||
static const bool js_ws[] = {
|
||||
|
@ -2157,7 +2157,7 @@ CharSet::disjoint(const jschar *beg, const jschar *end, uintN classes)
|
|||
}
|
||||
|
||||
/*
|
||||
* Predicate version of the STL's set_intersection. Assumes both ranges are
|
||||
* Predicate version of the STL's set_intersection. Assumes both ranges are
|
||||
* sorted and thus runs in linear time.
|
||||
*
|
||||
* FIXME: This is a reusable algorithm, perhaps it should be put somewhere.
|
||||
|
@ -2205,9 +2205,9 @@ CharSet::disjoint(const CharSet &other) const
|
|||
}
|
||||
|
||||
/*
|
||||
* Return true if the given subexpression may match the empty string. The
|
||||
* conservative answer is |true|. If |next| is true, then the subexpression is
|
||||
* considered to be |node| followed by the rest of |node->next|. Otherwise, the
|
||||
* Return true if the given subexpression may match the empty string. The
|
||||
* conservative answer is |true|. If |next| is true, then the subexpression is
|
||||
* considered to be |node| followed by the rest of |node->next|. Otherwise, the
|
||||
* subexpression is considered to be |node| by itself.
|
||||
*/
|
||||
static bool
|
||||
|
@ -2232,7 +2232,7 @@ mayMatchEmpty(RENode *node, bool next = true)
|
|||
|
||||
/*
|
||||
* Enumerate the set of characters that may be consumed next by the given
|
||||
* subexpression in isolation. Return whether the enumeration was successful.
|
||||
* subexpression in isolation. Return whether the enumeration was successful.
|
||||
*/
|
||||
static bool
|
||||
enumerateNextChars(JSContext *cx, RENode *node, CharSet &set)
|
||||
|
@ -2325,7 +2325,8 @@ class RegExpNativeCompiler {
|
|||
LIns* compileFlatSingleChar(jschar ch, LIns* pos, LInsList& fails)
|
||||
{
|
||||
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0);
|
||||
fails.pushBack(to_fail);
|
||||
if (!fails.append(to_fail))
|
||||
return NULL;
|
||||
LIns* text_ch = lir->insLoad(LIR_ldcs, pos, 0);
|
||||
|
||||
// Extra characters that need to be compared against when doing folding.
|
||||
|
@ -2410,7 +2411,8 @@ class RegExpNativeCompiler {
|
|||
extras[i].match = branch;
|
||||
}
|
||||
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, text_ch, lir->insImm(ch)), 0));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, text_ch, lir->insImm(ch)), 0)))
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < nextras; ++i)
|
||||
targetCurrentPoint(extras[i].match);
|
||||
|
@ -2456,12 +2458,14 @@ class RegExpNativeCompiler {
|
|||
}
|
||||
|
||||
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, lir->ins2(LIR_sub, cpend, lir->insImm(2))), 0);
|
||||
fails.pushBack(to_fail);
|
||||
if (!fails.append(to_fail))
|
||||
return NULL;
|
||||
LIns* text_word = lir->insLoad(LIR_ld, pos, 0);
|
||||
LIns* comp_word = useFastCI ?
|
||||
lir->ins2(LIR_or, text_word, lir->insImm(mask.i)) :
|
||||
text_word;
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_word, lir->insImm(word)), 0));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_word, lir->insImm(word)), 0)))
|
||||
return NULL;
|
||||
|
||||
return lir->ins2(LIR_piadd, pos, lir->insImm(4));
|
||||
}
|
||||
|
@ -2537,9 +2541,14 @@ class RegExpNativeCompiler {
|
|||
memcpy(bitmapData, charSet->u.bits, bitmapLen);
|
||||
|
||||
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0);
|
||||
fails.pushBack(to_fail);
|
||||
if (!fails.append(to_fail))
|
||||
return NULL;
|
||||
LIns* text_ch = lir->insLoad(LIR_ldcs, pos, 0);
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)), 0));
|
||||
if (!fails.append(lir->insBranch(LIR_jf,
|
||||
lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)),
|
||||
0))) {
|
||||
return NULL;
|
||||
}
|
||||
LIns* byteIndex = lir->ins2(LIR_rsh, text_ch, lir->insImm(3));
|
||||
LIns* bitmap = lir->insImmPtr(bitmapData);
|
||||
LIns* byte = lir->insLoad(LIR_ldcb, lir->ins2(LIR_piadd, bitmap, byteIndex), (int) 0);
|
||||
|
@ -2548,7 +2557,8 @@ class RegExpNativeCompiler {
|
|||
LIns* test = lir->ins2(LIR_eq, lir->ins2(LIR_and, byte, bitMask), lir->insImm(0));
|
||||
|
||||
LIns* to_next = lir->insBranch(LIR_jt, test, 0);
|
||||
fails.pushBack(to_next);
|
||||
if (!fails.append(to_next))
|
||||
return NULL;
|
||||
return lir->ins2(LIR_piadd, pos, lir->insImm(2));
|
||||
}
|
||||
|
||||
|
@ -2567,7 +2577,8 @@ class RegExpNativeCompiler {
|
|||
LIns *compileBuiltinClass(RENode *node, LIns *pos, LInsList &fails)
|
||||
{
|
||||
/* All the builtins checked below consume one character. */
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0)))
|
||||
return NULL;
|
||||
LIns *chr = lir->insLoad(LIR_ldcs, pos, 0);
|
||||
|
||||
switch (node->op) {
|
||||
|
@ -2575,21 +2586,27 @@ class RegExpNativeCompiler {
|
|||
{
|
||||
/* Accept any character except those in ECMA-262 15.10.2.8. */
|
||||
LIns *eq1 = lir->ins2(LIR_eq, chr, lir->insImm('\n'));
|
||||
fails.pushBack(lir->insBranch(LIR_jt, eq1, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jt, eq1, NULL)))
|
||||
return NULL;
|
||||
LIns *eq2 = lir->ins2(LIR_eq, chr, lir->insImm('\r'));
|
||||
fails.pushBack(lir->insBranch(LIR_jt, eq2, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jt, eq2, NULL)))
|
||||
return NULL;
|
||||
LIns *eq3 = lir->ins2(LIR_eq, chr, lir->insImm(LINE_SEPARATOR));
|
||||
fails.pushBack(lir->insBranch(LIR_jt, eq3, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jt, eq3, NULL)))
|
||||
return NULL;
|
||||
LIns *eq4 = lir->ins2(LIR_eq, chr, lir->insImm(PARA_SEPARATOR));
|
||||
fails.pushBack(lir->insBranch(LIR_jt, eq4, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jt, eq4, NULL)))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case REOP_DIGIT:
|
||||
{
|
||||
LIns *ge = lir->ins2(LIR_ge, chr, lir->insImm('0'));
|
||||
fails.pushBack(lir->insBranch(LIR_jf, ge, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, ge, NULL)))
|
||||
return NULL;
|
||||
LIns *le = lir->ins2(LIR_le, chr, lir->insImm('9'));
|
||||
fails.pushBack(lir->insBranch(LIR_jf, le, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, le, NULL)))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case REOP_NONDIGIT:
|
||||
|
@ -2598,7 +2615,8 @@ class RegExpNativeCompiler {
|
|||
LIns *ge = lir->ins2(LIR_ge, chr, lir->insImm('0'));
|
||||
LIns *le = lir->ins2(LIR_le, chr, lir->insImm('9'));
|
||||
LIns *both = lir->ins2(LIR_and, ge, le);
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins_eq0(both), NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, lir->ins_eq0(both), NULL)))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case REOP_ALNUM:
|
||||
|
@ -2608,9 +2626,11 @@ class RegExpNativeCompiler {
|
|||
* ((uint)*cp) < 128 && js_alnum[(uint)*cp]
|
||||
*/
|
||||
LIns *rangeCnd = lir->ins2(LIR_ult, chr, lir->insImm(128));
|
||||
fails.pushBack(lir->insBranch(LIR_jf, rangeCnd, NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, rangeCnd, NULL)))
|
||||
return NULL;
|
||||
LIns *tableVal = compileTableRead(chr, js_alnum);
|
||||
fails.pushBack(lir->insBranch(LIR_jt, lir->ins_eq0(tableVal), NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jt, lir->ins_eq0(tableVal), NULL)))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case REOP_NONALNUM:
|
||||
|
@ -2622,7 +2642,8 @@ class RegExpNativeCompiler {
|
|||
LIns *rangeCnd = lir->ins2(LIR_uge, chr, lir->insImm(128));
|
||||
LIns *rangeBr = lir->insBranch(LIR_jt, rangeCnd, NULL);
|
||||
LIns *tableVal = compileTableRead(chr, js_alnum);
|
||||
fails.pushBack(lir->insBranch(LIR_jf, lir->ins_eq0(tableVal), NULL));
|
||||
if (!fails.append(lir->insBranch(LIR_jf, lir->ins_eq0(tableVal), NULL)))
|
||||
return NULL;
|
||||
LIns *success = lir->ins0(LIR_label);
|
||||
rangeBr->setTarget(success);
|
||||
break;
|
||||
|
@ -2632,9 +2653,9 @@ class RegExpNativeCompiler {
|
|||
{
|
||||
/*
|
||||
* ECMA-262 7.2, 7.3, and 15.10.2.12 define a bunch of Unicode code
|
||||
* points for whitespace. We optimize here for the common case of
|
||||
* points for whitespace. We optimize here for the common case of
|
||||
* ASCII characters using a table lookup for the lower block that
|
||||
* can actually contain spaces. For the rest, use a (more or less)
|
||||
* can actually contain spaces. For the rest, use a (more or less)
|
||||
* binary search to minimize tests.
|
||||
*
|
||||
* [0000,0020]: 9, A, B, C, D, 20
|
||||
|
@ -2699,8 +2720,10 @@ class RegExpNativeCompiler {
|
|||
belowMissBr->setTarget(missLbl);
|
||||
aboveMissBr->setTarget(missLbl);
|
||||
LIns *missBr = lir->ins2(LIR_j, NULL, NULL);
|
||||
if (node->op == REOP_SPACE)
|
||||
fails.pushBack(missBr);
|
||||
if (node->op == REOP_SPACE) {
|
||||
if (!fails.append(missBr))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Collect matches. */
|
||||
LIns *matchLbl = lir->ins0(LIR_label);
|
||||
|
@ -2712,7 +2735,8 @@ class RegExpNativeCompiler {
|
|||
eq7Br->setTarget(matchLbl); eq8Br->setTarget(matchLbl);
|
||||
if (node->op == REOP_NONSPACE) {
|
||||
LIns *matchBr = lir->ins2(LIR_j, NULL, NULL);
|
||||
fails.pushBack(matchBr);
|
||||
if (!fails.append(matchBr))
|
||||
return NULL;
|
||||
}
|
||||
/* Fall through means match == success. */
|
||||
|
||||
|
@ -2735,7 +2759,7 @@ class RegExpNativeCompiler {
|
|||
|
||||
/*
|
||||
* If the RE continues after the alternative, we need to ensure that no
|
||||
* backtracking is required. Recursive calls to compileNode will fail
|
||||
* backtracking is required. Recursive calls to compileNode will fail
|
||||
* on capturing parens, so the only thing we have to check here is that,
|
||||
* if the left subexpression matches, we can keep going without later
|
||||
* deciding we need to try the right subexpression.
|
||||
|
@ -2835,7 +2859,7 @@ class RegExpNativeCompiler {
|
|||
|
||||
/*
|
||||
* If the RE continues after the alternative, we need to ensure that no
|
||||
* backtracking is required. Recursive calls to compileNode will fail
|
||||
* backtracking is required. Recursive calls to compileNode will fail
|
||||
* on capturing parens, so the only thing we have to check here is that,
|
||||
* if the quantifier body matches, we can continue matching the body
|
||||
* without later deciding we need to undo the body matches.
|
||||
|
@ -2892,7 +2916,8 @@ class RegExpNativeCompiler {
|
|||
*/
|
||||
if (mayMatchEmpty(bodyRe)) {
|
||||
LIns *eqCnd = lir->ins2(LIR_eq, iterBegin, iterEnd);
|
||||
kidFails.pushBack(lir->insBranch(LIR_jt, eqCnd, NULL));
|
||||
if (!kidFails.append(lir->insBranch(LIR_jt, eqCnd, NULL)))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* End iteration: store loop variables, increment, jump */
|
||||
|
@ -2901,17 +2926,17 @@ class RegExpNativeCompiler {
|
|||
|
||||
/*
|
||||
* This might be the only LIR_live in Mozilla, so I will explain its
|
||||
* sinister semantics. LIR_lives must appear immediately following a
|
||||
* sinister semantics. LIR_lives must appear immediately following a
|
||||
* backwards jump and describe what is live immediately at the *target*
|
||||
* of the back-edge. Thus, these instructions answer the question "what
|
||||
* of the back-edge. Thus, these instructions answer the question "what
|
||||
* is live at the top of the loop?", which makes sense, because the
|
||||
* backwards scan has not yet seen the top of the loop and needs this
|
||||
* information to continue working backwards up the inside of the loop.
|
||||
*
|
||||
* Here, 'cpend' and 'state' get defined before the loop, and used
|
||||
* inside, so they are live at 'loopTop'. While 'iterBegin' is used
|
||||
* after the loop, making it live in on loop exit, it gets defined after
|
||||
* 'loopTop', which "kills" its liveness.
|
||||
* inside, so they are live at 'loopTop'. While 'iterBegin' is used
|
||||
* after the loop, making it live in on loop exit, it gets defined
|
||||
* after 'loopTop', which "kills" its liveness.
|
||||
*/
|
||||
lir->ins1(LIR_live, state);
|
||||
lir->ins1(LIR_live, cpend);
|
||||
|
@ -2922,8 +2947,8 @@ class RegExpNativeCompiler {
|
|||
}
|
||||
|
||||
/*
|
||||
* Compile the regular expression rooted at 'node'. Return 0 on failed
|
||||
* compilation. Otherwise, generate code that falls through on success (the
|
||||
* Compile the regular expression rooted at 'node'. Return 0 on failed
|
||||
* compilation. Otherwise, generate code that falls through on success (the
|
||||
* returned LIns* is the current 'pos') and jumps to the end on failure (by
|
||||
* adding the guard LIns to 'fails').
|
||||
*/
|
||||
|
@ -3010,7 +3035,7 @@ class RegExpNativeCompiler {
|
|||
/* Compile normal regular expressions that can match starting at any char. */
|
||||
bool compileAnchoring(RENode *root, LIns *start)
|
||||
{
|
||||
/* Guard outer anchoring loop. Use <= to allow empty regexp match. */
|
||||
/* Guard outer anchoring loop. Use <= to allow empty regexp match. */
|
||||
LIns *anchorFail = lir->insBranch(LIR_jf, lir->ins2(LIR_le, start, cpend), 0);
|
||||
|
||||
if (!compileRootNode(root, start, anchorFail))
|
||||
|
|
|
@ -2762,6 +2762,26 @@ js_NewString(JSContext *cx, jschar *chars, size_t length)
|
|||
return str;
|
||||
}
|
||||
|
||||
JSString *
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb)
|
||||
{
|
||||
if (cb.empty())
|
||||
return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
|
||||
|
||||
size_t length = cb.size();
|
||||
if (!cb.append('\0'))
|
||||
return NULL;
|
||||
|
||||
jschar *buf = cb.extractRawBuffer();
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
JSString *str = js_NewString(cx, buf, length);
|
||||
if (!str)
|
||||
cx->free(buf);
|
||||
return str;
|
||||
}
|
||||
|
||||
JSString *
|
||||
js_NewDependentString(JSContext *cx, JSString *base, size_t start,
|
||||
size_t length)
|
||||
|
@ -2922,18 +2942,18 @@ js_ValueToString(JSContext *cx, jsval v)
|
|||
}
|
||||
|
||||
static inline JSBool
|
||||
pushAtom(JSAtom *atom, JSTempVector<jschar> &buf)
|
||||
pushAtom(JSAtom *atom, JSCharVector &buf)
|
||||
{
|
||||
JSString *str = ATOM_TO_STRING(atom);
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
str->getCharsAndLength(chars, length);
|
||||
return buf.pushBack(chars, chars + length);
|
||||
return buf.append(chars, length);
|
||||
}
|
||||
|
||||
/* This function implements E-262-3 section 9.8, toString. */
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_ValueToStringBuffer(JSContext *cx, jsval v, JSTempVector<jschar> &buf)
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
{
|
||||
if (!JSVAL_IS_PRIMITIVE(v) &&
|
||||
!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_STRING, &v)) {
|
||||
|
@ -2945,12 +2965,12 @@ js_ValueToStringBuffer(JSContext *cx, jsval v, JSTempVector<jschar> &buf)
|
|||
const jschar *chars;
|
||||
size_t length;
|
||||
str->getCharsAndLength(chars, length);
|
||||
return buf.pushBack(chars, chars + length);
|
||||
return buf.append(chars, length);
|
||||
}
|
||||
if (JSVAL_IS_NUMBER(v))
|
||||
return js_NumberValueToStringBuffer(cx, v, buf);
|
||||
return js_NumberValueToCharBuffer(cx, v, buf);
|
||||
if (JSVAL_IS_BOOLEAN(v))
|
||||
return js_BooleanToStringBuffer(cx, JSVAL_TO_BOOLEAN(v), buf);
|
||||
return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), buf);
|
||||
if (JSVAL_IS_NULL(v))
|
||||
return pushAtom(cx->runtime->atomState.nullAtom, buf);
|
||||
JS_ASSERT(JSVAL_IS_VOID(v));
|
||||
|
|
|
@ -574,6 +574,14 @@ extern const char js_encodeURIComponent_str[];
|
|||
extern JSString *
|
||||
js_NewString(JSContext *cx, jschar *chars, size_t length);
|
||||
|
||||
/*
|
||||
* GC-allocate a string descriptor and steal the char buffer held by |cb|.
|
||||
* This function takes responsibility for adding the terminating '\0' required
|
||||
* by js_NewString.
|
||||
*/
|
||||
extern JSString *
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb);
|
||||
|
||||
extern JSString *
|
||||
js_NewDependentString(JSContext *cx, JSString *base, size_t start,
|
||||
size_t length);
|
||||
|
@ -608,12 +616,12 @@ extern JS_FRIEND_API(JSString *)
|
|||
js_ValueToString(JSContext *cx, jsval v);
|
||||
|
||||
/*
|
||||
* This function implements E-262-3 section 9.8, toString. Convert the given
|
||||
* value to a string of jschars appended to the given buffer. On error, the
|
||||
* This function implements E-262-3 section 9.8, toString. Convert the given
|
||||
* value to a string of jschars appended to the given buffer. On error, the
|
||||
* passed buffer may have partial results appended.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_ValueToStringBuffer(JSContext *, jsval, JSTempVector<jschar> &);
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
|
||||
|
||||
/*
|
||||
* Convert a value to its source expression, returning null after reporting
|
||||
|
|
|
@ -40,29 +40,74 @@
|
|||
#ifndef jsvector_h_
|
||||
#define jsvector_h_
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
|
||||
#include "jsbit.h"
|
||||
|
||||
/* Library of template meta-programs for use in the C++ JS data-structures. */
|
||||
namespace JSUtils {
|
||||
|
||||
/* Statically compute min/max. */
|
||||
template <size_t i, size_t j> struct min {
|
||||
static const size_t result = i < j ? i : j;
|
||||
};
|
||||
template <size_t i, size_t j> struct max {
|
||||
static const size_t result = i > j ? i : j;
|
||||
};
|
||||
|
||||
/* Statically compute floor(log2(i)). */
|
||||
template <size_t i> struct FloorLog2 {
|
||||
static const size_t result = 1 + FloorLog2<i / 2>::result;
|
||||
};
|
||||
template <> struct FloorLog2<0> { /* Error */ };
|
||||
template <> struct FloorLog2<1> { static const size_t result = 0; };
|
||||
|
||||
/* Statically compute ceiling(log2(i)). */
|
||||
template <size_t i> struct CeilingLog2 {
|
||||
static const size_t result = FloorLog2<2 * i - 1>::result;
|
||||
};
|
||||
|
||||
/* Statically compute the number of bits in the given unsigned type. */
|
||||
template <class T> struct BitSize {
|
||||
static const size_t result = sizeof(T) * JS_BITS_PER_BYTE;
|
||||
};
|
||||
|
||||
/*
|
||||
* Traits class for identifying POD types. Until C++0x, there is no automatic
|
||||
* For the unsigned integral type size_t, compute a mask M for N such that
|
||||
* for all X, !(X & M) implies X * N will not overflow (w.r.t size_t)
|
||||
*/
|
||||
template <size_t N> struct MulOverflowMask {
|
||||
static const size_t result =
|
||||
~((1u << (BitSize<size_t>::result - CeilingLog2<N>::result)) - 1);
|
||||
};
|
||||
template <> struct MulOverflowMask<0> { /* Error */ };
|
||||
template <> struct MulOverflowMask<1> { static const size_t result = 0; };
|
||||
|
||||
/*
|
||||
* Traits class for identifying POD types. Until C++0x, there is no automatic
|
||||
* way to detect PODs, so for the moment it is done manually.
|
||||
*/
|
||||
template <class T> struct IsPodType { static const bool result = false; };
|
||||
template <> struct IsPodType<char> { static const bool result = true; };
|
||||
template <> struct IsPodType<int> { static const bool result = true; };
|
||||
template <> struct IsPodType<short> { static const bool result = true; };
|
||||
template <> struct IsPodType<long> { static const bool result = true; };
|
||||
template <> struct IsPodType<float> { static const bool result = true; };
|
||||
template <> struct IsPodType<double> { static const bool result = true; };
|
||||
template <> struct IsPodType<jschar> { static const bool result = true; };
|
||||
template <class T> struct IsPodType { static const bool result = false; };
|
||||
template <> struct IsPodType<char> { static const bool result = true; };
|
||||
template <> struct IsPodType<signed char> { static const bool result = true; };
|
||||
template <> struct IsPodType<unsigned char> { static const bool result = true; };
|
||||
template <> struct IsPodType<short> { static const bool result = true; };
|
||||
template <> struct IsPodType<unsigned short> { static const bool result = true; };
|
||||
template <> struct IsPodType<int> { static const bool result = true; };
|
||||
template <> struct IsPodType<unsigned int> { static const bool result = true; };
|
||||
template <> struct IsPodType<long> { static const bool result = true; };
|
||||
template <> struct IsPodType<unsigned long> { static const bool result = true; };
|
||||
template <> struct IsPodType<float> { static const bool result = true; };
|
||||
template <> struct IsPodType<double> { static const bool result = true; };
|
||||
|
||||
} // end namespace JSUtils
|
||||
|
||||
/*
|
||||
* This template class provides a default implementation for vector operations
|
||||
* when the element type is not known to be a POD, as judged by IsPodType.
|
||||
*/
|
||||
template <class T, bool IsPod>
|
||||
template <class T, size_t N, bool IsPod>
|
||||
struct JSTempVectorImpl
|
||||
{
|
||||
/* Destroys constructed objects in the range [begin, end). */
|
||||
|
@ -82,29 +127,38 @@ struct JSTempVectorImpl
|
|||
* [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend).
|
||||
*/
|
||||
template <class U>
|
||||
static inline void copyInitialize(T *dst, const U *srcbeg, const U *srcend) {
|
||||
static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) {
|
||||
for (const U *p = srcbeg; p != srcend; ++p, ++dst)
|
||||
new(dst) T(*p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Grows the given buffer to have capacity newcap, preserving the objects
|
||||
* constructed in the range [begin, end) and updating vec.
|
||||
* Copy-constructs objects in the uninitialized range [dst, dst+n) from the
|
||||
* same object u.
|
||||
*/
|
||||
static inline bool growTo(JSTempVector<T> &vec, size_t newcap) {
|
||||
size_t bytes = sizeof(T) * newcap;
|
||||
T *newbuf = reinterpret_cast<T *>(malloc(bytes));
|
||||
if (!newbuf) {
|
||||
js_ReportOutOfMemory(vec.mCx);
|
||||
template <class U>
|
||||
static inline void copyConstructN(T *dst, size_t n, const U &u) {
|
||||
for (T *end = dst + n; dst != end; ++dst)
|
||||
new(dst) T(u);
|
||||
}
|
||||
|
||||
/*
|
||||
* Grows the given buffer to have capacity newcap, preserving the objects
|
||||
* constructed in the range [begin, end) and updating v. Assumes that (1)
|
||||
* newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will
|
||||
* not overflow.
|
||||
*/
|
||||
static inline bool growTo(JSTempVector<T> &v, size_t newcap) {
|
||||
T *newbuf = reinterpret_cast<T *>(v.mCx->malloc(newcap * sizeof(T)));
|
||||
if (!newbuf)
|
||||
return false;
|
||||
}
|
||||
for (T *dst = newbuf, *src = vec.mBegin; src != vec.mEnd; ++dst, ++src)
|
||||
for (T *dst = newbuf, *src = v.heapBegin(); src != v.heapEnd(); ++dst, ++src)
|
||||
new(dst) T(*src);
|
||||
JSTempVectorImpl::destroy(vec.mBegin, vec.mEnd);
|
||||
free(vec.mBegin);
|
||||
vec.mEnd = newbuf + (vec.mEnd - vec.mBegin);
|
||||
vec.mBegin = newbuf;
|
||||
vec.mCapacity = newbuf + newcap;
|
||||
JSTempVectorImpl::destroy(v.heapBegin(), v.heapEnd());
|
||||
v.mCx->free(v.heapBegin());
|
||||
v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin());
|
||||
v.heapBegin() = newbuf;
|
||||
v.heapCapacity() = newcap;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -114,33 +168,51 @@ struct JSTempVectorImpl
|
|||
* vector operations when the element type is known to be a POD, as judged by
|
||||
* IsPodType.
|
||||
*/
|
||||
template <class T>
|
||||
struct JSTempVectorImpl<T, true>
|
||||
template <class T, size_t N>
|
||||
struct JSTempVectorImpl<T, N, true>
|
||||
{
|
||||
static inline void destroy(T *, T *) {}
|
||||
|
||||
static inline void initialize(T *begin, T *end) {
|
||||
//memset(begin, 0, sizeof(T) * (end-begin)); //SLOWER
|
||||
/*
|
||||
* You would think that memset would be a big win (or even break even)
|
||||
* when we know T is a POD. But currently it's not. This is probably
|
||||
* because |append| tends to be given small ranges and memset requires
|
||||
* a function call that doesn't get inlined.
|
||||
*
|
||||
* memset(begin, 0, sizeof(T) * (end-begin));
|
||||
*/
|
||||
for (T *p = begin; p != end; ++p)
|
||||
*p = 0;
|
||||
new(p) T();
|
||||
}
|
||||
|
||||
static inline void copyInitialize(T *dst, const T *srcbeg, const T *srcend) {
|
||||
//memcpy(dst, srcbeg, sizeof(T) * (srcend-srcbeg)); //SLOWER
|
||||
for (const T *p = srcbeg; p != srcend; ++p, ++dst)
|
||||
template <class U>
|
||||
static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) {
|
||||
/*
|
||||
* See above memset comment. Also, notice that copyConstruct is
|
||||
* currently templated (T != U), so memcpy won't work without
|
||||
* requiring T == U.
|
||||
*
|
||||
* memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg));
|
||||
*/
|
||||
for (const U *p = srcbeg; p != srcend; ++p, ++dst)
|
||||
*dst = *p;
|
||||
}
|
||||
|
||||
static inline bool growTo(JSTempVector<T> &vec, size_t newcap) {
|
||||
static inline void copyConstructN(T *dst, size_t n, const T &t) {
|
||||
for (T *end = dst + n; dst != end; ++dst)
|
||||
*dst = t;
|
||||
}
|
||||
|
||||
static inline bool growTo(JSTempVector<T,N> &v, size_t newcap) {
|
||||
JS_ASSERT(!v.usingInlineStorage());
|
||||
size_t bytes = sizeof(T) * newcap;
|
||||
T *newbuf = reinterpret_cast<T *>(realloc(vec.mBegin, bytes));
|
||||
if (!newbuf) {
|
||||
js_ReportOutOfMemory(vec.mCx);
|
||||
T *newbuf = reinterpret_cast<T *>(v.mCx->realloc(v.heapBegin(), bytes));
|
||||
if (!newbuf)
|
||||
return false;
|
||||
}
|
||||
vec.mEnd = newbuf + (vec.mEnd - vec.mBegin);
|
||||
vec.mBegin = newbuf;
|
||||
vec.mCapacity = newbuf + newcap;
|
||||
v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin());
|
||||
v.heapBegin() = newbuf;
|
||||
v.heapCapacity() = newcap;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -148,19 +220,125 @@ struct JSTempVectorImpl<T, true>
|
|||
/*
|
||||
* JS-friendly, STL-like container providing a short-lived, dynamic buffer.
|
||||
* JSTempVector calls the constructors/destructors of all elements stored in
|
||||
* its internal buffer, so non-PODs may be safely used.
|
||||
* its internal buffer, so non-PODs may be safely used. Additionally,
|
||||
* JSTempVector stores the first few elements in-place in its member data
|
||||
* before resorting to dynamic allocation. The minimum number of elements may
|
||||
* be specified by the parameter N.
|
||||
*
|
||||
* T requirements:
|
||||
* - default and copy constructible, assignable, destructible
|
||||
* - operations do not throw
|
||||
*
|
||||
* N.B: JSTempVector is not reentrant: T member functions called during
|
||||
* JSTempVector member functions must not call back into the same
|
||||
* JSTempVector.
|
||||
* JSTempVector member functions must not call back into the same object.
|
||||
*/
|
||||
template <class T>
|
||||
template <class T, size_t N>
|
||||
class JSTempVector
|
||||
{
|
||||
/* utilities */
|
||||
|
||||
typedef JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::result> Impl;
|
||||
friend struct JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::result>;
|
||||
|
||||
bool growHeapCapacityTo(size_t minCapacity);
|
||||
bool convertToHeapStorage(size_t minCapacity);
|
||||
|
||||
/* magic constants */
|
||||
|
||||
static const int sMaxInlineBytes = 1024;
|
||||
|
||||
/* compute constants */
|
||||
|
||||
/*
|
||||
* Pointers to the heap-allocated buffer. Only [heapBegin(), heapEnd())
|
||||
* hold valid constructed T objects. The range [heapEnd(), heapBegin() +
|
||||
* heapCapacity()) holds uninitialized memory.
|
||||
*/
|
||||
struct BufferPtrs {
|
||||
T *mBegin, *mEnd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Since a vector either stores elements inline or in a heap-allocated
|
||||
* buffer, reuse the storage. mSizeOrCapacity serves as the union
|
||||
* discriminator. In inline mode (when elements are stored in u.mBuf),
|
||||
* mSizeOrCapacity holds the vector's size. In heap mode (when elements
|
||||
* are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mSizeOrCapacity holds the
|
||||
* vector's capacity.
|
||||
*/
|
||||
static const size_t sInlineCapacity =
|
||||
JSUtils::min<JSUtils::max<N, sizeof(BufferPtrs) / sizeof(T)>::result,
|
||||
sMaxInlineBytes / sizeof(T)>::result;
|
||||
|
||||
/* member data */
|
||||
|
||||
JSContext *mCx;
|
||||
|
||||
size_t mSizeOrCapacity;
|
||||
bool usingInlineStorage() const { return mSizeOrCapacity <= sInlineCapacity; }
|
||||
|
||||
union {
|
||||
BufferPtrs ptrs;
|
||||
char mBuf[sInlineCapacity * sizeof(T)];
|
||||
} u;
|
||||
|
||||
/* Only valid when usingInlineStorage() */
|
||||
size_t &inlineSize() {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
}
|
||||
|
||||
size_t inlineSize() const {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
}
|
||||
|
||||
T *inlineBegin() const {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return (T *)u.mBuf;
|
||||
}
|
||||
|
||||
T *inlineEnd() const {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return ((T *)u.mBuf) + mSizeOrCapacity;
|
||||
}
|
||||
|
||||
/* Only valid when !usingInlineStorage() */
|
||||
size_t heapSize() {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mEnd - u.ptrs.mBegin;
|
||||
}
|
||||
|
||||
size_t &heapCapacity() {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
}
|
||||
|
||||
T *&heapBegin() {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mBegin;
|
||||
}
|
||||
|
||||
T *&heapEnd() {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mEnd;
|
||||
}
|
||||
|
||||
size_t heapCapacity() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
}
|
||||
|
||||
T *const &heapBegin() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mBegin;
|
||||
}
|
||||
|
||||
T *const &heapEnd() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mEnd;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mInProgress;
|
||||
#endif
|
||||
|
@ -184,77 +362,93 @@ class JSTempVector
|
|||
}
|
||||
};
|
||||
|
||||
public:
|
||||
JSTempVector(JSContext *cx)
|
||||
:
|
||||
#ifdef DEBUG
|
||||
mInProgress(false),
|
||||
#endif
|
||||
mCx(cx), mBegin(0), mEnd(0), mCapacity(0)
|
||||
{}
|
||||
~JSTempVector();
|
||||
|
||||
JSTempVector(const JSTempVector &);
|
||||
JSTempVector &operator=(const JSTempVector &);
|
||||
|
||||
public:
|
||||
JSTempVector(JSContext *cx)
|
||||
: mCx(cx), mSizeOrCapacity(0)
|
||||
#ifdef DEBUG
|
||||
, mInProgress(false)
|
||||
#endif
|
||||
{}
|
||||
~JSTempVector();
|
||||
|
||||
/* accessors */
|
||||
|
||||
size_t size() const { return mEnd - mBegin; }
|
||||
size_t capacity() const { return mCapacity - mBegin; }
|
||||
bool empty() const { return mBegin == mEnd; }
|
||||
|
||||
T &operator[](size_t i) {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
return mBegin[i];
|
||||
size_t size() const {
|
||||
return usingInlineStorage() ? inlineSize() : (heapEnd() - heapBegin());
|
||||
}
|
||||
|
||||
const T &operator[](size_t i) const {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
return mBegin[i];
|
||||
bool empty() const {
|
||||
return usingInlineStorage() ? inlineSize() == 0 : (heapBegin() == heapEnd());
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return usingInlineStorage() ? sInlineCapacity : heapCapacity();
|
||||
}
|
||||
|
||||
T *begin() {
|
||||
JS_ASSERT(!mInProgress);
|
||||
return mBegin;
|
||||
return usingInlineStorage() ? inlineBegin() : heapBegin();
|
||||
}
|
||||
|
||||
const T *begin() const {
|
||||
JS_ASSERT(!mInProgress);
|
||||
return mBegin;
|
||||
return usingInlineStorage() ? inlineBegin() : heapBegin();
|
||||
}
|
||||
|
||||
T *end() {
|
||||
JS_ASSERT(!mInProgress);
|
||||
return mEnd;
|
||||
return usingInlineStorage() ? inlineEnd() : heapEnd();
|
||||
}
|
||||
|
||||
const T *end() const {
|
||||
JS_ASSERT(!mInProgress);
|
||||
return mEnd;
|
||||
return usingInlineStorage() ? inlineEnd() : heapEnd();
|
||||
}
|
||||
|
||||
T &operator[](size_t i) {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
return begin()[i];
|
||||
}
|
||||
|
||||
const T &operator[](size_t i) const {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
return begin()[i];
|
||||
}
|
||||
|
||||
T &back() {
|
||||
JS_ASSERT(!mInProgress);
|
||||
return *(mEnd - 1);
|
||||
JS_ASSERT(!mInProgress && !empty());
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
const T &back() const {
|
||||
JS_ASSERT(!mInProgress && !empty());
|
||||
return *(mEnd - 1);
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
/* mutators */
|
||||
|
||||
bool reserve(size_t);
|
||||
bool growBy(size_t);
|
||||
bool reserve(size_t capacity);
|
||||
bool resize(size_t newSize);
|
||||
void shrinkBy(size_t incr);
|
||||
bool growBy(size_t incr);
|
||||
void clear();
|
||||
|
||||
bool pushBack(const T &);
|
||||
template <class U> bool pushBack(const U *begin, const U *end);
|
||||
bool append(const T &t);
|
||||
bool appendN(const T &t, size_t n);
|
||||
template <class U> bool append(const U *begin, const U *end);
|
||||
template <class U> bool append(const U *begin, size_t length);
|
||||
|
||||
void popBack();
|
||||
|
||||
/*
|
||||
* Transfers ownership of the internal buffer used by JSTempVector to the
|
||||
* caller. After this call, the JSTempVector is empty.
|
||||
* caller. After this call, the JSTempVector is empty. Since the returned
|
||||
* buffer may need to be allocated (if the elements are currently
|
||||
* stored in-place), the call can fail, returning NULL.
|
||||
*
|
||||
* N.B. Although a T*, only the range [0, size()) is constructed.
|
||||
*/
|
||||
T *extractRawBuffer();
|
||||
|
@ -264,146 +458,340 @@ class JSTempVector
|
|||
* N.B. This call assumes that there are no uninitialized elements in the
|
||||
* passed array.
|
||||
*/
|
||||
void replaceRawBuffer(T *, size_t length);
|
||||
|
||||
private:
|
||||
typedef JSTempVectorImpl<T, IsPodType<T>::result> Impl;
|
||||
friend struct JSTempVectorImpl<T, IsPodType<T>::result>;
|
||||
|
||||
static const int sGrowthFactor = 3;
|
||||
|
||||
bool checkOverflow(size_t newval, size_t oldval, size_t diff) const;
|
||||
|
||||
JSContext *mCx;
|
||||
T *mBegin, *mEnd, *mCapacity;
|
||||
void replaceRawBuffer(T *p, size_t length);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
JSTempVector<T>::~JSTempVector()
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
Impl::destroy(mBegin, mEnd);
|
||||
free(mBegin);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
JSTempVector<T>::reserve(size_t newsz)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
size_t oldcap = capacity();
|
||||
if (newsz > oldcap) {
|
||||
size_t diff = newsz - oldcap;
|
||||
size_t newcap = diff + oldcap * sGrowthFactor;
|
||||
return checkOverflow(newcap, oldcap, diff) &&
|
||||
Impl::growTo(*this, newcap);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
JSTempVector<T>::growBy(size_t amount)
|
||||
{
|
||||
/* grow if needed */
|
||||
size_t oldsize = size(), newsize = oldsize + amount;
|
||||
if (!checkOverflow(newsize, oldsize, amount) ||
|
||||
(newsize > capacity() && !reserve(newsize)))
|
||||
return false;
|
||||
|
||||
/* initialize new elements */
|
||||
ReentrancyGuard g(*this);
|
||||
JS_ASSERT(mCapacity - (mBegin + newsize) >= 0);
|
||||
T *newend = mBegin + newsize;
|
||||
Impl::initialize(mEnd, newend);
|
||||
mEnd = newend;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void
|
||||
JSTempVector<T>::clear()
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
Impl::destroy(mBegin, mEnd);
|
||||
mEnd = mBegin;
|
||||
}
|
||||
/* Helper functions */
|
||||
|
||||
/*
|
||||
* Check for overflow of an increased size or capacity (generically, 'value').
|
||||
* 'diff' is how much greater newval should be compared to oldval.
|
||||
* This helper function is specialized for appending the characters of a string
|
||||
* literal to a vector. This could not be done generically since one must take
|
||||
* care not to append the terminating '\0'.
|
||||
*/
|
||||
template <class T>
|
||||
inline bool
|
||||
JSTempVector<T>::checkOverflow(size_t newval, size_t oldval, size_t diff) const
|
||||
template <class T, size_t N, size_t ArraySize>
|
||||
bool
|
||||
js_AppendLiteral(JSTempVector<T,N> &v, const char (&array)[ArraySize])
|
||||
{
|
||||
size_t newbytes = newval * sizeof(T),
|
||||
oldbytes = oldval * sizeof(T),
|
||||
diffbytes = diff * sizeof(T);
|
||||
bool ok = newbytes >= oldbytes && (newbytes - oldbytes) >= diffbytes;
|
||||
if (!ok)
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
return ok;
|
||||
return v.append(array, array + ArraySize - 1);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool
|
||||
JSTempVector<T>::pushBack(const T &t)
|
||||
|
||||
/* JSTempVector Implementation */
|
||||
|
||||
template <class T, size_t N>
|
||||
inline
|
||||
JSTempVector<T,N>::~JSTempVector()
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (mEnd == mCapacity) {
|
||||
/* reallocate, doubling size */
|
||||
size_t oldcap = capacity();
|
||||
size_t newcap = empty() ? 1 : oldcap * sGrowthFactor;
|
||||
if (!checkOverflow(newcap, oldcap, 1) ||
|
||||
!Impl::growTo(*this, newcap))
|
||||
return false;
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
} else {
|
||||
Impl::destroy(heapBegin(), heapEnd());
|
||||
mCx->free(heapBegin());
|
||||
}
|
||||
JS_ASSERT(mEnd != mCapacity);
|
||||
new(mEnd++) T(t);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::growHeapCapacityTo(size_t mincap)
|
||||
{
|
||||
JS_ASSERT(mincap > heapCapacity());
|
||||
|
||||
/* Check for overflow in both CEILING_LOG2 and growTo. */
|
||||
if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) {
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Round up to next power of 2. */
|
||||
size_t newcap;
|
||||
JS_CEILING_LOG2(newcap, mincap);
|
||||
JS_ASSERT(newcap < JSUtils::BitSize<size_t>::result);
|
||||
newcap = size_t(1) << newcap;
|
||||
|
||||
return Impl::growTo(*this, newcap);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::convertToHeapStorage(size_t mincap)
|
||||
{
|
||||
JS_ASSERT(mincap > sInlineCapacity);
|
||||
|
||||
/* Check for overflow in both CEILING_LOG2 and malloc. */
|
||||
if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) {
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Round up to next power of 2. */
|
||||
size_t newcap;
|
||||
JS_CEILING_LOG2(newcap, mincap);
|
||||
JS_ASSERT(newcap < 32);
|
||||
newcap = 1u << newcap;
|
||||
|
||||
/* Allocate buffer. */
|
||||
T *newbuf = reinterpret_cast<T *>(mCx->malloc(newcap * sizeof(T)));
|
||||
if (!newbuf)
|
||||
return false;
|
||||
|
||||
/* Copy inline elements into heap buffer. */
|
||||
size_t size = inlineEnd() - inlineBegin();
|
||||
Impl::copyConstruct(newbuf, inlineBegin(), inlineEnd());
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
|
||||
/* Switch in heap buffer. */
|
||||
mSizeOrCapacity = newcap; /* marks us as !usingInlineStorage() */
|
||||
heapBegin() = newbuf;
|
||||
heapEnd() = newbuf + size;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::reserve(size_t request)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
if (request > sInlineCapacity)
|
||||
return convertToHeapStorage(request);
|
||||
} else {
|
||||
if (request > heapCapacity())
|
||||
return growHeapCapacityTo(request);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline void
|
||||
JSTempVector<T,N>::shrinkBy(size_t incr)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
JS_ASSERT(incr <= size());
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineEnd() - incr, inlineEnd());
|
||||
inlineSize() -= incr;
|
||||
} else {
|
||||
Impl::destroy(heapEnd() - incr, heapEnd());
|
||||
heapEnd() -= incr;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::growBy(size_t incr)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
if (incr <= freespace) {
|
||||
T *newend = inlineEnd() + incr;
|
||||
Impl::initialize(inlineEnd(), newend);
|
||||
inlineSize() += incr;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + incr))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* grow if needed */
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
if (incr > freespace) {
|
||||
if (!growHeapCapacityTo(heapSize() + incr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapCapacity() - heapSize() >= incr);
|
||||
T *newend = heapEnd() + incr;
|
||||
Impl::initialize(heapEnd(), newend);
|
||||
heapEnd() = newend;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::resize(size_t newsize)
|
||||
{
|
||||
size_t cursize = size();
|
||||
if (newsize > cursize)
|
||||
return growBy(newsize - cursize);
|
||||
shrinkBy(cursize - newsize);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline void
|
||||
JSTempVector<T,N>::clear()
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
}
|
||||
else {
|
||||
Impl::destroy(heapBegin(), heapEnd());
|
||||
heapEnd() = heapBegin();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::append(const T &t)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
if (inlineSize() < sInlineCapacity) {
|
||||
new(inlineEnd()) T(t);
|
||||
++inlineSize();
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + 1))
|
||||
return false;
|
||||
} else {
|
||||
if (heapSize() == heapCapacity() && !growHeapCapacityTo(heapSize() + 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= 1);
|
||||
new(heapEnd()++) T(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::appendN(const T &t, size_t needed)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
if (needed <= freespace) {
|
||||
Impl::copyConstructN(inlineEnd(), needed, t);
|
||||
inlineSize() += needed;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + needed))
|
||||
return false;
|
||||
} else {
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
if (needed > freespace && !growHeapCapacityTo(heapSize() + needed))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed);
|
||||
Impl::copyConstructN(heapEnd(), needed, t);
|
||||
heapEnd() += needed;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
template <class U>
|
||||
inline bool
|
||||
JSTempVector<T>::pushBack(const U *begin, const U *end)
|
||||
JSTempVector<T,N>::append(const U *insBegin, const U *insEnd)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
size_t space = mCapacity - mEnd, needed = end - begin;
|
||||
if (space < needed) {
|
||||
/* reallocate, doubling size */
|
||||
size_t oldcap = capacity();
|
||||
size_t newcap = empty() ? needed : (needed + oldcap * sGrowthFactor);
|
||||
if (!checkOverflow(newcap, oldcap, needed) ||
|
||||
!Impl::growTo(*this, newcap))
|
||||
size_t needed = insEnd - insBegin;
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
if (needed <= freespace) {
|
||||
Impl::copyConstruct(inlineEnd(), insBegin, insEnd);
|
||||
inlineSize() += needed;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + needed))
|
||||
return false;
|
||||
} else {
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
if (needed > freespace && !growHeapCapacityTo(heapSize() + needed))
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT((mCapacity - mEnd) >= (end - begin));
|
||||
Impl::copyInitialize(mEnd, begin, end);
|
||||
mEnd += needed;
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed);
|
||||
Impl::copyConstruct(heapEnd(), insBegin, insEnd);
|
||||
heapEnd() += needed;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T *
|
||||
JSTempVector<T>::extractRawBuffer()
|
||||
template <class T, size_t N>
|
||||
template <class U>
|
||||
inline bool
|
||||
JSTempVector<T,N>::append(const U *insBegin, size_t length)
|
||||
{
|
||||
T *ret = mBegin;
|
||||
mBegin = mEnd = mCapacity = 0;
|
||||
return this->append(insBegin, insBegin + length);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline void
|
||||
JSTempVector<T,N>::popBack()
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
JS_ASSERT(!empty());
|
||||
if (usingInlineStorage()) {
|
||||
--inlineSize();
|
||||
inlineEnd()->~T();
|
||||
} else {
|
||||
--heapEnd();
|
||||
heapEnd()->~T();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
inline T *
|
||||
JSTempVector<T,N>::extractRawBuffer()
|
||||
{
|
||||
if (usingInlineStorage()) {
|
||||
T *ret = reinterpret_cast<T *>(mCx->malloc(inlineSize() * sizeof(T)));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
Impl::copyConstruct(ret, inlineBegin(), inlineEnd());
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
T *ret = heapBegin();
|
||||
mSizeOrCapacity = 0; /* marks us as !usingInlineStorage() */
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class T, size_t N>
|
||||
inline void
|
||||
JSTempVector<T>::replaceRawBuffer(T *p, size_t length)
|
||||
JSTempVector<T,N>::replaceRawBuffer(T *p, size_t length)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
Impl::destroy(mBegin, mEnd);
|
||||
free(mBegin);
|
||||
mBegin = p;
|
||||
mCapacity = mEnd = mBegin + length;
|
||||
|
||||
/* Destroy what we have. */
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
} else {
|
||||
Impl::destroy(heapBegin(), heapEnd());
|
||||
mCx->free(heapBegin());
|
||||
}
|
||||
|
||||
/* Take in the new buffer. */
|
||||
if (length <= sInlineCapacity) {
|
||||
/*
|
||||
* (mSizeOrCapacity <= sInlineCapacity) means inline storage, so we MUST
|
||||
* use inline storage, even though p might otherwise be acceptable.
|
||||
*/
|
||||
mSizeOrCapacity = length; /* marks us as usingInlineStorage() */
|
||||
Impl::copyConstruct(inlineBegin(), p, p + length);
|
||||
Impl::destroy(p, p + length);
|
||||
mCx->free(p);
|
||||
} else {
|
||||
mSizeOrCapacity = length; /* marks us as !usingInlineStorage() */
|
||||
heapBegin() = p;
|
||||
heapEnd() = heapBegin() + length;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* jsvector_h_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче