summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/intl/FormatBuffer.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/intl/FormatBuffer.h')
-rw-r--r--js/src/builtin/intl/FormatBuffer.h155
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 */