diff options
Diffstat (limited to 'js/src/builtin/intl/FormatBuffer.h')
-rw-r--r-- | js/src/builtin/intl/FormatBuffer.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/js/src/builtin/intl/FormatBuffer.h b/js/src/builtin/intl/FormatBuffer.h new file mode 100644 index 0000000000..73d450a546 --- /dev/null +++ b/js/src/builtin/intl/FormatBuffer.h @@ -0,0 +1,155 @@ +/* -*- 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 builtin_intl_FormatBuffer_h +#define builtin_intl_FormatBuffer_h + +#include "mozilla/Assertions.h" +#include "mozilla/Span.h" +#include "mozilla/TextUtils.h" + +#include <stddef.h> +#include <stdint.h> + +#include "gc/Allocator.h" +#include "js/AllocPolicy.h" +#include "js/CharacterEncoding.h" +#include "js/TypeDecls.h" +#include "js/UniquePtr.h" +#include "js/Vector.h" +#include "vm/StringType.h" + +namespace js::intl { + +/** + * A buffer for formatting unified intl data. + */ +template <typename CharT, size_t MinInlineCapacity = 0, + class AllocPolicy = TempAllocPolicy> +class FormatBuffer { + public: + using CharType = CharT; + + // Allow move constructors, but not copy constructors, as this class owns a + // js::Vector. + FormatBuffer(FormatBuffer&& other) noexcept = default; + FormatBuffer& operator=(FormatBuffer&& other) noexcept = default; + + explicit FormatBuffer(AllocPolicy aP = AllocPolicy()) + : buffer_(std::move(aP)) { + // The initial capacity matches the requested minimum inline capacity, as + // long as it doesn't exceed |Vector::kMaxInlineBytes / sizeof(CharT)|. If + // this assertion should ever fail, either reduce |MinInlineCapacity| or + // make the FormatBuffer initialization fallible. + MOZ_ASSERT(buffer_.capacity() == MinInlineCapacity); + if constexpr (MinInlineCapacity > 0) { + // Ensure the full capacity is marked as reserved. + // + // Reserving the minimum inline capacity can never fail, even when + // simulating OOM. + MOZ_ALWAYS_TRUE(buffer_.reserve(MinInlineCapacity)); + } + } + + // Implicitly convert to a Span. + operator mozilla::Span<CharType>() { return buffer_; } + operator mozilla::Span<const CharType>() const { return buffer_; } + + /** + * Ensures the buffer has enough space to accommodate |size| elements. + */ + [[nodiscard]] bool reserve(size_t size) { + // Call |reserve| a second time to ensure its full capacity is marked as + // reserved. + return buffer_.reserve(size) && buffer_.reserve(buffer_.capacity()); + } + + /** + * Returns the raw data inside the buffer. + */ + CharType* data() { return buffer_.begin(); } + + /** + * Returns the count of elements written into the buffer. + */ + size_t length() const { return buffer_.length(); } + + /** + * Returns the buffer's overall capacity. + */ + size_t capacity() const { return buffer_.capacity(); } + + /** + * Resizes the buffer to the given amount of written elements. + */ + void written(size_t amount) { + MOZ_ASSERT(amount <= buffer_.capacity()); + // This sets |buffer_|'s internal size so that it matches how much was + // written. This is necessary because the write happens across FFI + // boundaries. + size_t curLength = length(); + if (amount > curLength) { + buffer_.infallibleGrowByUninitialized(amount - curLength); + } else { + buffer_.shrinkBy(curLength - amount); + } + } + + /** + * Copies the buffer's data to a JSString. + * + * TODO(#1715842) - This should be more explicit on needing to handle OOM + * errors. In this case it returns a nullptr that must be checked, but it may + * not be obvious. + */ + JSLinearString* toString(JSContext* cx) const { + if constexpr (std::is_same_v<CharT, uint8_t> || + std::is_same_v<CharT, unsigned char> || + std::is_same_v<CharT, char>) { + // Handle the UTF-8 encoding case. + return NewStringCopyUTF8N( + cx, JS::UTF8Chars(buffer_.begin(), buffer_.length())); + } else { + // Handle the UTF-16 encoding case. + static_assert(std::is_same_v<CharT, char16_t>); + return NewStringCopyN<CanGC>(cx, buffer_.begin(), buffer_.length()); + } + } + + /** + * Copies the buffer's data to a JSString. The buffer must contain only + * ASCII characters. + */ + JSLinearString* toAsciiString(JSContext* cx) const { + static_assert(std::is_same_v<CharT, char>); + + MOZ_ASSERT(mozilla::IsAscii(buffer_)); + return NewStringCopyN<CanGC>(cx, buffer_.begin(), buffer_.length()); + } + + /** + * Extract this buffer's content as a null-terminated string. + */ + UniquePtr<CharType[], JS::FreePolicy> extractStringZ() { + // Adding the NUL character on an already null-terminated string is likely + // an error. If there's ever a valid use case which triggers this assertion, + // we should change the below code to only conditionally add '\0'. + MOZ_ASSERT_IF(!buffer_.empty(), buffer_.end()[-1] != '\0'); + + if (!buffer_.append('\0')) { + return nullptr; + } + return UniquePtr<CharType[], JS::FreePolicy>( + buffer_.extractOrCopyRawBuffer()); + } + + private: + js::Vector<CharT, MinInlineCapacity, AllocPolicy> buffer_; +}; + +} // namespace js::intl + +#endif /* builtin_intl_FormatBuffer_h */ |