Bug 639883 - Use JSString (not JSShortString) for inline really short inline strings (r=njn)

--HG--
extra : rebase_source : 5d18f67f841864064e032836014978cf51b52f5c
This commit is contained in:
Luke Wagner 2011-03-31 16:07:21 -07:00
parent 53aca4319a
commit 1913b99eb0
3 changed files with 153 additions and 81 deletions

View File

@ -3575,28 +3575,38 @@ js_NewString(JSContext *cx, jschar *chars, size_t length)
return JSFixedString::new_(cx, chars, length);
}
static JS_ALWAYS_INLINE JSShortString *
static JS_ALWAYS_INLINE JSFixedString *
NewShortString(JSContext *cx, const jschar *chars, size_t length)
{
/*
* Don't bother trying to find a static atom; measurement shows that not
* many get here (for one, Atomize is catching them).
*/
JS_ASSERT(JSShortString::lengthFits(length));
JSShortString *str = js_NewGCShortString(cx);
JSInlineString *str = JSInlineString::lengthFits(length)
? JSInlineString::new_(cx)
: JSShortString::new_(cx);
if (!str)
return NULL;
jschar *storage = str->init(length);
PodCopy(storage, chars, length);
storage[length] = 0;
return str;
}
static JSShortString *
static JSInlineString *
NewShortString(JSContext *cx, const char *chars, size_t length)
{
JS_ASSERT(JSShortString::lengthFits(length));
JSShortString *str = js_NewGCShortString(cx);
JSInlineString *str = JSInlineString::lengthFits(length)
? JSInlineString::new_(cx)
: JSShortString::new_(cx);
if (!str)
return NULL;
jschar *storage = str->init(length);
jschar *storage = str->init(length);
if (js_CStringsAreUTF8) {
#ifdef DEBUG
size_t oldLength = length;

View File

@ -77,8 +77,8 @@
* property via a flat string's "extensible" property.
*
* - To avoid allocating small char arrays, short strings can be stored inline
* in the string header. To increase the max size of such inline strings,
* double-wide string headers (JSShortString) can be used.
* in the string header (JSInlineString). To increase the max size of such
* inline strings, extra-large string headers can be used (JSShortString).
*
* - To avoid comparing O(n) string equality comparison, strings can be
* canonicalized to "atoms" (JSAtom) such that there is a single atom with a
@ -113,11 +113,15 @@
* |
* JSFixedString - / may have external pointers into char array
* | \ \
* | \ JSExternalString - / chars stored in header
* | \ JSExternalString - / char array memory managed by embedding
* | \
* | JSShortString - / chars stored in header
* | |
* JSAtom | - / string equality === pointer equality
* | JSInlineString - / chars stored in header
* | | \
* | | JSShortString - / header is fat
* | | |
* JSAtom | | - / string equality === pointer equality
* | \ | |
* | JSInlineAtom | - / atomized JSInlineString
* | \ |
* | JSShortAtom - / atomized JSShortString
* |
@ -149,7 +153,7 @@ class JSString : public js::gc::Cell
JSString *left; /* JSRope */
} u1;
union {
jschar inlineStorage[NUM_INLINE_CHARS]; /* JSShortString */
jschar inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|Short)String */
struct {
union {
JSLinearString *base; /* JSDependentString */
@ -187,6 +191,7 @@ class JSString : public js::gc::Cell
* JSFlatString xx00
* JSExtensibleString 1100
* JSFixedString xy00 where xy != 11
* JSInlineString 0100 and chars == inlineStorage
* JSShortString 0100 and in FINALIZE_SHORT_STRING arena
* JSExternalString 0100 and in FINALIZE_EXTERNAL_STRING arena
* JSAtom x000
@ -480,6 +485,63 @@ class JSFixedString : public JSFlatString
JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
class JSInlineString : public JSFixedString
{
static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
public:
static inline JSInlineString *new_(JSContext *cx);
inline jschar *init(size_t length);
inline void resetLength(size_t length);
static bool lengthFits(size_t length) {
return length <= MAX_INLINE_LENGTH;
}
};
JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
class JSShortString : public JSInlineString
{
/* This can be any value that is a multiple of sizeof(gc::FreeCell). */
static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
static void staticAsserts() {
JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % sizeof(js::gc::FreeCell) == 0);
JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
(sizeof(JSShortString) -
offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
}
jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
public:
static inline JSShortString *new_(JSContext *cx);
jschar *inlineStorageBeforeInit() {
return d.inlineStorage;
}
inline void initAtOffsetInBuffer(const jschar *chars, size_t length);
static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
INLINE_EXTENSION_CHARS
-1 /* null terminator */;
static bool lengthFits(size_t length) {
return length <= MAX_SHORT_LENGTH;
}
/* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
JS_ALWAYS_INLINE void finalize(JSContext *cx);
};
JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
class JSExternalString : public JSFixedString
{
static void staticAsserts() {
@ -520,59 +582,6 @@ class JSExternalString : public JSFixedString
JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
class JSShortString : public JSFixedString
{
/* This can be any value that is a multiple of sizeof(gc::FreeCell). */
static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
static void staticAsserts() {
JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % sizeof(js::gc::FreeCell) == 0);
JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
(sizeof(JSShortString) -
offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
}
jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
public:
jschar *inlineStorageBeforeInit() {
return d.inlineStorage;
}
jschar *init(size_t length) {
JS_ASSERT(lengthFits(length));
d.u1.chars = d.inlineStorage;
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
return d.inlineStorage;
}
void resetLength(size_t length) {
JS_ASSERT(lengthFits(length));
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
}
void initAtOffsetInBuffer(const jschar *chars, size_t length) {
JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
d.u1.chars = chars;
}
static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
INLINE_EXTENSION_CHARS
-1 /* null terminator */;
static inline bool lengthFits(size_t length) {
return length <= MAX_SHORT_LENGTH;
}
/* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
JS_ALWAYS_INLINE void finalize(JSContext *cx);
};
JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
class JSAtom : public JSFixedString
{
public:
@ -637,7 +646,17 @@ class JSAtom : public JSFixedString
JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
class JSShortAtom : public JSShortString /*, JSAtom */
class JSInlineAtom : public JSInlineString /*, JSAtom */
{
/*
* JSInlineAtom is not explicitly used and is only present for consistency.
* See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
*/
};
JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
class JSShortAtom : public JSShortString /*, JSInlineAtom */
{
/*
* JSShortAtom is not explicitly used and is only present for consistency.

View File

@ -338,6 +338,43 @@ JSFixedString::morphInternedStringIntoAtom()
return &asAtom();
}
JS_ALWAYS_INLINE JSInlineString *
JSInlineString::new_(JSContext *cx)
{
return (JSInlineString *)js_NewGCString(cx);
}
JS_ALWAYS_INLINE jschar *
JSInlineString::init(size_t length)
{
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
d.u1.chars = d.inlineStorage;
JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
return d.inlineStorage;
}
JS_ALWAYS_INLINE void
JSInlineString::resetLength(size_t length)
{
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
}
JS_ALWAYS_INLINE JSShortString *
JSShortString::new_(JSContext *cx)
{
return js_NewGCShortString(cx);
}
JS_ALWAYS_INLINE void
JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
{
JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
d.u1.chars = chars;
}
JS_ALWAYS_INLINE void
JSExternalString::init(const jschar *chars, size_t length, intN type)
{
@ -478,7 +515,13 @@ JSFlatString::finalize(JSRuntime *rt)
{
JS_ASSERT(!isShort());
rt->stringMemoryUsed -= length() * 2;
rt->free(const_cast<jschar *>(chars()));
/*
* This check depends on the fact that 'chars' is only initialized to the
* beginning of inlineStorage. E.g., this is not the case for short strings.
*/
if (chars() != d.inlineStorage)
rt->free_(const_cast<jschar *>(chars()));
}
inline void