| /* -*- 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); |
| } |