LCOV - code coverage report
Current view: top level - js/src/vm - StringBuffer.h (source / functions) Hit Total Coverage
Test: output.info Lines: 78 137 56.9 %
Date: 2017-07-14 16:53:18 Functions: 35 61 57.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef vm_StringBuffer_h
       8             : #define vm_StringBuffer_h
       9             : 
      10             : #include "mozilla/DebugOnly.h"
      11             : #include "mozilla/MaybeOneOf.h"
      12             : 
      13             : #include "jscntxt.h"
      14             : 
      15             : #include "js/Vector.h"
      16             : 
      17             : namespace js {
      18             : 
      19             : /*
      20             :  * String builder that eagerly checks for over-allocation past the maximum
      21             :  * string length.
      22             :  *
      23             :  * Any operation which would exceed the maximum string length causes an
      24             :  * exception report on the context and results in a failed return value.
      25             :  *
      26             :  * Well-sized extractions (which waste no more than 1/4 of their char
      27             :  * buffer space) are guaranteed for strings built by this interface.
      28             :  * See |extractWellSized|.
      29             :  */
      30       24771 : class StringBuffer
      31             : {
      32             :     /*
      33             :      * The Vector's buffer may be either stolen or copied, so we need to use
      34             :      * TempAllocPolicy and account for the memory manually when stealing.
      35             :      */
      36             :     typedef Vector<Latin1Char, 64> Latin1CharBuffer;
      37             :     typedef Vector<char16_t, 32> TwoByteCharBuffer;
      38             : 
      39             :     JSContext* cx;
      40             : 
      41             :     /*
      42             :      * If Latin1 strings are enabled, cb starts out as a Latin1CharBuffer. When
      43             :      * a TwoByte char is appended, inflateChars() constructs a TwoByteCharBuffer
      44             :      * and copies the Latin1 chars.
      45             :      */
      46             :     mozilla::MaybeOneOf<Latin1CharBuffer, TwoByteCharBuffer> cb;
      47             : 
      48             : #ifdef DEBUG
      49             :     /*
      50             :      * Make sure ensureTwoByteChars() is called before calling
      51             :      * infallibleAppend(char16_t).
      52             :      */
      53             :     bool hasEnsuredTwoByteChars_;
      54             : #endif
      55             : 
      56             :     /* Number of reserve()'d chars, see inflateChars. */
      57             :     size_t reserved_;
      58             : 
      59             :     StringBuffer(const StringBuffer& other) = delete;
      60             :     void operator=(const StringBuffer& other) = delete;
      61             : 
      62      102938 :     MOZ_ALWAYS_INLINE bool isLatin1() const { return cb.constructed<Latin1CharBuffer>(); }
      63           0 :     MOZ_ALWAYS_INLINE bool isTwoByte() const { return !isLatin1(); }
      64             : 
      65       89007 :     MOZ_ALWAYS_INLINE Latin1CharBuffer& latin1Chars() { return cb.ref<Latin1CharBuffer>(); }
      66         114 :     MOZ_ALWAYS_INLINE TwoByteCharBuffer& twoByteChars() { return cb.ref<TwoByteCharBuffer>(); }
      67             : 
      68       31820 :     MOZ_ALWAYS_INLINE const Latin1CharBuffer& latin1Chars() const {
      69       31820 :         return cb.ref<Latin1CharBuffer>();
      70             :     }
      71          34 :     MOZ_ALWAYS_INLINE const TwoByteCharBuffer& twoByteChars() const {
      72          34 :         return cb.ref<TwoByteCharBuffer>();
      73             :     }
      74             : 
      75             :     MOZ_MUST_USE bool inflateChars();
      76             : 
      77             :   public:
      78       24771 :     explicit StringBuffer(JSContext* cx)
      79       24771 :       : cx(cx)
      80             : #ifdef DEBUG
      81             :       , hasEnsuredTwoByteChars_(false)
      82             : #endif
      83       24771 :       , reserved_(0)
      84             :     {
      85       24771 :         cb.construct<Latin1CharBuffer>(cx);
      86       24771 :     }
      87             : 
      88           0 :     void clear() {
      89           0 :         if (isLatin1())
      90           0 :             latin1Chars().clear();
      91             :         else
      92           0 :             twoByteChars().clear();
      93           0 :     }
      94         563 :     MOZ_MUST_USE bool reserve(size_t len) {
      95         563 :         if (len > reserved_)
      96         563 :             reserved_ = len;
      97         563 :         return isLatin1() ? latin1Chars().reserve(len) : twoByteChars().reserve(len);
      98             :     }
      99             :     MOZ_MUST_USE bool resize(size_t len) {
     100             :         return isLatin1() ? latin1Chars().resize(len) : twoByteChars().resize(len);
     101             :     }
     102        5678 :     bool empty() const {
     103        5678 :         return isLatin1() ? latin1Chars().empty() : twoByteChars().empty();
     104             :     }
     105       22532 :     size_t length() const {
     106       22532 :         return isLatin1() ? latin1Chars().length() : twoByteChars().length();
     107             :     }
     108        3394 :     char16_t getChar(size_t idx) const {
     109        3394 :         return isLatin1() ? latin1Chars()[idx] : twoByteChars()[idx];
     110             :     }
     111             : 
     112          15 :     MOZ_MUST_USE bool ensureTwoByteChars() {
     113          15 :         if (isLatin1() && !inflateChars())
     114           0 :             return false;
     115             : 
     116             : #ifdef DEBUG
     117          15 :         hasEnsuredTwoByteChars_ = true;
     118             : #endif
     119          15 :         return true;
     120             :     }
     121             : 
     122        7322 :     MOZ_MUST_USE bool append(const char16_t c) {
     123        7322 :         if (isLatin1()) {
     124        7322 :             if (c <= JSString::MAX_LATIN1_CHAR)
     125        7322 :                 return latin1Chars().append(Latin1Char(c));
     126           0 :             if (!inflateChars())
     127           0 :                 return false;
     128             :         }
     129           0 :         return twoByteChars().append(c);
     130             :     }
     131        8716 :     MOZ_MUST_USE bool append(Latin1Char c) {
     132        8716 :         return isLatin1() ? latin1Chars().append(c) : twoByteChars().append(c);
     133             :     }
     134        8412 :     MOZ_MUST_USE bool append(char c) {
     135        8412 :         return append(Latin1Char(c));
     136             :     }
     137             : 
     138             :     inline MOZ_MUST_USE bool append(const char16_t* begin, const char16_t* end);
     139             : 
     140         282 :     MOZ_MUST_USE bool append(const char16_t* chars, size_t len) {
     141         282 :         return append(chars, chars + len);
     142             :     }
     143             : 
     144       14715 :     MOZ_MUST_USE bool append(const Latin1Char* begin, const Latin1Char* end) {
     145       14715 :         return isLatin1() ? latin1Chars().append(begin, end) : twoByteChars().append(begin, end);
     146             :     }
     147       14590 :     MOZ_MUST_USE bool append(const Latin1Char* chars, size_t len) {
     148       14590 :         return append(chars, chars + len);
     149             :     }
     150             : 
     151             :     MOZ_MUST_USE bool append(const JS::ConstCharPtr chars, size_t len) {
     152             :         return append(chars.get(), chars.get() + len);
     153             :     }
     154           3 :     MOZ_MUST_USE bool appendN(Latin1Char c, size_t n) {
     155           3 :         return isLatin1() ? latin1Chars().appendN(c, n) : twoByteChars().appendN(c, n);
     156             :     }
     157             : 
     158             :     inline MOZ_MUST_USE bool append(JSString* str);
     159             :     inline MOZ_MUST_USE bool append(JSLinearString* str);
     160             :     inline MOZ_MUST_USE bool appendSubstring(JSString* base, size_t off, size_t len);
     161             :     inline MOZ_MUST_USE bool appendSubstring(JSLinearString* base, size_t off, size_t len);
     162             : 
     163       14590 :     MOZ_MUST_USE bool append(const char* chars, size_t len) {
     164       14590 :         return append(reinterpret_cast<const Latin1Char*>(chars), len);
     165             :     }
     166             : 
     167             :     template <size_t ArrayLength>
     168       14547 :     MOZ_MUST_USE bool append(const char (&array)[ArrayLength]) {
     169       14547 :         return append(array, ArrayLength - 1); /* No trailing '\0'. */
     170             :     }
     171             : 
     172             :     /* Infallible variants usable when the corresponding space is reserved. */
     173           0 :     void infallibleAppend(Latin1Char c) {
     174           0 :         if (isLatin1())
     175           0 :             latin1Chars().infallibleAppend(c);
     176             :         else
     177           0 :             twoByteChars().infallibleAppend(c);
     178           0 :     }
     179           0 :     void infallibleAppend(char c) {
     180           0 :         infallibleAppend(Latin1Char(c));
     181           0 :     }
     182           0 :     void infallibleAppend(const Latin1Char* chars, size_t len) {
     183           0 :         if (isLatin1())
     184           0 :             latin1Chars().infallibleAppend(chars, len);
     185             :         else
     186           0 :             twoByteChars().infallibleAppend(chars, len);
     187           0 :     }
     188             :     void infallibleAppend(const char* chars, size_t len) {
     189             :         infallibleAppend(reinterpret_cast<const Latin1Char*>(chars), len);
     190             :     }
     191             : 
     192             :     void infallibleAppendSubstring(JSLinearString* base, size_t off, size_t len);
     193             : 
     194             :     /*
     195             :      * Because inflation is fallible, these methods should only be used after
     196             :      * calling ensureTwoByteChars().
     197             :      */
     198           0 :     void infallibleAppend(const char16_t* chars, size_t len) {
     199           0 :         MOZ_ASSERT(hasEnsuredTwoByteChars_);
     200           0 :         twoByteChars().infallibleAppend(chars, len);
     201           0 :     }
     202           0 :     void infallibleAppend(char16_t c) {
     203           0 :         MOZ_ASSERT(hasEnsuredTwoByteChars_);
     204           0 :         twoByteChars().infallibleAppend(c);
     205           0 :     }
     206             : 
     207          79 :     bool isUnderlyingBufferLatin1() const { return isLatin1(); }
     208             : 
     209           0 :     char16_t* rawTwoByteBegin() { return twoByteChars().begin(); }
     210             :     char16_t* rawTwoByteEnd() { return twoByteChars().end(); }
     211           0 :     const char16_t* rawTwoByteBegin() const { return twoByteChars().begin(); }
     212           0 :     const char16_t* rawTwoByteEnd() const { return twoByteChars().end(); }
     213             : 
     214             :     Latin1Char* rawLatin1Begin() { return latin1Chars().begin(); }
     215             :     Latin1Char* rawLatin1End() { return latin1Chars().end(); }
     216         125 :     const Latin1Char* rawLatin1Begin() const { return latin1Chars().begin(); }
     217         125 :     const Latin1Char* rawLatin1End() const { return latin1Chars().end(); }
     218             : 
     219             :     /*
     220             :      * Creates a string from the characters in this buffer, then (regardless
     221             :      * whether string creation succeeded or failed) empties the buffer.
     222             :      */
     223             :     JSFlatString* finishString();
     224             : 
     225             :     /* Identical to finishString() except that an atom is created. */
     226             :     JSAtom* finishAtom();
     227             : 
     228             :     /*
     229             :      * Creates a raw string from the characters in this buffer.  The string is
     230             :      * exactly the characters in this buffer (inflated to TwoByte), it is *not*
     231             :      * null-terminated unless the last appended character was '\0'.
     232             :      */
     233             :     char16_t* stealChars();
     234             : };
     235             : 
     236             : inline bool
     237         282 : StringBuffer::append(const char16_t* begin, const char16_t* end)
     238             : {
     239         282 :     MOZ_ASSERT(begin <= end);
     240         282 :     if (isLatin1()) {
     241             :         while (true) {
     242        2083 :             if (begin >= end)
     243         271 :                 return true;
     244         906 :             if (*begin > JSString::MAX_LATIN1_CHAR)
     245           0 :                 break;
     246         906 :             if (!latin1Chars().append(*begin))
     247           0 :                 return false;
     248         906 :             ++begin;
     249             :         }
     250           0 :         if (!inflateChars())
     251           0 :             return false;
     252             :     }
     253          11 :     return twoByteChars().append(begin, end);
     254             : }
     255             : 
     256             : inline bool
     257       20131 : StringBuffer::append(JSLinearString* str)
     258             : {
     259       40262 :     JS::AutoCheckCannotGC nogc;
     260       20131 :     if (isLatin1()) {
     261       20127 :         if (str->hasLatin1Chars())
     262       20123 :             return latin1Chars().append(str->latin1Chars(nogc), str->length());
     263           4 :         if (!inflateChars())
     264           0 :             return false;
     265             :     }
     266           8 :     return str->hasLatin1Chars()
     267          12 :            ? twoByteChars().append(str->latin1Chars(nogc), str->length())
     268          12 :            : twoByteChars().append(str->twoByteChars(nogc), str->length());
     269             : }
     270             : 
     271             : inline void
     272           0 : StringBuffer::infallibleAppendSubstring(JSLinearString* base, size_t off, size_t len)
     273             : {
     274           0 :     MOZ_ASSERT(off + len <= base->length());
     275           0 :     MOZ_ASSERT_IF(base->hasTwoByteChars(), isTwoByte());
     276             : 
     277           0 :     JS::AutoCheckCannotGC nogc;
     278           0 :     if (base->hasLatin1Chars())
     279           0 :         infallibleAppend(base->latin1Chars(nogc) + off, len);
     280             :     else
     281           0 :         infallibleAppend(base->twoByteChars(nogc) + off, len);
     282           0 : }
     283             : 
     284             : inline bool
     285         368 : StringBuffer::appendSubstring(JSLinearString* base, size_t off, size_t len)
     286             : {
     287         368 :     MOZ_ASSERT(off + len <= base->length());
     288             : 
     289         736 :     JS::AutoCheckCannotGC nogc;
     290         368 :     if (isLatin1()) {
     291         368 :         if (base->hasLatin1Chars())
     292         368 :             return latin1Chars().append(base->latin1Chars(nogc) + off, len);
     293           0 :         if (!inflateChars())
     294           0 :             return false;
     295             :     }
     296           0 :     return base->hasLatin1Chars()
     297           0 :            ? twoByteChars().append(base->latin1Chars(nogc) + off, len)
     298           0 :            : twoByteChars().append(base->twoByteChars(nogc) + off, len);
     299             : }
     300             : 
     301             : inline bool
     302             : StringBuffer::appendSubstring(JSString* base, size_t off, size_t len)
     303             : {
     304             :     JSLinearString* linear = base->ensureLinear(cx);
     305             :     if (!linear)
     306             :         return false;
     307             : 
     308             :     return appendSubstring(linear, off, len);
     309             : }
     310             : 
     311             : inline bool
     312        1665 : StringBuffer::append(JSString* str)
     313             : {
     314        1665 :     JSLinearString* linear = str->ensureLinear(cx);
     315        1665 :     if (!linear)
     316           0 :         return false;
     317             : 
     318        1665 :     return append(linear);
     319             : }
     320             : 
     321             : /* ES5 9.8 ToString, appending the result to the string buffer. */
     322             : extern bool
     323             : ValueToStringBufferSlow(JSContext* cx, const Value& v, StringBuffer& sb);
     324             : 
     325             : inline bool
     326           0 : ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb)
     327             : {
     328           0 :     if (v.isString())
     329           0 :         return sb.append(v.toString());
     330             : 
     331           0 :     return ValueToStringBufferSlow(cx, v, sb);
     332             : }
     333             : 
     334             : /* ES5 9.8 ToString for booleans, appending the result to the string buffer. */
     335             : inline bool
     336           0 : BooleanToStringBuffer(bool b, StringBuffer& sb)
     337             : {
     338           0 :     return b ? sb.append("true") : sb.append("false");
     339             : }
     340             : 
     341             : }  /* namespace js */
     342             : 
     343             : #endif /* vm_StringBuffer_h */

Generated by: LCOV version 1.13