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"
16 template <typename CharT
>
17 struct MaximumInlineLength
;
20 struct MaximumInlineLength
<Latin1Char
> {
21 static constexpr size_t value
= JSFatInlineString::MAX_LENGTH_LATIN1
;
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
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
;
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
;
65 void assertValidRequest(size_t expectedLastLength
, size_t length
) {}
69 CharT
* get() { return heapStorage
? heapStorage
.get() : inlineStorage
; }
71 bool maybeAlloc(JSContext
* cx
, size_t length
) {
72 assertValidRequest(0, length
);
74 if (length
<= InlineCapacity
) {
78 MOZ_ASSERT(!heapStorage
, "heap storage already allocated");
80 cx
->make_pod_arena_array
<CharT
>(js::StringBufferArena
, length
);
84 bool maybeRealloc(JSContext
* cx
, size_t oldLength
, size_t newLength
) {
85 assertValidRequest(oldLength
, newLength
);
87 if (newLength
<= InlineCapacity
) {
93 cx
->make_pod_arena_array
<CharT
>(js::StringBufferArena
, newLength
);
98 MOZ_ASSERT(oldLength
<= InlineCapacity
);
99 mozilla::PodCopy(heapStorage
.get(), inlineStorage
, oldLength
);
103 CharT
* oldChars
= heapStorage
.release();
104 CharT
* newChars
= cx
->pod_arena_realloc(js::StringBufferArena
, oldChars
,
105 oldLength
, newLength
);
111 heapStorage
.reset(newChars
);
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
)) {
122 "expected only inline storage when length fits in inline string");
124 if (JSString
* str
= TryEmptyOrStaticString(cx
, inlineStorage
, length
)) {
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
,
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
)) {
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
);
165 #endif /* vm_InlineCharBuffer_inl_h */