FormatBuffer.h (4431B)
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/. */ 6 7 #ifndef builtin_intl_FormatBuffer_h 8 #define builtin_intl_FormatBuffer_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Span.h" 12 #include "mozilla/TextUtils.h" 13 14 #include <stddef.h> 15 16 #include "js/AllocPolicy.h" 17 #include "js/TypeDecls.h" 18 #include "js/UniquePtr.h" 19 #include "js/Vector.h" 20 #include "vm/StringType.h" 21 22 namespace js::intl { 23 24 /** 25 * A buffer for formatting unified intl data. 26 */ 27 template <typename CharT, size_t MinInlineCapacity = 0, 28 class AllocPolicy = TempAllocPolicy> 29 class FormatBuffer { 30 public: 31 using CharType = CharT; 32 33 // Allow move constructors, but not copy constructors, as this class owns a 34 // js::Vector. 35 FormatBuffer(FormatBuffer&& other) noexcept = default; 36 FormatBuffer& operator=(FormatBuffer&& other) noexcept = default; 37 38 explicit FormatBuffer(AllocPolicy aP = AllocPolicy()) 39 : buffer_(std::move(aP)) { 40 // The initial capacity matches the requested minimum inline capacity, as 41 // long as it doesn't exceed |Vector::kMaxInlineBytes / sizeof(CharT)|. If 42 // this assertion should ever fail, either reduce |MinInlineCapacity| or 43 // make the FormatBuffer initialization fallible. 44 MOZ_ASSERT(buffer_.capacity() == MinInlineCapacity); 45 if constexpr (MinInlineCapacity > 0) { 46 // Ensure the full capacity is marked as reserved. 47 // 48 // Reserving the minimum inline capacity can never fail, even when 49 // simulating OOM. 50 MOZ_ALWAYS_TRUE(buffer_.reserve(MinInlineCapacity)); 51 } 52 } 53 54 // Implicitly convert to a Span. 55 operator mozilla::Span<CharType>() { return buffer_; } 56 operator mozilla::Span<const CharType>() const { return buffer_; } 57 58 /** 59 * Ensures the buffer has enough space to accommodate |size| elements. 60 */ 61 [[nodiscard]] bool reserve(size_t size) { 62 // Call |reserve| a second time to ensure its full capacity is marked as 63 // reserved. 64 return buffer_.reserve(size) && buffer_.reserve(buffer_.capacity()); 65 } 66 67 /** 68 * Returns the raw data inside the buffer. 69 */ 70 CharType* data() { return buffer_.begin(); } 71 72 /** 73 * Returns the count of elements written into the buffer. 74 */ 75 size_t length() const { return buffer_.length(); } 76 77 /** 78 * Returns the buffer's overall capacity. 79 */ 80 size_t capacity() const { return buffer_.capacity(); } 81 82 /** 83 * Resizes the buffer to the given amount of written elements. 84 */ 85 void written(size_t amount) { 86 MOZ_ASSERT(amount <= buffer_.capacity()); 87 // This sets |buffer_|'s internal size so that it matches how much was 88 // written. This is necessary because the write happens across FFI 89 // boundaries. 90 size_t curLength = length(); 91 if (amount > curLength) { 92 buffer_.infallibleGrowByUninitialized(amount - curLength); 93 } else { 94 buffer_.shrinkBy(curLength - amount); 95 } 96 } 97 98 /** 99 * Copies the buffer's data to a JSString. 100 */ 101 JSLinearString* toString(JSContext* cx) const { 102 static_assert(std::is_same_v<CharT, char16_t>); 103 return NewStringCopyN<CanGC>(cx, buffer_.begin(), buffer_.length()); 104 } 105 106 /** 107 * Copies the buffer's data to a JSString. The buffer must contain only 108 * ASCII characters. 109 */ 110 JSLinearString* toAsciiString(JSContext* cx) const { 111 static_assert(std::is_same_v<CharT, char>); 112 113 MOZ_ASSERT(mozilla::IsAscii(buffer_)); 114 return NewStringCopyN<CanGC>(cx, buffer_.begin(), buffer_.length()); 115 } 116 117 /** 118 * Extract this buffer's content as a null-terminated string. 119 */ 120 UniquePtr<CharType[], JS::FreePolicy> extractStringZ() { 121 // Adding the NUL character on an already null-terminated string is likely 122 // an error. If there's ever a valid use case which triggers this assertion, 123 // we should change the below code to only conditionally add '\0'. 124 MOZ_ASSERT_IF(!buffer_.empty(), buffer_.end()[-1] != '\0'); 125 126 if (!buffer_.append('\0')) { 127 return nullptr; 128 } 129 return UniquePtr<CharType[], JS::FreePolicy>( 130 buffer_.extractOrCopyRawBuffer()); 131 } 132 133 private: 134 js::Vector<CharT, MinInlineCapacity, AllocPolicy> buffer_; 135 }; 136 137 } // namespace js::intl 138 139 #endif /* builtin_intl_FormatBuffer_h */