DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (7b5b4eed4707)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/* -*- 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 vm_InlineCharBuffer_inl_h
#define vm_InlineCharBuffer_inl_h

#include "vm/StringType-inl.h"

namespace js {

template <typename CharT>
struct MaximumInlineLength;

template <>
struct MaximumInlineLength<Latin1Char> {
  static constexpr size_t value = JSFatInlineString::MAX_LENGTH_LATIN1;
};

template <>
struct MaximumInlineLength<char16_t> {
  static constexpr size_t value = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
};

// Character buffer class used for ToLowerCase and ToUpperCase operations, as
// well as other string operations where the final string length is known in
// advance.
//
// Case conversion operations normally return a string with the same length as
// the input string. To avoid over-allocation, we optimistically allocate an
// array with same size as the input string and only when we detect special
// casing characters, which can change the output string length, we reallocate
// the output buffer to the final string length.
//
// As a further mean to improve runtime performance, the character buffer
// contains an inline storage, so we don't need to heap-allocate an array when
// a JSInlineString will be used for the output string.
//
// Why not use mozilla::Vector instead? mozilla::Vector doesn't provide enough
// fine-grained control to avoid over-allocation when (re)allocating for exact
// buffer sizes. This led to visible performance regressions in ยต-benchmarks.
template <typename CharT>
class MOZ_NON_PARAM InlineCharBuffer {
  static constexpr size_t InlineCapacity = MaximumInlineLength<CharT>::value;

  CharT inlineStorage[InlineCapacity];
  UniquePtr<CharT[], JS::FreePolicy> heapStorage;

#ifdef DEBUG
  // In debug mode, we keep track of the requested string lengths to ensure
  // all character buffer methods are called in the correct order and with
  // the expected argument values.
  size_t lastRequestedLength = 0;

  void assertValidRequest(size_t expectedLastLength, size_t length) {
    MOZ_ASSERT(length >= expectedLastLength, "cannot shrink requested length");
    MOZ_ASSERT(lastRequestedLength == expectedLastLength);
    lastRequestedLength = length;
  }
#else
  void assertValidRequest(size_t expectedLastLength, size_t length) {}
#endif

 public:
  CharT* get() { return heapStorage ? heapStorage.get() : inlineStorage; }

  bool maybeAlloc(JSContext* cx, size_t length) {
    assertValidRequest(0, length);

    if (length <= InlineCapacity) {
      return true;
    }

    MOZ_ASSERT(!heapStorage, "heap storage already allocated");
    heapStorage = cx->make_pod_array<CharT>(length + 1, js::StringBufferArena);
    return !!heapStorage;
  }

  bool maybeRealloc(JSContext* cx, size_t oldLength, size_t newLength) {
    assertValidRequest(oldLength, newLength);

    if (newLength <= InlineCapacity) {
      return true;
    }

    if (!heapStorage) {
      heapStorage =
          cx->make_pod_array<CharT>(newLength + 1, js::StringBufferArena);
      if (!heapStorage) {
        return false;
      }

      MOZ_ASSERT(oldLength <= InlineCapacity);
      mozilla::PodCopy(heapStorage.get(), inlineStorage, oldLength);
      return true;
    }

    CharT* oldChars = heapStorage.release();
    CharT* newChars = cx->pod_realloc(oldChars, oldLength + 1, newLength + 1,
                                      js::StringBufferArena);
    if (!newChars) {
      js_free(oldChars);
      return false;
    }

    heapStorage.reset(newChars);
    return true;
  }

  JSString* toStringDontDeflate(JSContext* cx, size_t length) {
    MOZ_ASSERT(length == lastRequestedLength);

    if (JSInlineString::lengthFits<CharT>(length)) {
      MOZ_ASSERT(
          !heapStorage,
          "expected only inline storage when length fits in inline string");

      if (JSString* str = TryEmptyOrStaticString(cx, inlineStorage, length)) {
        return str;
      }

      mozilla::Range<const CharT> range(inlineStorage, length);
      return NewInlineString<CanGC>(cx, range);
    }

    MOZ_ASSERT(heapStorage,
               "heap storage was not allocated for non-inline string");

    heapStorage.get()[length] = '\0';  // Null-terminate
    return NewStringDontDeflate<CanGC>(cx, std::move(heapStorage), length);
  }

  JSString* toString(JSContext* cx, size_t length) {
    MOZ_ASSERT(length == lastRequestedLength);

    if (JSInlineString::lengthFits<CharT>(length)) {
      MOZ_ASSERT(
          !heapStorage,
          "expected only inline storage when length fits in inline string");

      return NewStringCopyN<CanGC>(cx, inlineStorage, length);
    }

    MOZ_ASSERT(heapStorage,
               "heap storage was not allocated for non-inline string");

    heapStorage.get()[length] = '\0';  // Null-terminate
    return NewString<CanGC>(cx, std::move(heapStorage), length);
  }
};

} /* namespace js */

#endif /* vm_InlineCharBuffer_inl_h */