/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 PROFILEJSONWRITER_H #define PROFILEJSONWRITER_H #include "mozilla/JSONWriter.h" #include "mozilla/UniquePtr.h" #include #include #include class SpliceableChunkedJSONWriter; // On average, profile JSONs are large enough such that we want to avoid // reallocating its buffer when expanding. Additionally, the contents of the // profile are not accessed until the profile is entirely written. For these // reasons we use a chunked writer that keeps an array of chunks, which is // concatenated together after writing is finished. class ChunkedJSONWriteFunc : public mozilla::JSONWriteFunc { public: friend class SpliceableJSONWriter; ChunkedJSONWriteFunc() : mChunkPtr{nullptr}, mChunkEnd{nullptr} { AllocChunk(kChunkSize); } bool IsEmpty() const { MOZ_ASSERT_IF(!mChunkPtr, !mChunkEnd && mChunkList.length() == 0 && mChunkLengths.length() == 0); return !mChunkPtr; } void Write(const char* aStr) override; void CopyDataIntoLazilyAllocatedBuffer( const std::function& aAllocator) const; mozilla::UniquePtr CopyData() const; void Take(ChunkedJSONWriteFunc&& aOther); // Returns the byte length of the complete combined string, including the // null terminator byte. size_t GetTotalLength() const; private: void AllocChunk(size_t aChunkSize); static const size_t kChunkSize = 4096 * 512; // Pointer for writing inside the current chunk. // // The current chunk is always at the back of mChunkList, i.e., // mChunkList.back() <= mChunkPtr <= mChunkEnd. char* mChunkPtr; // Pointer to the end of the current chunk. // // The current chunk is always at the back of mChunkList, i.e., // mChunkEnd >= mChunkList.back() + mChunkLengths.back(). char* mChunkEnd; // List of chunks and their lengths. // // For all i, the length of the string in mChunkList[i] is // mChunkLengths[i]. mozilla::Vector> mChunkList; mozilla::Vector mChunkLengths; }; struct OStreamJSONWriteFunc : public mozilla::JSONWriteFunc { explicit OStreamJSONWriteFunc(std::ostream& aStream) : mStream(aStream) {} void Write(const char* aStr) override { mStream << aStr; } std::ostream& mStream; }; class SpliceableJSONWriter : public mozilla::JSONWriter { public: explicit SpliceableJSONWriter( mozilla::UniquePtr aWriter) : JSONWriter(std::move(aWriter)) {} void StartBareList(CollectionStyle aStyle = MultiLineStyle) { StartCollection(nullptr, "", aStyle); } void EndBareList() { EndCollection(""); } void NullElements(uint32_t aCount) { for (uint32_t i = 0; i < aCount; i++) { NullElement(); } } void Splice(const ChunkedJSONWriteFunc* aFunc); void Splice(const char* aStr); // Splice the given JSON directly in, without quoting. void SplicedJSONProperty(const char* aMaybePropertyName, const char* aJsonValue) { Scalar(aMaybePropertyName, aJsonValue); } // Takes the chunks from aFunc and write them. If move is not possible // (e.g., using OStreamJSONWriteFunc), aFunc's chunks are copied and its // storage cleared. virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc); }; class SpliceableChunkedJSONWriter : public SpliceableJSONWriter { public: explicit SpliceableChunkedJSONWriter() : SpliceableJSONWriter(mozilla::MakeUnique()) {} ChunkedJSONWriteFunc* WriteFunc() const { return static_cast(JSONWriter::WriteFunc()); } // Adopts the chunks from aFunc without copying. virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc) override; }; class JSONSchemaWriter { mozilla::JSONWriter& mWriter; uint32_t mIndex; public: explicit JSONSchemaWriter(mozilla::JSONWriter& aWriter) : mWriter(aWriter), mIndex(0) { aWriter.StartObjectProperty("schema", SpliceableJSONWriter::SingleLineStyle); } void WriteField(const char* aName) { mWriter.IntProperty(aName, mIndex++); } ~JSONSchemaWriter() { mWriter.EndObject(); } }; #endif // PROFILEJSONWRITER_H