/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * 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 static CharT* ExtractWellSized(ExclusiveContext* cx, Buffer& cb) { size_t capacity = cb.capacity(); size_t length = cb.length(); CharT* buf = cb.extractOrCopyRawBuffer(); 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(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(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(Move(twoByte)); return true; } template static JSFlatString* FinishStringFlat(ExclusiveContext* cx, StringBuffer& sb, Buffer& cb) { size_t len = sb.length(); if (!sb.append('\0')) return nullptr; ScopedJSFreePtr buf(ExtractWellSized(cx, cb)); if (!buf) return nullptr; JSFlatString* str = NewStringDontDeflate(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 (JSAtom* staticStr = cx->staticStrings().lookup(latin1Chars().begin(), len)) return staticStr; if (JSInlineString::lengthFits(len)) { mozilla::Range range(latin1Chars().begin(), len); return NewInlineString(cx, range); } } else { if (JSAtom* staticStr = cx->staticStrings().lookup(twoByteChars().begin(), len)) return staticStr; if (JSInlineString::lengthFits(len)) { mozilla::Range range(twoByteChars().begin(), len); return NewInlineString(cx, range); } } return isLatin1() ? FinishStringFlat(cx, *this, latin1Chars()) : FinishStringFlat(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_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SYMBOL_TO_STRING); return false; } if (v.isBigInt()) { RootedBigInt i(cx, v.toBigInt()); JSLinearString* str = BigInt::toString(cx, i, 10); if (!str) return false; return sb.append(str); } MOZ_ASSERT(v.isUndefined()); return sb.append(cx->names().undefined); }