Bug 1052579 - Refactor and cleanup for JS StringBuffer class r=sfink

Refactored a couple of minor issues with JS StringBuffer, partly to prepare
it to handle the new JSString buffer arena.

1.  There were public functions that were returning mutable references to
    private members, making them not-really "private" at all. They are now
    hidden and new API calls were added to provide needed functionality.
2.  ExtractWellSized() now uses the buffer's policy to resize/free it instead
    of directly calling JS alloc API
3.  Changed FinishStringFlat to no longer take a reference to a StringBuffer
    and a reference to a StringBuffer member

Differential Revision: https://phabricator.services.mozilla.com/D25706

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Chris Martin 2019-04-09 12:56:55 +00:00
Родитель cc9d6b8505
Коммит 4e5edc2663
3 изменённых файлов: 138 добавлений и 118 удалений

Просмотреть файл

@ -138,34 +138,19 @@ static MOZ_ALWAYS_INLINE RangedPtr<DstCharT> InfallibleQuote(
return dstPtr;
}
template <typename SrcCharT, typename CharVectorT>
static bool Quote(JSContext* cx, CharVectorT& sb, JSLinearString* str) {
// We resize the backing buffer to the maximum size we could possibly need,
// write the escaped string into it, and shrink it back to the size we ended
// up needing.
size_t len = str->length();
CheckedInt<size_t> reservedLen = CheckedInt<size_t>(len) * 6 + 2;
if (MOZ_UNLIKELY(!reservedLen.isValid())) {
ReportAllocationOverflow(cx);
return false;
}
size_t sbInitialLen = sb.length();
if (!sb.growByUninitialized(reservedLen.value())) {
return false;
}
typedef typename CharVectorT::ElementType DstCharT;
template <typename SrcCharT, typename DstCharT>
static size_t QuoteHelper(const JSLinearString& linear, StringBuffer& sb,
size_t sbOffset) {
size_t len = linear.length();
JS::AutoCheckCannotGC nogc;
RangedPtr<const SrcCharT> srcBegin{str->chars<SrcCharT>(nogc), len};
RangedPtr<DstCharT> dstBegin{sb.begin(), sb.begin(), sb.end()};
RangedPtr<const SrcCharT> srcBegin{linear.chars<SrcCharT>(nogc), len};
RangedPtr<DstCharT> dstBegin{sb.begin<DstCharT>(), sb.begin<DstCharT>(),
sb.end<DstCharT>()};
RangedPtr<DstCharT> dstEnd =
InfallibleQuote(srcBegin, srcBegin + len, dstBegin + sbInitialLen);
size_t newSize = dstEnd - dstBegin;
sb.shrinkTo(newSize);
return true;
InfallibleQuote(srcBegin, srcBegin + len, dstBegin + sbOffset);
return dstEnd - dstBegin;
}
static bool Quote(JSContext* cx, StringBuffer& sb, JSString* str) {
@ -174,20 +159,40 @@ static bool Quote(JSContext* cx, StringBuffer& sb, JSString* str) {
return false;
}
// Check if either has non-latin1 before calling ensure, so that the buffer's
// hasEnsured flag is set if the converstion to twoByte was automatic.
if (!sb.isUnderlyingBufferLatin1() || linear->hasTwoByteChars()) {
if (!sb.ensureTwoByteChars()) {
return false;
}
}
if (linear->hasTwoByteChars()) {
return Quote<char16_t>(cx, sb.rawTwoByteBuffer(), linear);
if (linear->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
return false;
}
return sb.isUnderlyingBufferLatin1()
? Quote<Latin1Char>(cx, sb.latin1Chars(), linear)
: Quote<Latin1Char>(cx, sb.rawTwoByteBuffer(), linear);
// We resize the backing buffer to the maximum size we could possibly need,
// write the escaped string into it, and shrink it back to the size we ended
// up needing.
size_t len = linear->length();
size_t sbInitialLen = sb.length();
CheckedInt<size_t> reservedLen = CheckedInt<size_t>(len) * 6 + 2;
if (MOZ_UNLIKELY(!reservedLen.isValid())) {
ReportAllocationOverflow(cx);
return false;
}
if (!sb.growByUninitialized(reservedLen.value())) {
return false;
}
size_t newSize;
if (linear->hasTwoByteChars()) {
newSize = QuoteHelper<char16_t, char16_t>(*linear, sb, sbInitialLen);
} else if (sb.isUnderlyingBufferLatin1()) {
newSize = QuoteHelper<Latin1Char, Latin1Char>(*linear, sb, sbInitialLen);
} else {
newSize = QuoteHelper<Latin1Char, char16_t>(*linear, sb, sbInitialLen);
}
sb.shrinkTo(newSize);
return true;
}
namespace {

Просмотреть файл

@ -15,9 +15,10 @@
using namespace js;
template <typename CharT, class Buffer>
static CharT* ExtractWellSized(JSContext* cx, Buffer& cb) {
static CharT* ExtractWellSized(Buffer& cb) {
size_t capacity = cb.capacity();
size_t length = cb.length();
TempAllocPolicy allocPolicy = cb.allocPolicy();
CharT* buf = cb.extractOrCopyRawBuffer();
if (!buf) {
@ -27,9 +28,9 @@ static CharT* ExtractWellSized(JSContext* cx, Buffer& cb) {
/* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
MOZ_ASSERT(capacity >= length);
if (length > Buffer::sMaxInlineStorage && capacity - length > length / 4) {
CharT* tmp = cx->pod_realloc<CharT>(buf, capacity, length + 1);
CharT* tmp = allocPolicy.pod_realloc<CharT>(buf, capacity, length + 1);
if (!tmp) {
js_free(buf);
allocPolicy.free_(buf);
return nullptr;
}
buf = tmp;
@ -43,7 +44,7 @@ char16_t* StringBuffer::stealChars() {
return nullptr;
}
return ExtractWellSized<char16_t>(cx, twoByteChars());
return ExtractWellSized<char16_t>(twoByteChars());
}
bool StringBuffer::inflateChars() {
@ -68,15 +69,22 @@ bool StringBuffer::inflateChars() {
return true;
}
template <typename CharT, class Buffer>
static JSFlatString* FinishStringFlat(JSContext* cx, StringBuffer& sb,
Buffer& cb) {
size_t len = sb.length();
if (!sb.append('\0')) {
template <typename CharT>
JSFlatString* StringBuffer::finishStringInternal(JSContext* cx) {
size_t len = length();
if (JSInlineString::lengthFits<CharT>(len)) {
mozilla::Range<const CharT> range(begin<CharT>(), len);
return NewInlineString<CanGC>(cx, range);
}
if (!append('\0')) {
return nullptr;
}
UniquePtr<CharT[], JS::FreePolicy> buf(ExtractWellSized<CharT>(cx, cb));
UniquePtr<CharT[], JS::FreePolicy> buf(
ExtractWellSized<CharT>(chars<CharT>()));
if (!buf) {
return nullptr;
}
@ -110,20 +118,8 @@ JSFlatString* StringBuffer::finishString() {
JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_LATIN1 <
Latin1CharBuffer::InlineLength);
if (isLatin1()) {
if (JSInlineString::lengthFits<Latin1Char>(len)) {
mozilla::Range<const Latin1Char> range(latin1Chars().begin(), len);
return NewInlineString<CanGC>(cx, range);
}
} else {
if (JSInlineString::lengthFits<char16_t>(len)) {
mozilla::Range<const char16_t> range(twoByteChars().begin(), len);
return NewInlineString<CanGC>(cx, range);
}
}
return isLatin1() ? FinishStringFlat<Latin1Char>(cx, *this, latin1Chars())
: FinishStringFlat<char16_t>(cx, *this, twoByteChars());
return isLatin1() ? finishStringInternal<Latin1Char>(cx)
: finishStringInternal<char16_t>(cx);
}
JSAtom* StringBuffer::finishAtom() {

Просмотреть файл

@ -28,12 +28,15 @@ namespace js {
* See |extractWellSized|.
*/
class StringBuffer {
template <typename CharT>
using BufferType = Vector<CharT, 64 / sizeof(CharT)>;
/*
* The Vector's buffer may be either stolen or copied, so we need to use
* TempAllocPolicy and account for the memory manually when stealing.
*/
typedef Vector<Latin1Char, 64> Latin1CharBuffer;
typedef Vector<char16_t, 32> TwoByteCharBuffer;
using Latin1CharBuffer = BufferType<Latin1Char>;
using TwoByteCharBuffer = BufferType<char16_t>;
JSContext* cx;
@ -44,55 +47,59 @@ class StringBuffer {
*/
mozilla::MaybeOneOf<Latin1CharBuffer, TwoByteCharBuffer> cb;
#ifdef DEBUG
/*
* Make sure ensureTwoByteChars() is called before calling
* infallibleAppend(char16_t).
*/
bool hasEnsuredTwoByteChars_;
#endif
/* Number of reserve()'d chars, see inflateChars. */
size_t reserved_;
StringBuffer(const StringBuffer& other) = delete;
void operator=(const StringBuffer& other) = delete;
MOZ_ALWAYS_INLINE bool isLatin1() const {
return cb.constructed<Latin1CharBuffer>();
template <typename CharT>
MOZ_ALWAYS_INLINE bool isCharType() const {
return cb.constructed<BufferType<CharT>>();
}
MOZ_ALWAYS_INLINE bool isLatin1() const { return isCharType<Latin1Char>(); }
MOZ_ALWAYS_INLINE bool isTwoByte() const { return isCharType<char16_t>(); }
template <typename CharT>
MOZ_ALWAYS_INLINE BufferType<CharT>& chars() {
MOZ_ASSERT(isCharType<CharT>());
return cb.ref<BufferType<CharT>>();
}
template <typename CharT>
MOZ_ALWAYS_INLINE const BufferType<CharT>& chars() const {
MOZ_ASSERT(isCharType<CharT>());
return cb.ref<BufferType<CharT>>();
}
MOZ_ALWAYS_INLINE bool isTwoByte() const { return !isLatin1(); }
MOZ_ALWAYS_INLINE TwoByteCharBuffer& twoByteChars() {
return cb.ref<TwoByteCharBuffer>();
return chars<char16_t>();
}
MOZ_ALWAYS_INLINE const TwoByteCharBuffer& twoByteChars() const {
return cb.ref<TwoByteCharBuffer>();
return chars<char16_t>();
}
MOZ_ALWAYS_INLINE Latin1CharBuffer& latin1Chars() {
return chars<Latin1Char>();
}
MOZ_ALWAYS_INLINE const Latin1CharBuffer& latin1Chars() const {
return chars<Latin1Char>();
}
MOZ_MUST_USE bool inflateChars();
template <typename CharT>
JSFlatString* finishStringInternal(JSContext* cx);
public:
explicit StringBuffer(JSContext* cx)
: cx(cx)
#ifdef DEBUG
,
hasEnsuredTwoByteChars_(false)
#endif
,
reserved_(0) {
explicit StringBuffer(JSContext* cx) : cx(cx), reserved_(0) {
cb.construct<Latin1CharBuffer>(cx);
}
MOZ_ALWAYS_INLINE Latin1CharBuffer& latin1Chars() {
return cb.ref<Latin1CharBuffer>();
}
MOZ_ALWAYS_INLINE const Latin1CharBuffer& latin1Chars() const {
return cb.ref<Latin1CharBuffer>();
}
void clear() {
if (isLatin1()) {
latin1Chars().clear();
@ -110,6 +117,14 @@ class StringBuffer {
MOZ_MUST_USE bool resize(size_t len) {
return isLatin1() ? latin1Chars().resize(len) : twoByteChars().resize(len);
}
MOZ_MUST_USE bool growByUninitialized(size_t incr) {
return isLatin1() ? latin1Chars().growByUninitialized(incr)
: twoByteChars().growByUninitialized(incr);
}
void shrinkTo(size_t newLength) {
return isLatin1() ? latin1Chars().shrinkTo(newLength)
: twoByteChars().shrinkTo(newLength);
}
bool empty() const {
return isLatin1() ? latin1Chars().empty() : twoByteChars().empty();
}
@ -121,14 +136,7 @@ class StringBuffer {
}
MOZ_MUST_USE bool ensureTwoByteChars() {
if (isLatin1() && !inflateChars()) {
return false;
}
#ifdef DEBUG
hasEnsuredTwoByteChars_ = true;
#endif
return true;
return isTwoByte() || inflateChars();
}
MOZ_MUST_USE bool append(const char16_t c) {
@ -147,11 +155,6 @@ class StringBuffer {
}
MOZ_MUST_USE bool append(char c) { return append(Latin1Char(c)); }
TwoByteCharBuffer& rawTwoByteBuffer() {
MOZ_ASSERT(hasEnsuredTwoByteChars_);
return twoByteChars();
}
inline MOZ_MUST_USE bool append(const char16_t* begin, const char16_t* end);
MOZ_MUST_USE bool append(const char16_t* chars, size_t len) {
@ -225,25 +228,41 @@ class StringBuffer {
* calling ensureTwoByteChars().
*/
void infallibleAppend(const char16_t* chars, size_t len) {
MOZ_ASSERT(hasEnsuredTwoByteChars_);
twoByteChars().infallibleAppend(chars, len);
}
void infallibleAppend(char16_t c) {
MOZ_ASSERT(hasEnsuredTwoByteChars_);
twoByteChars().infallibleAppend(c);
}
void infallibleAppend(char16_t c) { twoByteChars().infallibleAppend(c); }
bool isUnderlyingBufferLatin1() const { return isLatin1(); }
char16_t* rawTwoByteBegin() { return twoByteChars().begin(); }
char16_t* rawTwoByteEnd() { return twoByteChars().end(); }
const char16_t* rawTwoByteBegin() const { return twoByteChars().begin(); }
const char16_t* rawTwoByteEnd() const { return twoByteChars().end(); }
template <typename CharT>
CharT* begin() {
return chars<CharT>().begin();
}
Latin1Char* rawLatin1Begin() { return latin1Chars().begin(); }
Latin1Char* rawLatin1End() { return latin1Chars().end(); }
const Latin1Char* rawLatin1Begin() const { return latin1Chars().begin(); }
const Latin1Char* rawLatin1End() const { return latin1Chars().end(); }
template <typename CharT>
CharT* end() {
return chars<CharT>().end();
}
template <typename CharT>
const CharT* begin() const {
return chars<CharT>().begin();
}
template <typename CharT>
const CharT* end() const {
return chars<CharT>().end();
}
char16_t* rawTwoByteBegin() { return begin<char16_t>(); }
char16_t* rawTwoByteEnd() { return end<char16_t>(); }
const char16_t* rawTwoByteBegin() const { return begin<char16_t>(); }
const char16_t* rawTwoByteEnd() const { return end<char16_t>(); }
Latin1Char* rawLatin1Begin() { return begin<Latin1Char>(); }
Latin1Char* rawLatin1End() { return end<Latin1Char>(); }
const Latin1Char* rawLatin1Begin() const { return begin<Latin1Char>(); }
const Latin1Char* rawLatin1End() const { return end<Latin1Char>(); }
/*
* Creates a string from the characters in this buffer, then (regardless