summaryrefslogtreecommitdiffstats
path: root/js/src/vm/StringBuffer.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/StringBuffer.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/StringBuffer.h')
-rw-r--r--js/src/vm/StringBuffer.h343
1 files changed, 343 insertions, 0 deletions
diff --git a/js/src/vm/StringBuffer.h b/js/src/vm/StringBuffer.h
new file mode 100644
index 000000000..19da43526
--- /dev/null
+++ b/js/src/vm/StringBuffer.h
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 vm_StringBuffer_h
+#define vm_StringBuffer_h
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/MaybeOneOf.h"
+
+#include "jscntxt.h"
+
+#include "js/Vector.h"
+
+namespace js {
+
+/*
+ * String builder that eagerly checks for over-allocation past the maximum
+ * string length.
+ *
+ * Any operation which would exceed the maximum string length causes an
+ * exception report on the context and results in a failed return value.
+ *
+ * Well-sized extractions (which waste no more than 1/4 of their char
+ * buffer space) are guaranteed for strings built by this interface.
+ * See |extractWellSized|.
+ */
+class StringBuffer
+{
+ /*
+ * 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;
+
+ ExclusiveContext* cx;
+
+ /*
+ * If Latin1 strings are enabled, cb starts out as a Latin1CharBuffer. When
+ * a TwoByte char is appended, inflateChars() constructs a TwoByteCharBuffer
+ * and copies the Latin1 chars.
+ */
+ 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>(); }
+ MOZ_ALWAYS_INLINE bool isTwoByte() const { return !isLatin1(); }
+
+ MOZ_ALWAYS_INLINE Latin1CharBuffer& latin1Chars() { return cb.ref<Latin1CharBuffer>(); }
+ MOZ_ALWAYS_INLINE TwoByteCharBuffer& twoByteChars() { return cb.ref<TwoByteCharBuffer>(); }
+
+ MOZ_ALWAYS_INLINE const Latin1CharBuffer& latin1Chars() const {
+ return cb.ref<Latin1CharBuffer>();
+ }
+ MOZ_ALWAYS_INLINE const TwoByteCharBuffer& twoByteChars() const {
+ return cb.ref<TwoByteCharBuffer>();
+ }
+
+ MOZ_MUST_USE bool inflateChars();
+
+ public:
+ explicit StringBuffer(ExclusiveContext* cx)
+ : cx(cx)
+#ifdef DEBUG
+ , hasEnsuredTwoByteChars_(false)
+#endif
+ , reserved_(0)
+ {
+ cb.construct<Latin1CharBuffer>(cx);
+ }
+
+ void clear() {
+ if (isLatin1())
+ latin1Chars().clear();
+ else
+ twoByteChars().clear();
+ }
+ MOZ_MUST_USE bool reserve(size_t len) {
+ if (len > reserved_)
+ reserved_ = len;
+ return isLatin1() ? latin1Chars().reserve(len) : twoByteChars().reserve(len);
+ }
+ MOZ_MUST_USE bool resize(size_t len) {
+ return isLatin1() ? latin1Chars().resize(len) : twoByteChars().resize(len);
+ }
+ bool empty() const {
+ return isLatin1() ? latin1Chars().empty() : twoByteChars().empty();
+ }
+ size_t length() const {
+ return isLatin1() ? latin1Chars().length() : twoByteChars().length();
+ }
+ char16_t getChar(size_t idx) const {
+ return isLatin1() ? latin1Chars()[idx] : twoByteChars()[idx];
+ }
+
+ MOZ_MUST_USE bool ensureTwoByteChars() {
+ if (isLatin1() && !inflateChars())
+ return false;
+
+#ifdef DEBUG
+ hasEnsuredTwoByteChars_ = true;
+#endif
+ return true;
+ }
+
+ MOZ_MUST_USE bool append(const char16_t c) {
+ if (isLatin1()) {
+ if (c <= JSString::MAX_LATIN1_CHAR)
+ return latin1Chars().append(Latin1Char(c));
+ if (!inflateChars())
+ return false;
+ }
+ return twoByteChars().append(c);
+ }
+ MOZ_MUST_USE bool append(Latin1Char c) {
+ return isLatin1() ? latin1Chars().append(c) : twoByteChars().append(c);
+ }
+ MOZ_MUST_USE bool append(char c) {
+ return append(Latin1Char(c));
+ }
+
+ 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) {
+ return append(chars, chars + len);
+ }
+
+ MOZ_MUST_USE bool append(const Latin1Char* begin, const Latin1Char* end) {
+ return isLatin1() ? latin1Chars().append(begin, end) : twoByteChars().append(begin, end);
+ }
+ MOZ_MUST_USE bool append(const Latin1Char* chars, size_t len) {
+ return append(chars, chars + len);
+ }
+
+ MOZ_MUST_USE bool append(const JS::ConstCharPtr chars, size_t len) {
+ return append(chars.get(), chars.get() + len);
+ }
+ MOZ_MUST_USE bool appendN(Latin1Char c, size_t n) {
+ return isLatin1() ? latin1Chars().appendN(c, n) : twoByteChars().appendN(c, n);
+ }
+
+ inline MOZ_MUST_USE bool append(JSString* str);
+ inline MOZ_MUST_USE bool append(JSLinearString* str);
+ inline MOZ_MUST_USE bool appendSubstring(JSString* base, size_t off, size_t len);
+ inline MOZ_MUST_USE bool appendSubstring(JSLinearString* base, size_t off, size_t len);
+
+ MOZ_MUST_USE bool append(const char* chars, size_t len) {
+ return append(reinterpret_cast<const Latin1Char*>(chars), len);
+ }
+
+ template <size_t ArrayLength>
+ MOZ_MUST_USE bool append(const char (&array)[ArrayLength]) {
+ return append(array, ArrayLength - 1); /* No trailing '\0'. */
+ }
+
+ /* Infallible variants usable when the corresponding space is reserved. */
+ void infallibleAppend(Latin1Char c) {
+ if (isLatin1())
+ latin1Chars().infallibleAppend(c);
+ else
+ twoByteChars().infallibleAppend(c);
+ }
+ void infallibleAppend(char c) {
+ infallibleAppend(Latin1Char(c));
+ }
+ void infallibleAppend(const Latin1Char* chars, size_t len) {
+ if (isLatin1())
+ latin1Chars().infallibleAppend(chars, len);
+ else
+ twoByteChars().infallibleAppend(chars, len);
+ }
+ void infallibleAppend(const char* chars, size_t len) {
+ infallibleAppend(reinterpret_cast<const Latin1Char*>(chars), len);
+ }
+
+ void infallibleAppendSubstring(JSLinearString* base, size_t off, size_t len);
+
+ /*
+ * Because inflation is fallible, these methods should only be used after
+ * 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);
+ }
+
+ 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(); }
+
+ Latin1Char* rawLatin1Begin() { return latin1Chars().begin(); }
+ Latin1Char* rawLatin1End() { return latin1Chars().end(); }
+ const Latin1Char* rawLatin1Begin() const { return latin1Chars().begin(); }
+ const Latin1Char* rawLatin1End() const { return latin1Chars().end(); }
+
+ /*
+ * Creates a string from the characters in this buffer, then (regardless
+ * whether string creation succeeded or failed) empties the buffer.
+ */
+ JSFlatString* finishString();
+
+ /* Identical to finishString() except that an atom is created. */
+ JSAtom* finishAtom();
+
+ /*
+ * Creates a raw string from the characters in this buffer. The string is
+ * exactly the characters in this buffer (inflated to TwoByte), it is *not*
+ * null-terminated unless the last appended character was '\0'.
+ */
+ char16_t* stealChars();
+};
+
+inline bool
+StringBuffer::append(const char16_t* begin, const char16_t* end)
+{
+ MOZ_ASSERT(begin <= end);
+ if (isLatin1()) {
+ while (true) {
+ if (begin >= end)
+ return true;
+ if (*begin > JSString::MAX_LATIN1_CHAR)
+ break;
+ if (!latin1Chars().append(*begin))
+ return false;
+ ++begin;
+ }
+ if (!inflateChars())
+ return false;
+ }
+ return twoByteChars().append(begin, end);
+}
+
+inline bool
+StringBuffer::append(JSLinearString* str)
+{
+ JS::AutoCheckCannotGC nogc;
+ if (isLatin1()) {
+ if (str->hasLatin1Chars())
+ return latin1Chars().append(str->latin1Chars(nogc), str->length());
+ if (!inflateChars())
+ return false;
+ }
+ return str->hasLatin1Chars()
+ ? twoByteChars().append(str->latin1Chars(nogc), str->length())
+ : twoByteChars().append(str->twoByteChars(nogc), str->length());
+}
+
+inline void
+StringBuffer::infallibleAppendSubstring(JSLinearString* base, size_t off, size_t len)
+{
+ MOZ_ASSERT(off + len <= base->length());
+ MOZ_ASSERT_IF(base->hasTwoByteChars(), isTwoByte());
+
+ JS::AutoCheckCannotGC nogc;
+ if (base->hasLatin1Chars())
+ infallibleAppend(base->latin1Chars(nogc) + off, len);
+ else
+ infallibleAppend(base->twoByteChars(nogc) + off, len);
+}
+
+inline bool
+StringBuffer::appendSubstring(JSLinearString* base, size_t off, size_t len)
+{
+ MOZ_ASSERT(off + len <= base->length());
+
+ JS::AutoCheckCannotGC nogc;
+ if (isLatin1()) {
+ if (base->hasLatin1Chars())
+ return latin1Chars().append(base->latin1Chars(nogc) + off, len);
+ if (!inflateChars())
+ return false;
+ }
+ return base->hasLatin1Chars()
+ ? twoByteChars().append(base->latin1Chars(nogc) + off, len)
+ : twoByteChars().append(base->twoByteChars(nogc) + off, len);
+}
+
+inline bool
+StringBuffer::appendSubstring(JSString* base, size_t off, size_t len)
+{
+ JSLinearString* linear = base->ensureLinear(cx);
+ if (!linear)
+ return false;
+
+ return appendSubstring(linear, off, len);
+}
+
+inline bool
+StringBuffer::append(JSString* str)
+{
+ JSLinearString* linear = str->ensureLinear(cx);
+ if (!linear)
+ return false;
+
+ return append(linear);
+}
+
+/* ES5 9.8 ToString, appending the result to the string buffer. */
+extern bool
+ValueToStringBufferSlow(JSContext* cx, const Value& v, StringBuffer& sb);
+
+inline bool
+ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb)
+{
+ if (v.isString())
+ return sb.append(v.toString());
+
+ return ValueToStringBufferSlow(cx, v, sb);
+}
+
+/* ES5 9.8 ToString for booleans, appending the result to the string buffer. */
+inline bool
+BooleanToStringBuffer(bool b, StringBuffer& sb)
+{
+ return b ? sb.append("true") : sb.append("false");
+}
+
+} /* namespace js */
+
+#endif /* vm_StringBuffer_h */