gecko/js/src/jsstrinlines.h

331 lines
8.9 KiB
C++

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** 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 ***** */
#ifndef jsstrinlines_h___
#define jsstrinlines_h___
#include "jsatom.h"
#include "jsstr.h"
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
namespace js {
static inline bool
CheckStringLength(JSContext *cx, size_t length)
{
if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
if (JS_ON_TRACE(cx)) {
/*
* If we can't leave the trace, signal OOM condition, otherwise
* exit from trace before throwing.
*/
if (!CanLeaveTrace(cx))
return NULL;
LeaveTrace(cx);
}
js_ReportAllocationOverflow(cx);
return false;
}
return true;
}
/*
* String builder that eagerly checks for over-allocation past the maximum
* string length.
*
* Note: over-allocation is not checked for when using the infallible
* |replaceRawBuffer|, so the implementation of |finishString| also must check
* for over-allocation.
*/
class StringBuffer
{
/* cb's buffer is taken by the new string so use ContextAllocPolicy. */
typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
CharBuffer cb;
static inline bool checkLength(JSContext *cx, size_t length);
inline bool checkLength(size_t length);
JSContext *context() const { return cb.allocPolicy().context(); }
jschar *extractWellSized();
public:
explicit inline StringBuffer(JSContext *cx);
bool reserve(size_t len);
bool resize(size_t len);
bool append(const jschar c);
bool append(const jschar *chars, size_t len);
bool append(const jschar *begin, const jschar *end);
bool append(JSString *str);
bool append(JSAtom *atom);
bool appendN(const jschar c, size_t n);
bool appendInflated(const char *cstr, size_t len);
/* Infallible variants usable when the corresponding space is reserved. */
void infallibleAppend(const jschar c) {
cb.infallibleAppend(c);
}
void infallibleAppend(const jschar *chars, size_t len) {
cb.infallibleAppend(chars, len);
}
void infallibleAppend(const jschar *begin, const jschar *end) {
cb.infallibleAppend(begin, end);
}
void infallibleAppendN(const jschar c, size_t n) {
cb.infallibleAppendN(c, n);
}
JSAtom *atomize(uintN flags = 0);
static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, uintN flags = 0);
static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, uintN flags = 0);
void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); }
jschar *begin() { return cb.begin(); }
jschar *end() { return cb.end(); }
const jschar *begin() const { return cb.begin(); }
const jschar *end() const { return cb.end(); }
bool empty() const { return cb.empty(); }
inline jsint length() const;
/*
* Creates a string from the characters in this buffer, then (regardless
* whether string creation succeeded or failed) empties the buffer.
*/
JSFixedString *finishString();
/* Identical to finishString() except that an atom is created. */
JSAtom *finishAtom();
template <size_t ArrayLength>
bool append(const char (&array)[ArrayLength]) {
return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
}
};
inline
StringBuffer::StringBuffer(JSContext *cx)
: cb(cx)
{}
inline bool
StringBuffer::reserve(size_t len)
{
if (!checkLength(len))
return false;
return cb.reserve(len);
}
inline bool
StringBuffer::resize(size_t len)
{
if (!checkLength(len))
return false;
return cb.resize(len);
}
inline bool
StringBuffer::append(const jschar c)
{
if (!checkLength(cb.length() + 1))
return false;
return cb.append(c);
}
inline bool
StringBuffer::append(const jschar *chars, size_t len)
{
if (!checkLength(cb.length() + len))
return false;
return cb.append(chars, len);
}
inline bool
StringBuffer::append(const jschar *begin, const jschar *end)
{
if (!checkLength(cb.length() + (end - begin)))
return false;
return cb.append(begin, end);
}
inline bool
StringBuffer::append(JSString *str)
{
JSLinearString *linear = str->ensureLinear(context());
if (!linear)
return false;
size_t strLen = linear->length();
if (!checkLength(cb.length() + strLen))
return false;
return cb.append(linear->chars(), strLen);
}
inline bool
StringBuffer::append(JSAtom *atom)
{
size_t strLen = atom->length();
if (!checkLength(cb.length() + strLen))
return false;
return cb.append(atom->chars(), strLen);
}
inline bool
StringBuffer::appendN(const jschar c, size_t n)
{
if (!checkLength(cb.length() + n))
return false;
return cb.appendN(c, n);
}
inline bool
StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
{
size_t lengthBefore = length();
if (!cb.growByUninitialized(cstrlen))
return false;
#if DEBUG
size_t oldcstrlen = cstrlen;
bool ok =
#endif
InflateStringToBuffer(context(), cstr, cstrlen, begin() + lengthBefore, &cstrlen);
JS_ASSERT(ok && oldcstrlen == cstrlen);
return true;
}
inline jsint
StringBuffer::length() const
{
JS_STATIC_ASSERT(jsint(JSString::MAX_LENGTH) == JSString::MAX_LENGTH);
JS_ASSERT(cb.length() <= JSString::MAX_LENGTH);
return jsint(cb.length());
}
inline bool
StringBuffer::checkLength(size_t length)
{
return CheckStringLength(context(), length);
}
extern bool
ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb);
inline bool
ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
{
if (v.isString())
return sb.append(v.toString());
return ValueToStringBufferSlow(cx, v, sb);
}
class RopeBuilder {
JSContext *cx;
JSString *res;
public:
RopeBuilder(JSContext *cx)
: cx(cx), res(cx->runtime->emptyString)
{}
inline bool append(JSString *str) {
res = js_ConcatStrings(cx, res, str);
return !!res;
}
inline JSString *result() {
return res;
}
};
class StringSegmentRange
{
/*
* If malloc() shows up in any profiles from this vector, we can add a new
* StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx.
*/
Vector<JSString *, 32> stack;
JSLinearString *cur;
bool settle(JSString *str) {
while (str->isRope()) {
JSRope &rope = str->asRope();
if (!stack.append(rope.rightChild()))
return false;
str = rope.leftChild();
}
cur = &str->asLinear();
return true;
}
public:
StringSegmentRange(JSContext *cx)
: stack(cx), cur(NULL)
{}
JS_WARN_UNUSED_RESULT bool init(JSString *str) {
JS_ASSERT(stack.empty());
return settle(str);
}
bool empty() const {
return cur == NULL;
}
JSLinearString *front() const {
JS_ASSERT(!cur->isRope());
return cur;
}
JS_WARN_UNUSED_RESULT bool popFront() {
JS_ASSERT(!empty());
if (stack.empty()) {
cur = NULL;
return true;
}
return settle(stack.popCopy());
}
};
} /* namespace js */
#endif /* jsstrinlines_h___ */