blob: 31e7e15b4e99e302831fdeb9a73e033d27e77d33 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/StringBuffer.h"
#include "mozilla/Range.h"
#include "jsobjinlines.h"
#include "vm/String-inl.h"
using namespace js;
template <typename CharT, class Buffer>
static CharT*
ExtractWellSized(ExclusiveContext* cx, Buffer& cb)
{
size_t capacity = cb.capacity();
size_t length = cb.length();
CharT* buf = cb.extractRawBuffer();
if (!buf)
return nullptr;
/* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
MOZ_ASSERT(capacity >= length);
if (length > Buffer::sMaxInlineStorage && capacity - length > length / 4) {
CharT* tmp = cx->zone()->pod_realloc<CharT>(buf, capacity, length + 1);
if (!tmp) {
js_free(buf);
ReportOutOfMemory(cx);
return nullptr;
}
buf = tmp;
}
return buf;
}
char16_t*
StringBuffer::stealChars()
{
if (isLatin1() && !inflateChars())
return nullptr;
return ExtractWellSized<char16_t>(cx, twoByteChars());
}
bool
StringBuffer::inflateChars()
{
MOZ_ASSERT(isLatin1());
TwoByteCharBuffer twoByte(cx);
/*
* Note: we don't use Vector::capacity() because it always returns a
* value >= sInlineCapacity. Since Latin1CharBuffer::sInlineCapacity >
* TwoByteCharBuffer::sInlineCapacitychars, we'd always malloc here.
*/
size_t capacity = Max(reserved_, latin1Chars().length());
if (!twoByte.reserve(capacity))
return false;
twoByte.infallibleAppend(latin1Chars().begin(), latin1Chars().length());
cb.destroy();
cb.construct<TwoByteCharBuffer>(Move(twoByte));
return true;
}
template <typename CharT, class Buffer>
static JSFlatString*
FinishStringFlat(ExclusiveContext* cx, StringBuffer& sb, Buffer& cb)
{
size_t len = sb.length();
if (!sb.append('\0'))
return nullptr;
ScopedJSFreePtr<CharT> buf(ExtractWellSized<CharT>(cx, cb));
if (!buf)
return nullptr;
JSFlatString* str = NewStringDontDeflate<CanGC>(cx, buf.get(), len);
if (!str)
return nullptr;
/*
* The allocation was made on a TempAllocPolicy, so account for the string
* data on the string's zone.
*/
str->zone()->updateMallocCounter(sizeof(CharT) * len);
buf.forget();
return str;
}
JSFlatString*
StringBuffer::finishString()
{
size_t len = length();
if (len == 0)
return cx->names().empty;
if (!JSString::validateLength(cx, len))
return nullptr;
JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_TWO_BYTE < TwoByteCharBuffer::InlineLength);
JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_LATIN1 < Latin1CharBuffer::InlineLength);
if (isLatin1()) {
if (JSInlineString::lengthFits<Latin1Char>(len)) {
mozilla::Range<const Latin1Char> range(latin1Chars().begin(), len);
return NewInlineString<CanGC>(cx, range);
}
} else {
if (JSInlineString::lengthFits<char16_t>(len)) {
mozilla::Range<const char16_t> range(twoByteChars().begin(), len);
return NewInlineString<CanGC>(cx, range);
}
}
return isLatin1()
? FinishStringFlat<Latin1Char>(cx, *this, latin1Chars())
: FinishStringFlat<char16_t>(cx, *this, twoByteChars());
}
JSAtom*
StringBuffer::finishAtom()
{
size_t len = length();
if (len == 0)
return cx->names().empty;
if (isLatin1()) {
JSAtom* atom = AtomizeChars(cx, latin1Chars().begin(), len);
latin1Chars().clear();
return atom;
}
JSAtom* atom = AtomizeChars(cx, twoByteChars().begin(), len);
twoByteChars().clear();
return atom;
}
bool
js::ValueToStringBufferSlow(JSContext* cx, const Value& arg, StringBuffer& sb)
{
RootedValue v(cx, arg);
if (!ToPrimitive(cx, JSTYPE_STRING, &v))
return false;
if (v.isString())
return sb.append(v.toString());
if (v.isNumber())
return NumberValueToStringBuffer(cx, v, sb);
if (v.isBoolean())
return BooleanToStringBuffer(v.toBoolean(), sb);
if (v.isNull())
return sb.append(cx->names().null);
if (v.isSymbol()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SYMBOL_TO_STRING);
return false;
}
MOZ_ASSERT(v.isUndefined());
return sb.append(cx->names().undefined);
}