Backed out changeset 8f976ed899d7 (bug 1847231) for causing bc failures on browser_se...
[gecko.git] / js / src / vm / InlineCharBuffer-inl.h
blob3bd8b1418b8dc545665c55df20b9c12a2fcf4edf
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef vm_InlineCharBuffer_inl_h
8 #define vm_InlineCharBuffer_inl_h
10 #include "vm/JSAtomUtils.h"
12 #include "vm/StringType-inl.h"
14 namespace js {
16 template <typename CharT>
17 struct MaximumInlineLength;
19 template <>
20 struct MaximumInlineLength<Latin1Char> {
21 static constexpr size_t value = JSFatInlineString::MAX_LENGTH_LATIN1;
24 template <>
25 struct MaximumInlineLength<char16_t> {
26 static constexpr size_t value = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
29 // Character buffer class used for ToLowerCase and ToUpperCase operations, as
30 // well as other string operations where the final string length is known in
31 // advance.
33 // Case conversion operations normally return a string with the same length as
34 // the input string. To avoid over-allocation, we optimistically allocate an
35 // array with same size as the input string and only when we detect special
36 // casing characters, which can change the output string length, we reallocate
37 // the output buffer to the final string length.
39 // As a further mean to improve runtime performance, the character buffer
40 // contains an inline storage, so we don't need to heap-allocate an array when
41 // a JSInlineString will be used for the output string.
43 // Why not use mozilla::Vector instead? mozilla::Vector doesn't provide enough
44 // fine-grained control to avoid over-allocation when (re)allocating for exact
45 // buffer sizes. This led to visible performance regressions in ยต-benchmarks.
46 template <typename CharT>
47 class MOZ_NON_PARAM InlineCharBuffer {
48 static constexpr size_t InlineCapacity = MaximumInlineLength<CharT>::value;
50 CharT inlineStorage[InlineCapacity];
51 UniquePtr<CharT[], JS::FreePolicy> heapStorage;
53 #ifdef DEBUG
54 // In debug mode, we keep track of the requested string lengths to ensure
55 // all character buffer methods are called in the correct order and with
56 // the expected argument values.
57 size_t lastRequestedLength = 0;
59 void assertValidRequest(size_t expectedLastLength, size_t length) {
60 MOZ_ASSERT(length >= expectedLastLength, "cannot shrink requested length");
61 MOZ_ASSERT(lastRequestedLength == expectedLastLength);
62 lastRequestedLength = length;
64 #else
65 void assertValidRequest(size_t expectedLastLength, size_t length) {}
66 #endif
68 public:
69 CharT* get() { return heapStorage ? heapStorage.get() : inlineStorage; }
71 bool maybeAlloc(JSContext* cx, size_t length) {
72 assertValidRequest(0, length);
74 if (length <= InlineCapacity) {
75 return true;
78 MOZ_ASSERT(!heapStorage, "heap storage already allocated");
79 heapStorage =
80 cx->make_pod_arena_array<CharT>(js::StringBufferArena, length);
81 return !!heapStorage;
84 bool maybeRealloc(JSContext* cx, size_t oldLength, size_t newLength) {
85 assertValidRequest(oldLength, newLength);
87 if (newLength <= InlineCapacity) {
88 return true;
91 if (!heapStorage) {
92 heapStorage =
93 cx->make_pod_arena_array<CharT>(js::StringBufferArena, newLength);
94 if (!heapStorage) {
95 return false;
98 MOZ_ASSERT(oldLength <= InlineCapacity);
99 mozilla::PodCopy(heapStorage.get(), inlineStorage, oldLength);
100 return true;
103 CharT* oldChars = heapStorage.release();
104 CharT* newChars = cx->pod_arena_realloc(js::StringBufferArena, oldChars,
105 oldLength, newLength);
106 if (!newChars) {
107 js_free(oldChars);
108 return false;
111 heapStorage.reset(newChars);
112 return true;
115 JSString* toStringDontDeflate(JSContext* cx, size_t length,
116 js::gc::Heap heap = js::gc::Heap::Default) {
117 MOZ_ASSERT(length == lastRequestedLength);
119 if (JSInlineString::lengthFits<CharT>(length)) {
120 MOZ_ASSERT(
121 !heapStorage,
122 "expected only inline storage when length fits in inline string");
124 if (JSString* str = TryEmptyOrStaticString(cx, inlineStorage, length)) {
125 return str;
128 mozilla::Range<const CharT> range(inlineStorage, length);
129 return NewInlineString<CanGC>(cx, range, heap);
132 MOZ_ASSERT(heapStorage,
133 "heap storage was not allocated for non-inline string");
135 return NewStringDontDeflate<CanGC>(cx, std::move(heapStorage), length,
136 heap);
139 JSString* toString(JSContext* cx, size_t length,
140 js::gc::Heap heap = js::gc::Heap::Default) {
141 MOZ_ASSERT(length == lastRequestedLength);
143 if (JSInlineString::lengthFits<CharT>(length)) {
144 MOZ_ASSERT(
145 !heapStorage,
146 "expected only inline storage when length fits in inline string");
148 return NewStringCopyN<CanGC>(cx, inlineStorage, length, heap);
151 MOZ_ASSERT(heapStorage,
152 "heap storage was not allocated for non-inline string");
154 return NewString<CanGC>(cx, std::move(heapStorage), length, heap);
157 JSAtom* toAtom(JSContext* cx, size_t length) {
158 MOZ_ASSERT(length == lastRequestedLength);
159 return AtomizeChars(cx, get(), length);
163 } /* namespace js */
165 #endif /* vm_InlineCharBuffer_inl_h */