[SM91] Update to Spidermonkey 91.1.3 APIs
[0ad.git] / libraries / source / spidermonkey / include-win32-release / mozilla / BaseProfileJSONWriter.h
blob5dcf06f3f33be6d7dacd574b69463a0f2862c89c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef BASEPROFILEJSONWRITER_H
7 #define BASEPROFILEJSONWRITER_H
9 #include "mozilla/HashFunctions.h"
10 #include "mozilla/HashTable.h"
11 #include "mozilla/JSONWriter.h"
12 #include "mozilla/TimeStamp.h"
13 #include "mozilla/UniquePtr.h"
15 #include <functional>
16 #include <ostream>
17 #include <string_view>
19 namespace mozilla {
20 namespace baseprofiler {
22 class SpliceableJSONWriter;
24 // On average, profile JSONs are large enough such that we want to avoid
25 // reallocating its buffer when expanding. Additionally, the contents of the
26 // profile are not accessed until the profile is entirely written. For these
27 // reasons we use a chunked writer that keeps an array of chunks, which is
28 // concatenated together after writing is finished.
29 class ChunkedJSONWriteFunc final : public JSONWriteFunc {
30 public:
31 friend class SpliceableJSONWriter;
33 ChunkedJSONWriteFunc() : mChunkPtr{nullptr}, mChunkEnd{nullptr} {
34 AllocChunk(kChunkSize);
37 bool IsEmpty() const {
38 MOZ_ASSERT_IF(!mChunkPtr, !mChunkEnd && mChunkList.length() == 0 &&
39 mChunkLengths.length() == 0);
40 return !mChunkPtr;
43 void Write(const Span<const char>& aStr) override {
44 MOZ_ASSERT(mChunkPtr >= mChunkList.back().get() && mChunkPtr <= mChunkEnd);
45 MOZ_ASSERT(mChunkEnd >= mChunkList.back().get() + mChunkLengths.back());
46 MOZ_ASSERT(*mChunkPtr == '\0');
48 // Most strings to be written are small, but subprocess profiles (e.g.,
49 // from the content process in e10s) may be huge. If the string is larger
50 // than a chunk, allocate its own chunk.
51 char* newPtr;
52 if (aStr.size() >= kChunkSize) {
53 AllocChunk(aStr.size() + 1);
54 newPtr = mChunkPtr + aStr.size();
55 } else {
56 newPtr = mChunkPtr + aStr.size();
57 if (newPtr >= mChunkEnd) {
58 AllocChunk(kChunkSize);
59 newPtr = mChunkPtr + aStr.size();
63 memcpy(mChunkPtr, aStr.data(), aStr.size());
64 *newPtr = '\0';
65 mChunkPtr = newPtr;
66 mChunkLengths.back() += aStr.size();
68 void CopyDataIntoLazilyAllocatedBuffer(
69 const std::function<char*(size_t)>& aAllocator) const {
70 // Request a buffer for the full content plus a null terminator.
71 MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
72 size_t totalLen = 1;
73 for (size_t i = 0; i < mChunkLengths.length(); i++) {
74 MOZ_ASSERT(strlen(mChunkList[i].get()) == mChunkLengths[i]);
75 totalLen += mChunkLengths[i];
77 char* ptr = aAllocator(totalLen);
79 if (!ptr) {
80 // Failed to allocate memory.
81 return;
84 for (size_t i = 0; i < mChunkList.length(); i++) {
85 size_t len = mChunkLengths[i];
86 memcpy(ptr, mChunkList[i].get(), len);
87 ptr += len;
89 *ptr = '\0';
91 UniquePtr<char[]> CopyData() const {
92 UniquePtr<char[]> c;
93 CopyDataIntoLazilyAllocatedBuffer([&](size_t allocationSize) {
94 c = MakeUnique<char[]>(allocationSize);
95 return c.get();
96 });
97 return c;
99 void Take(ChunkedJSONWriteFunc&& aOther) {
100 for (size_t i = 0; i < aOther.mChunkList.length(); i++) {
101 MOZ_ALWAYS_TRUE(mChunkLengths.append(aOther.mChunkLengths[i]));
102 MOZ_ALWAYS_TRUE(mChunkList.append(std::move(aOther.mChunkList[i])));
104 mChunkPtr = mChunkList.back().get() + mChunkLengths.back();
105 mChunkEnd = mChunkPtr;
106 aOther.mChunkPtr = nullptr;
107 aOther.mChunkEnd = nullptr;
108 aOther.mChunkList.clear();
109 aOther.mChunkLengths.clear();
112 private:
113 void AllocChunk(size_t aChunkSize) {
114 MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
115 UniquePtr<char[]> newChunk = MakeUnique<char[]>(aChunkSize);
116 mChunkPtr = newChunk.get();
117 mChunkEnd = mChunkPtr + aChunkSize;
118 *mChunkPtr = '\0';
119 MOZ_ALWAYS_TRUE(mChunkLengths.append(0));
120 MOZ_ALWAYS_TRUE(mChunkList.append(std::move(newChunk)));
123 static const size_t kChunkSize = 4096 * 512;
125 // Pointer for writing inside the current chunk.
127 // The current chunk is always at the back of mChunkList, i.e.,
128 // mChunkList.back() <= mChunkPtr <= mChunkEnd.
129 char* mChunkPtr;
131 // Pointer to the end of the current chunk.
133 // The current chunk is always at the back of mChunkList, i.e.,
134 // mChunkEnd >= mChunkList.back() + mChunkLengths.back().
135 char* mChunkEnd;
137 // List of chunks and their lengths.
139 // For all i, the length of the string in mChunkList[i] is
140 // mChunkLengths[i].
141 Vector<UniquePtr<char[]>> mChunkList;
142 Vector<size_t> mChunkLengths;
145 struct OStreamJSONWriteFunc final : public JSONWriteFunc {
146 explicit OStreamJSONWriteFunc(std::ostream& aStream) : mStream(aStream) {}
148 void Write(const Span<const char>& aStr) override {
149 std::string_view sv(aStr.data(), aStr.size());
150 mStream << sv;
153 std::ostream& mStream;
156 class UniqueJSONStrings;
158 class SpliceableJSONWriter : public JSONWriter {
159 public:
160 explicit SpliceableJSONWriter(UniquePtr<JSONWriteFunc> aWriter)
161 : JSONWriter(std::move(aWriter)) {}
163 void StartBareList(CollectionStyle aStyle = MultiLineStyle) {
164 StartCollection(scEmptyString, scEmptyString, aStyle);
167 void EndBareList() { EndCollection(scEmptyString); }
169 // This function must be used to correctly stream timestamps in profiles.
170 // Null timestamps don't output anything.
171 void TimeProperty(const Span<const char>& aName, const TimeStamp& aTime) {
172 if (!aTime.IsNull()) {
173 DoubleProperty(aName,
174 (aTime - TimeStamp::ProcessCreation()).ToMilliseconds());
178 void NullElements(uint32_t aCount) {
179 for (uint32_t i = 0; i < aCount; i++) {
180 NullElement();
184 void Splice(const Span<const char>& aStr) {
185 Separator();
186 WriteFunc()->Write(aStr);
187 mNeedComma[mDepth] = true;
190 void Splice(const char* aStr, size_t aLen) {
191 Separator();
192 WriteFunc()->Write(Span<const char>(aStr, aLen));
193 mNeedComma[mDepth] = true;
196 // Splice the given JSON directly in, without quoting.
197 void SplicedJSONProperty(const Span<const char>& aMaybePropertyName,
198 const Span<const char>& aJsonValue) {
199 Scalar(aMaybePropertyName, aJsonValue);
202 void CopyAndSplice(const ChunkedJSONWriteFunc& aFunc) {
203 Separator();
204 for (size_t i = 0; i < aFunc.mChunkList.length(); i++) {
205 WriteFunc()->Write(
206 Span<const char>(aFunc.mChunkList[i].get(), aFunc.mChunkLengths[i]));
208 mNeedComma[mDepth] = true;
211 // Takes the chunks from aFunc and write them. If move is not possible
212 // (e.g., using OStreamJSONWriteFunc), aFunc's chunks are copied and its
213 // storage cleared.
214 virtual void TakeAndSplice(ChunkedJSONWriteFunc&& aFunc) {
215 Separator();
216 for (size_t i = 0; i < aFunc.mChunkList.length(); i++) {
217 WriteFunc()->Write(
218 Span<const char>(aFunc.mChunkList[i].get(), aFunc.mChunkLengths[i]));
220 aFunc.mChunkPtr = nullptr;
221 aFunc.mChunkEnd = nullptr;
222 aFunc.mChunkList.clear();
223 aFunc.mChunkLengths.clear();
224 mNeedComma[mDepth] = true;
227 // Set (or reset) the pointer to a UniqueJSONStrings.
228 void SetUniqueStrings(UniqueJSONStrings& aUniqueStrings) {
229 MOZ_RELEASE_ASSERT(!mUniqueStrings);
230 mUniqueStrings = &aUniqueStrings;
233 // Set (or reset) the pointer to a UniqueJSONStrings.
234 void ResetUniqueStrings() {
235 MOZ_RELEASE_ASSERT(mUniqueStrings);
236 mUniqueStrings = nullptr;
239 // Add `aStr` to the unique-strings list (if not already there), and write its
240 // index as a named object property.
241 inline void UniqueStringProperty(const Span<const char>& aName,
242 const Span<const char>& aStr);
244 // Add `aStr` to the unique-strings list (if not already there), and write its
245 // index as an array element.
246 inline void UniqueStringElement(const Span<const char>& aStr);
248 private:
249 UniqueJSONStrings* mUniqueStrings = nullptr;
252 class SpliceableChunkedJSONWriter final : public SpliceableJSONWriter {
253 public:
254 explicit SpliceableChunkedJSONWriter()
255 : SpliceableJSONWriter(MakeUnique<ChunkedJSONWriteFunc>()) {}
257 // Access the ChunkedJSONWriteFunc as reference-to-const, usually to copy data
258 // out.
259 const ChunkedJSONWriteFunc& ChunkedWriteFunc() const {
260 MOZ_ASSERT(!mTaken);
261 // The WriteFunc was non-fallibly allocated as a ChunkedJSONWriteFunc in the
262 // only constructor above, so it's safe to cast to ChunkedJSONWriteFunc*.
263 return *static_cast<const ChunkedJSONWriteFunc*>(WriteFunc());
266 // Access the ChunkedJSONWriteFunc as rvalue-reference, usually to take its
267 // data out. This writer shouldn't be used anymore after this.
268 ChunkedJSONWriteFunc&& TakeChunkedWriteFunc() {
269 #ifdef DEBUG
270 MOZ_ASSERT(!mTaken);
271 mTaken = true;
272 #endif //
273 // The WriteFunc was non-fallibly allocated as a ChunkedJSONWriteFunc in the
274 // only constructor above, so it's safe to cast to ChunkedJSONWriteFunc*.
275 return std::move(*static_cast<ChunkedJSONWriteFunc*>(WriteFunc()));
278 // Adopts the chunks from aFunc without copying.
279 void TakeAndSplice(ChunkedJSONWriteFunc&& aFunc) override {
280 MOZ_ASSERT(!mTaken);
281 Separator();
282 // The WriteFunc was non-fallibly allocated as a ChunkedJSONWriteFunc in the
283 // only constructor above, so it's safe to cast to ChunkedJSONWriteFunc*.
284 static_cast<ChunkedJSONWriteFunc*>(WriteFunc())->Take(std::move(aFunc));
285 mNeedComma[mDepth] = true;
288 #ifdef DEBUG
289 private:
290 bool mTaken = false;
291 #endif //
294 class JSONSchemaWriter {
295 JSONWriter& mWriter;
296 uint32_t mIndex;
298 public:
299 explicit JSONSchemaWriter(JSONWriter& aWriter) : mWriter(aWriter), mIndex(0) {
300 aWriter.StartObjectProperty("schema",
301 SpliceableJSONWriter::SingleLineStyle);
304 void WriteField(const Span<const char>& aName) {
305 mWriter.IntProperty(aName, mIndex++);
308 template <size_t Np1>
309 void WriteField(const char (&aName)[Np1]) {
310 WriteField(Span<const char>(aName, Np1 - 1));
313 ~JSONSchemaWriter() { mWriter.EndObject(); }
316 // This class helps create an indexed list of unique strings, and inserts the
317 // index as a JSON value. The collected list of unique strings can later be
318 // inserted as a JSON array.
319 // This can be useful for elements/properties with many repeated strings.
321 // With only JSONWriter w,
322 // `w.WriteElement("a"); w.WriteElement("b"); w.WriteElement("a");`
323 // when done inside a JSON array, will generate:
324 // `["a", "b", "c"]`
326 // With UniqueStrings u,
327 // `u.WriteElement(w, "a"); u.WriteElement(w, "b"); u.WriteElement(w, "a");`
328 // when done inside a JSON array, will generate:
329 // `[0, 1, 0]`
330 // and later, `u.SpliceStringTableElements(w)` (inside a JSON array), will
331 // output the corresponding indexed list of unique strings:
332 // `["a", "b"]`
333 class UniqueJSONStrings {
334 public:
335 // Start an empty list of unique strings.
336 MFBT_API explicit UniqueJSONStrings(
337 JSONWriter::CollectionStyle aStyle = JSONWriter::MultiLineStyle);
339 // Start with a copy of the strings from another list.
340 MFBT_API explicit UniqueJSONStrings(
341 const UniqueJSONStrings& aOther,
342 JSONWriter::CollectionStyle aStyle = JSONWriter::MultiLineStyle);
344 MFBT_API ~UniqueJSONStrings();
346 // Add `aStr` to the list (if not already there), and write its index as a
347 // named object property.
348 void WriteProperty(JSONWriter& aWriter, const Span<const char>& aName,
349 const Span<const char>& aStr) {
350 aWriter.IntProperty(aName, GetOrAddIndex(aStr));
353 // Add `aStr` to the list (if not already there), and write its index as an
354 // array element.
355 void WriteElement(JSONWriter& aWriter, const Span<const char>& aStr) {
356 aWriter.IntElement(GetOrAddIndex(aStr));
359 // Splice all collected unique strings into an array. This should only be done
360 // once, and then this UniqueStrings shouldn't be used anymore.
361 MFBT_API void SpliceStringTableElements(SpliceableJSONWriter& aWriter);
363 private:
364 // If `aStr` is already listed, return its index.
365 // Otherwise add it to the list and return the new index.
366 MFBT_API uint32_t GetOrAddIndex(const Span<const char>& aStr);
368 SpliceableChunkedJSONWriter mStringTableWriter;
369 HashMap<HashNumber, uint32_t> mStringHashToIndexMap;
372 void SpliceableJSONWriter::UniqueStringProperty(const Span<const char>& aName,
373 const Span<const char>& aStr) {
374 MOZ_RELEASE_ASSERT(mUniqueStrings);
375 mUniqueStrings->WriteProperty(*this, aName, aStr);
378 // Add `aStr` to the list (if not already there), and write its index as an
379 // array element.
380 void SpliceableJSONWriter::UniqueStringElement(const Span<const char>& aStr) {
381 MOZ_RELEASE_ASSERT(mUniqueStrings);
382 mUniqueStrings->WriteElement(*this, aStr);
385 } // namespace baseprofiler
386 } // namespace mozilla
388 #endif // BASEPROFILEJSONWRITER_H