diff options
Diffstat (limited to 'js/src/vm/InlineCharBuffer-inl.h')
-rw-r--r-- | js/src/vm/InlineCharBuffer-inl.h | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/js/src/vm/InlineCharBuffer-inl.h b/js/src/vm/InlineCharBuffer-inl.h new file mode 100644 index 0000000000..ac006a0d98 --- /dev/null +++ b/js/src/vm/InlineCharBuffer-inl.h @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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/. */ + +#ifndef vm_InlineCharBuffer_inl_h +#define vm_InlineCharBuffer_inl_h + +#include "vm/StringType-inl.h" + +namespace js { + +template <typename CharT> +struct MaximumInlineLength; + +template <> +struct MaximumInlineLength<Latin1Char> { + static constexpr size_t value = JSFatInlineString::MAX_LENGTH_LATIN1; +}; + +template <> +struct MaximumInlineLength<char16_t> { + static constexpr size_t value = JSFatInlineString::MAX_LENGTH_TWO_BYTE; +}; + +// Character buffer class used for ToLowerCase and ToUpperCase operations, as +// well as other string operations where the final string length is known in +// advance. +// +// Case conversion operations normally return a string with the same length as +// the input string. To avoid over-allocation, we optimistically allocate an +// array with same size as the input string and only when we detect special +// casing characters, which can change the output string length, we reallocate +// the output buffer to the final string length. +// +// As a further mean to improve runtime performance, the character buffer +// contains an inline storage, so we don't need to heap-allocate an array when +// a JSInlineString will be used for the output string. +// +// Why not use mozilla::Vector instead? mozilla::Vector doesn't provide enough +// fine-grained control to avoid over-allocation when (re)allocating for exact +// buffer sizes. This led to visible performance regressions in ยต-benchmarks. +template <typename CharT> +class MOZ_NON_PARAM InlineCharBuffer { + static constexpr size_t InlineCapacity = MaximumInlineLength<CharT>::value; + + CharT inlineStorage[InlineCapacity]; + UniquePtr<CharT[], JS::FreePolicy> heapStorage; + +#ifdef DEBUG + // In debug mode, we keep track of the requested string lengths to ensure + // all character buffer methods are called in the correct order and with + // the expected argument values. + size_t lastRequestedLength = 0; + + void assertValidRequest(size_t expectedLastLength, size_t length) { + MOZ_ASSERT(length >= expectedLastLength, "cannot shrink requested length"); + MOZ_ASSERT(lastRequestedLength == expectedLastLength); + lastRequestedLength = length; + } +#else + void assertValidRequest(size_t expectedLastLength, size_t length) {} +#endif + + public: + CharT* get() { return heapStorage ? heapStorage.get() : inlineStorage; } + + bool maybeAlloc(JSContext* cx, size_t length) { + assertValidRequest(0, length); + + if (length <= InlineCapacity) { + return true; + } + + MOZ_ASSERT(!heapStorage, "heap storage already allocated"); + heapStorage = + cx->make_pod_arena_array<CharT>(js::StringBufferArena, length); + return !!heapStorage; + } + + bool maybeRealloc(JSContext* cx, size_t oldLength, size_t newLength) { + assertValidRequest(oldLength, newLength); + + if (newLength <= InlineCapacity) { + return true; + } + + if (!heapStorage) { + heapStorage = + cx->make_pod_arena_array<CharT>(js::StringBufferArena, newLength); + if (!heapStorage) { + return false; + } + + MOZ_ASSERT(oldLength <= InlineCapacity); + mozilla::PodCopy(heapStorage.get(), inlineStorage, oldLength); + return true; + } + + CharT* oldChars = heapStorage.release(); + CharT* newChars = cx->pod_arena_realloc(js::StringBufferArena, oldChars, + oldLength, newLength); + if (!newChars) { + js_free(oldChars); + return false; + } + + heapStorage.reset(newChars); + return true; + } + + JSString* toStringDontDeflate(JSContext* cx, size_t length, + js::gc::Heap heap = js::gc::Heap::Default) { + MOZ_ASSERT(length == lastRequestedLength); + + if (JSInlineString::lengthFits<CharT>(length)) { + MOZ_ASSERT( + !heapStorage, + "expected only inline storage when length fits in inline string"); + + if (JSString* str = TryEmptyOrStaticString(cx, inlineStorage, length)) { + return str; + } + + mozilla::Range<const CharT> range(inlineStorage, length); + return NewInlineString<CanGC>(cx, range, heap); + } + + MOZ_ASSERT(heapStorage, + "heap storage was not allocated for non-inline string"); + + return NewStringDontDeflate<CanGC>(cx, std::move(heapStorage), length, + heap); + } + + JSString* toString(JSContext* cx, size_t length, + js::gc::Heap heap = js::gc::Heap::Default) { + MOZ_ASSERT(length == lastRequestedLength); + + if (JSInlineString::lengthFits<CharT>(length)) { + MOZ_ASSERT( + !heapStorage, + "expected only inline storage when length fits in inline string"); + + return NewStringCopyN<CanGC>(cx, inlineStorage, length, heap); + } + + MOZ_ASSERT(heapStorage, + "heap storage was not allocated for non-inline string"); + + return NewString<CanGC>(cx, std::move(heapStorage), length, heap); + } +}; + +} /* namespace js */ + +#endif /* vm_InlineCharBuffer_inl_h */ |