tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FakeString.h (9714B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_dom_FakeString_h__
      8 #define mozilla_dom_FakeString_h__
      9 
     10 #include "js/String.h"
     11 #include "mozilla/RefPtr.h"
     12 #include "mozilla/Span.h"
     13 #include "mozilla/StringBuffer.h"
     14 #include "nsString.h"
     15 #include "nsTStringRepr.h"
     16 
     17 namespace mozilla::dom::binding_detail {
     18 // A struct that has a layout compatible with nsAString, so that
     19 // reinterpret-casting a FakeString as a const nsAString is safe, but much
     20 // faster constructor and destructor behavior. FakeString uses inline storage
     21 // for small strings and an StringBuffer for longer strings.  It can also
     22 // point to a literal (static-lifetime) string that's compiled into the binary,
     23 // or point at the buffer of an nsAString whose lifetime is longer than that of
     24 // the FakeString.
     25 template <typename CharT>
     26 struct MOZ_GSL_OWNER FakeString {
     27  using char_type = CharT;
     28  using string_type = nsTString<CharT>;
     29  using size_type = typename string_type::size_type;
     30  using DataFlags = typename string_type::DataFlags;
     31  using ClassFlags = typename string_type::ClassFlags;
     32  using AString = nsTSubstring<CharT>;
     33  using LengthStorage = mozilla::detail::nsTStringLengthStorage<CharT>;
     34 
     35  static const size_t kInlineCapacity = 64;
     36  using AutoString = nsTAutoStringN<CharT, kInlineCapacity>;
     37 
     38  FakeString()
     39      : mDataFlags(DataFlags::TERMINATED),
     40        mClassFlags(ClassFlags::INLINE),
     41        mInlineCapacity(kInlineCapacity - 1) {}
     42 
     43  ~FakeString() {
     44    if (mDataFlags & DataFlags::REFCOUNTED) {
     45      MOZ_ASSERT(mDataInitialized);
     46      StringBuffer::FromData(mData)->Release();
     47    }
     48  }
     49 
     50  // Share aString's string buffer, if it has one; otherwise, make this string
     51  // depend upon aString's data.  aString should outlive this instance of
     52  // FakeString.
     53  void ShareOrDependUpon(const AString& aString) {
     54    RefPtr<StringBuffer> sharedBuffer = aString.GetStringBuffer();
     55    if (!sharedBuffer) {
     56      InitData(aString.BeginReading(), aString.Length());
     57      if (!aString.IsTerminated()) {
     58        mDataFlags &= ~DataFlags::TERMINATED;
     59      }
     60    } else {
     61      AssignFromStringBuffer(sharedBuffer.forget(), aString.Length());
     62    }
     63  }
     64 
     65  void Truncate() { InitData(string_type::char_traits::sEmptyBuffer, 0); }
     66 
     67  void SetIsVoid(bool aValue) {
     68    MOZ_ASSERT(aValue, "We don't support SetIsVoid(false) on FakeString!");
     69    Truncate();
     70    mDataFlags |= DataFlags::VOIDED;
     71  }
     72 
     73  char_type* BeginWriting() {
     74    MOZ_ASSERT(IsMutable());
     75    MOZ_ASSERT(mDataInitialized);
     76    return mData;
     77  }
     78 
     79  size_type Length() const { return mLength; }
     80 
     81  operator mozilla::Span<const char_type>() const {
     82    MOZ_ASSERT(mDataInitialized);
     83    // Explicitly specify template argument here to avoid instantiating
     84    // Span<char_type> first and then implicitly converting to Span<const
     85    // char_type>
     86    return mozilla::Span<const char_type>{mData, Length()};
     87  }
     88 
     89  mozilla::Result<mozilla::BulkWriteHandle<CharT>, nsresult> BulkWrite(
     90      size_type aCapacity, size_type aPrefixToPreserve, bool aAllowShrinking) {
     91    MOZ_ASSERT(!mDataInitialized);
     92    InitData(mStorage, 0);
     93    mDataFlags |= DataFlags::INLINE;
     94    return ToAStringPtr()->BulkWrite(aCapacity, aPrefixToPreserve,
     95                                     aAllowShrinking);
     96  }
     97 
     98  // Reserve space to write aLength chars, not including null-terminator.
     99  bool SetLength(size_type aLength, mozilla::fallible_t const&) {
    100    // Use mStorage for small strings.
    101    if (aLength < kInlineCapacity) {
    102      InitData(mStorage, aLength);
    103      mDataFlags |= DataFlags::INLINE;
    104    } else {
    105      RefPtr<StringBuffer> buf =
    106          StringBuffer::Alloc((aLength + 1) * sizeof(char_type));
    107      if (MOZ_UNLIKELY(!buf)) {
    108        return false;
    109      }
    110 
    111      AssignFromStringBuffer(buf.forget(), aLength);
    112    }
    113 
    114    MOZ_ASSERT(mDataInitialized);
    115    mData[mLength] = char_type(0);
    116    return true;
    117  }
    118 
    119  // Returns false on allocation failure.
    120  bool EnsureMutable() {
    121    MOZ_ASSERT(mDataInitialized);
    122 
    123    if (IsMutable()) {
    124      return true;
    125    }
    126 
    127    RefPtr<StringBuffer> buffer;
    128    if (mDataFlags & DataFlags::REFCOUNTED) {
    129      // Make sure we'll drop it when we're done.
    130      buffer = dont_AddRef(StringBuffer::FromData(mData));
    131      // And make sure we don't release it twice by accident.
    132    }
    133    const char_type* oldChars = mData;
    134 
    135    mDataFlags = DataFlags::TERMINATED;
    136 #ifdef DEBUG
    137    // Reset mDataInitialized because we're explicitly reinitializing
    138    // it via the SetLength call.
    139    mDataInitialized = false;
    140 #endif  // DEBUG
    141    // SetLength will make sure we have our own buffer to work with.  Note that
    142    // we may be transitioning from having a (short) readonly stringbuffer to
    143    // our inline storage or whatnot.  That's all fine; SetLength is responsible
    144    // for setting up our flags correctly.
    145    if (!SetLength(Length(), fallible)) {
    146      return false;
    147    }
    148    MOZ_ASSERT(oldChars != mData, "Should have new chars now!");
    149    MOZ_ASSERT(IsMutable(), "Why are we still not mutable?");
    150    memcpy(mData, oldChars, Length() * sizeof(char_type));
    151    return true;
    152  }
    153 
    154  void AssignFromStringBuffer(already_AddRefed<StringBuffer> aBuffer,
    155                              size_t aLength) {
    156    InitData(static_cast<char_type*>(aBuffer.take()->Data()), aLength);
    157    mDataFlags |= DataFlags::REFCOUNTED;
    158  }
    159 
    160  // The preferred way to assign literals to a FakeString.  This should only be
    161  // called with actual C++ literal strings (i.e. u"stuff") or character arrays
    162  // that originally come from passing such literal strings.
    163  template <int N>
    164  void AssignLiteral(const char_type (&aData)[N]) {
    165    AssignLiteral(aData, N - 1);
    166  }
    167 
    168  // Assign a literal to a FakeString when it's not an actual literal
    169  // in the code, but is known to be a literal somehow (e.g. it came
    170  // from an nsAString that tested true for IsLiteral()).
    171  void AssignLiteral(const char_type* aData, size_t aLength) {
    172    InitData(aData, aLength);
    173    mDataFlags |= DataFlags::LITERAL;
    174  }
    175 
    176  // If this ever changes, change the corresponding code in the
    177  // Optional<nsA[C]String> specialization as well.
    178  const AString* ToAStringPtr() const {
    179    return reinterpret_cast<const string_type*>(this);
    180  }
    181 
    182  operator const AString&() const { return *ToAStringPtr(); }
    183 
    184 private:
    185  AString* ToAStringPtr() { return reinterpret_cast<string_type*>(this); }
    186 
    187  // mData is left uninitialized for optimization purposes.
    188  MOZ_INIT_OUTSIDE_CTOR char_type* mData;
    189  // mLength is left uninitialized for optimization purposes.
    190  MOZ_INIT_OUTSIDE_CTOR uint32_t mLength;
    191  DataFlags mDataFlags;
    192  const ClassFlags mClassFlags;
    193 
    194  const uint32_t mInlineCapacity;
    195  char_type mStorage[kInlineCapacity];
    196 #ifdef DEBUG
    197  bool mDataInitialized = false;
    198 #endif  // DEBUG
    199 
    200  FakeString(const FakeString& other) = delete;
    201  void operator=(const FakeString& other) = delete;
    202 
    203  void InitData(const char_type* aData, size_type aLength) {
    204    MOZ_ASSERT(aLength <= LengthStorage::kMax, "string is too large");
    205    MOZ_ASSERT(mDataFlags == DataFlags::TERMINATED);
    206    MOZ_ASSERT(!mDataInitialized);
    207    mData = const_cast<char_type*>(aData);
    208    mLength = uint32_t(aLength);
    209 #ifdef DEBUG
    210    mDataInitialized = true;
    211 #endif  // DEBUG
    212  }
    213 
    214  bool IsMutable() {
    215    return (mDataFlags & DataFlags::INLINE) ||
    216           ((mDataFlags & DataFlags::REFCOUNTED) &&
    217            !StringBuffer::FromData(mData)->IsReadonly());
    218  }
    219 
    220  friend class NonNull<AString>;
    221 
    222  // A class to use for our static asserts to ensure our object layout
    223  // matches that of nsString.
    224  class StringAsserter;
    225  friend class StringAsserter;
    226 
    227  class StringAsserter : public AutoString {
    228   public:
    229    static void StaticAsserts() {
    230      static_assert(sizeof(AutoString) == sizeof(FakeString),
    231                    "Should be binary compatible with nsTAutoString");
    232      static_assert(
    233          offsetof(FakeString, mInlineCapacity) == sizeof(string_type),
    234          "FakeString should include all nsString members");
    235      static_assert(
    236          offsetof(FakeString, mData) == offsetof(StringAsserter, mData),
    237          "Offset of mData should match");
    238      static_assert(
    239          offsetof(FakeString, mLength) == offsetof(StringAsserter, mLength),
    240          "Offset of mLength should match");
    241      static_assert(offsetof(FakeString, mDataFlags) ==
    242                        offsetof(StringAsserter, mDataFlags),
    243                    "Offset of mDataFlags should match");
    244      static_assert(offsetof(FakeString, mClassFlags) ==
    245                        offsetof(StringAsserter, mClassFlags),
    246                    "Offset of mClassFlags should match");
    247      static_assert(offsetof(FakeString, mInlineCapacity) ==
    248                        offsetof(StringAsserter, mInlineCapacity),
    249                    "Offset of mInlineCapacity should match");
    250      static_assert(
    251          offsetof(FakeString, mStorage) == offsetof(StringAsserter, mStorage),
    252          "Offset of mStorage should match");
    253      static_assert(JS::MaxStringLength <= LengthStorage::kMax,
    254                    "JS::MaxStringLength fits in a nsTString");
    255    }
    256  };
    257 };
    258 }  // namespace mozilla::dom::binding_detail
    259 
    260 namespace mozilla {
    261 
    262 template <typename CharT>
    263 inline void AssignFromStringBuffer(
    264    StringBuffer* aBuffer, size_t aLength,
    265    dom::binding_detail::FakeString<CharT>& aDest) {
    266  aDest.AssignFromStringBuffer(do_AddRef(aBuffer), aLength);
    267 }
    268 
    269 }  // namespace mozilla
    270 
    271 #endif /* mozilla_dom_FakeString_h__ */