blob: c43ba30000c8eb30eaa54e46554d8b3e4e615bda [file]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_GN_STRING_OUTPUT_BUFFER_H_
#define TOOLS_GN_STRING_OUTPUT_BUFFER_H_
#include <charconv>
#include <concepts>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace base {
class FilePath;
} // namespace base
class Err;
inline size_t MaximumSize(std::string_view s) {
return s.size();
}
inline void WriteToBuf(std::string_view s, char buf[], size_t& upto) {
memcpy(&buf[upto], s.data(), s.size());
upto += s.size();
}
inline size_t MaximumSize(char) {
return 1;
}
inline void WriteToBuf(char c, char buf[], size_t& upto) {
buf[upto++] = c;
}
template <typename T>
requires(std::integral<T> && !std::same_as<T, char> && !std::same_as<T, bool>)
inline size_t MaximumSize(T) {
// digits10 is the number of digits such that *every* number of this length
// can be represented. For int32 for example, this returns the number of
// digits required to store 999M (since we can store every number < 1B).
// +1 because although we can't represent *every* number in the billions, we
// can represent *some* numbers in the billions.
// +1 for negative sign.
return std::numeric_limits<T>::digits10 + 2;
}
template <typename T>
requires(std::integral<T> && !std::same_as<T, char> && !std::same_as<T, bool>)
inline void WriteToBuf(T val, char buf[], size_t& upto) {
upto =
std::to_chars(&buf[upto], &buf[upto + MaximumSize(val)], val).ptr - buf;
}
template <typename T>
concept Writable = !std::same_as<std::remove_cvref_t<T>, bool> &&
requires(const T& val, char buf[], size_t& upto) {
{ MaximumSize(val) } -> std::convertible_to<size_t>;
WriteToBuf(val, buf, upto);
};
// An append-only very large storage area for string data. Useful for the parts
// of GN that need to generate huge output files (e.g. --ide=json will create
// a 139 MiB project.json file for the Fuchsia build).
//
// Usage is the following:
//
// 1) Create instance.
//
// 2) Use operator<<, or Append() to append data to the instance.
//
// 3) Use ContentsEqual() to compare the instance's content with that of a
// given file.
//
// 4) Use WriteToFile() to write the content to a given file.
//
class StringOutputBuffer {
public:
StringOutputBuffer() = default;
// Reset the buffer contents to empty, while retaining allocated capacity.
void Clear();
// Convert content to single std::string instance. Useful for unit-testing.
std::string str() const;
// Return the number of characters stored in this instance.
size_t size() const { return ro_size_ + rw_.size; }
// Append a value to this instance.
void Append(const Writable auto& value) {
Reserve(MaximumSize(value));
WriteToBuf(value, rw_.buf.get(), rw_.size);
}
void Append(const char* str, size_t len) {
Append(std::string_view(str, len));
}
StringOutputBuffer& operator<<(const Writable auto& value) {
Append(value);
return *this;
}
// Compare the content of this instance with that of the file at |file_path|.
bool ContentsEqual(const base::FilePath& file_path) const;
// Write the contents of this instance to a file at |file_path|.
bool WriteToFile(const base::FilePath& file_path, Err* err) const;
// Write the contents of this instance to a file at |file_path| unless the
// file already exists and the contents are equal.
bool WriteToFileIfChanged(const base::FilePath& file_path, Err* err) const;
static size_t GetPageSizeForTesting() { return kPageSize; }
private:
// Return the number of free bytes in the current page.
size_t page_free_size() const { return rw_capacity_ - rw_.size; }
// Ensures that the read-write section of the buffer has at least n bytes
// available.
void Reserve(size_t n);
static constexpr size_t kPageSize = 65536;
struct Page {
std::unique_ptr<char[]> buf;
size_t size = 0;
};
// Because this is an extremely hot path, we avoid pointer indirection by
// storing the current page inline.
// The current page is read-write.
Page rw_;
size_t rw_capacity_ = 0;
// Completed read-only pages.
std::vector<Page> ro_;
// Contains sum([page.size for page in ro_])
size_t ro_size_ = 0;
};
std::string Pretty(const StringOutputBuffer& buffer);
#endif // TOOLS_GN_STRING_OUTPUT_BUFFER_H_