Bug 1711902 - Add a FormatBuffer for use in SpiderMonkey; r=anba

Differential Revision: https://phabricator.services.mozilla.com/D116911
This commit is contained in:
Greg Tatum 2021-06-10 20:14:16 +00:00
parent 9534c128ad
commit efe295fd94
5 changed files with 157 additions and 23 deletions

View File

@ -5,38 +5,68 @@
#define intl_components_gtest_TestBuffer_h_
#include <string_view>
#include "mozilla/DebugOnly.h"
#include "mozilla/Vector.h"
namespace mozilla::intl {
/**
* This buffer class is useful for testing formatting operations.
* A test buffer for interfacing with unified intl classes.
* Closely resembles the FormatBuffer class, but without
* JavaScript-specific implementation details.
*/
template <typename C>
template <typename C, size_t inlineCapacity = 0>
class TestBuffer {
public:
using CharType = C;
bool allocate(size_t aSize) {
mBuffer.resize(aSize);
return true;
}
// Only allow moves, and not copies, as this class owns the mozilla::Vector.
TestBuffer(TestBuffer&& other) noexcept = default;
TestBuffer& operator=(TestBuffer&& other) noexcept = default;
CharType* data() { return mBuffer.data(); }
explicit TestBuffer(const size_t aSize = 0) { reserve(aSize); }
size_t size() const { return mBuffer.size(); }
/**
* Ensures the buffer has enough space to accommodate |aSize| elemtns.
*/
bool reserve(const size_t aSize) { return mBuffer.reserve(aSize); }
/**
* Returns the raw data inside the buffer.
*/
CharType* data() { return mBuffer.begin(); }
/**
* Returns the count of elements in written to the buffer.
*/
size_t length() const { return mBuffer.length(); }
/**
* Returns the buffer's overall capacity.
*/
size_t capacity() const { return mBuffer.capacity(); }
void written(size_t aAmount) { mWritten = aAmount; }
template <typename C2>
std::basic_string_view<const C2> get_string_view() {
return std::basic_string_view<const C2>(
reinterpret_cast<const C2*>(mBuffer.data()), mWritten);
/**
* Resizes the buffer to the given amount of written elements.
* This is necessary because the buffer gets written to across
* FFI boundaries, so this needs to happen in a separate step.
*/
void written(size_t aAmount) {
MOZ_ASSERT(aAmount <= mBuffer.capacity());
mozilla::DebugOnly<bool> result = mBuffer.resizeUninitialized(aAmount);
MOZ_ASSERT(result);
}
std::vector<C> mBuffer;
size_t mWritten = 0;
/**
* Get a string view into the buffer, which is useful for test assertions.
*/
template <typename C2>
std::basic_string_view<const C2> get_string_view() {
return std::basic_string_view<const C2>(reinterpret_cast<const C2*>(data()),
length());
}
Vector<C, inlineCapacity> mBuffer{};
};
} // namespace mozilla::intl

View File

@ -48,7 +48,7 @@ static ICUResult FillBufferWithICUCall(Buffer& buffer,
if (status == U_BUFFER_OVERFLOW_ERROR) {
MOZ_ASSERT(length >= 0);
if (!buffer.allocate(length)) {
if (!buffer.reserve(length)) {
return Err(ICUError::OutOfMemory);
}
@ -110,13 +110,13 @@ template <typename Buffer>
return false;
}
if (!utf8TargetBuffer.allocate(3 * utf16Span.Length())) {
if (!utf8TargetBuffer.reserve(3 * utf16Span.Length())) {
return false;
}
size_t amount = ConvertUtf16toUtf8(
utf16Span, Span(reinterpret_cast<char*>(std::data(utf8TargetBuffer)),
std::size(utf8TargetBuffer)));
utf16Span, Span(reinterpret_cast<char*>(utf8TargetBuffer.data()),
utf8TargetBuffer.capacity()));
utf8TargetBuffer.written(amount);

View File

@ -397,7 +397,7 @@ class NumberFormat final {
} else {
// ICU provides APIs which accept a buffer, but they just copy from an
// internal buffer behind the scenes anyway.
if (!buffer.allocate(result.size())) {
if (!buffer.reserve(result.size())) {
return Err(FormatError::OutOfMemory);
}
PodCopy(static_cast<char16_t*>(buffer.data()), result.data(),

View File

@ -22,7 +22,7 @@ class SizeableUTF8Buffer {
public:
using CharType = uint8_t;
bool allocate(size_t size) {
bool reserve(size_t size) {
mBuffer.reset(reinterpret_cast<CharType*>(malloc(size)));
mCapacity = size;
return true;
@ -30,7 +30,7 @@ class SizeableUTF8Buffer {
CharType* data() { return mBuffer.get(); }
size_t size() const { return mCapacity; }
size_t capacity() const { return mCapacity; }
void written(size_t amount) { mWritten = amount; }

View File

@ -0,0 +1,104 @@
/* -*- 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/Range.h"
#include <stddef.h>
#include <stdint.h>
#include "gc/Allocator.h"
#include "js/AllocPolicy.h"
#include "js/TypeDecls.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 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(JSContext* cx) : cx_(cx), buffer_(cx) {
MOZ_ASSERT(cx);
}
/**
* Ensures the buffer has enough space to accommodate |size| elements.
*/
[[nodiscard]] bool reserve(const size_t size) {
return buffer_.reserve(size);
}
/**
* 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.
mozilla::DebugOnly<bool> result = buffer_.resizeUninitialized(amount);
MOZ_ASSERT(result);
}
/**
* 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.
*/
JSString* toString() 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<CanGC>(
cx_, mozilla::Range(reinterpret_cast<unsigned char>(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());
}
}
private:
JSContext* cx_;
js::Vector<CharT, MinInlineCapacity> buffer_;
};
} // namespace js::intl
#endif /* builtin_intl_FormatBuffer_h */