// Copyright 2023 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_ #define CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_ #include <stdint.h> #include <string.h> #include <algorithm> #include <array> #include <limits> #include <optional> #include <type_traits> #include <vector> #include "base/numerics/safe_math.h" namespace crashpad { //! \brief Capacity of a `RingBufferData`, in bytes. RingBufferCapacity; namespace internal { //! \brief Default capacity of `RingBufferData`, in bytes. inline constexpr RingBufferCapacity kDefaultRingBufferDataCapacity = …; //! \brief A tuple holding the current range of bytes which can be read from or //! have been written to. struct Range final { … }; // This struct is persisted to disk, so its size must not change. static_assert …; //! \brief The number of bits encoded in each byte of a Base 128-encoded varint. inline constexpr int kBase128ByteValueBits = …; //! \!brief Calculates the length in bytes of `value` encoded using //! little-endian Base 128 varint encoding. //! \sa https://developers.google.com/protocol-buffers/docs/encoding#varints //! //! `LengthDelimitedRingBufferWriter` uses varint-encoded delimiters to enable //! zero-copy deserialization of the ringbuffer's contents when storing //! protobufs inside the ringbuffer, e.g. via //! `google::protobuf::util::ParseDelimitedFromZeroCopyStream()` or similar. //! //! \sa //! https://github.com/protocolbuffers/protobuf/blob/3202b9da88ceb75b65bbabaf4033c95e872f828d/src/google/protobuf/util/delimited_message_util.h#L85 //! \sa //! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/zero_copy_stream_impl_lite.h#L68 //! \sa //! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/coded_stream.h#L171 //! //! \!param[in] value Value to be encoded in Base 128 varint encoding. //! \!return The length in bytes of `value` in Base 128 varint encoding. template <typename IntegerType> constexpr Range::Length Base128VarintEncodedLength(IntegerType value) { … } // Note that std::array capacity is a size_t, not a RingBufferCapacity. RingBufferArray; //! \return The size of the `RingBufferArray` as a `Range::Length`. template <size_t ArrayCapacity> constexpr Range::Length RingBufferArraySize( const RingBufferArray<ArrayCapacity>& ring_buffer_data) { … } //! \brief Reads data from the ring buffer into a target buffer. //! \param[in] ring_buffer_data The ring buffer to read. //! \param[in,out] ring_buffer_read_range The range of the data available //! to read. Upon return, set to the remaining range of data available //! to read, if any. //! \param[in] target_buffer Buffer into which data will be written. //! \param[in] target_buffer_length Number of bytes to write into //! `target_buffer`. //! //! \return `true` if the read succeeded, `false` otherwise. On success, updates //! `ring_buffer_read_range` to reflect the bytes consumed. //! //! The bytes can wrap around the end of the ring buffer, in which case the read //! continues at the beginning of the ring buffer (if the ring buffer is long //! enough). template <typename RingBufferArrayType> bool ReadBytesFromRingBuffer(const RingBufferArrayType& ring_buffer_data, internal::Range& ring_buffer_read_range, uint8_t* target_buffer, Range::Length target_buffer_length) { … } //! \brief Reads a single little-endian Base 128 varint-encoded integer from //! the ring buffer. //! \param[in] ring_buffer_data The ring buffer to read. //! \param[in,out] ring_buffer_read_range The range of the data available //! to read. Upon return, set to the remaining range of data available //! to read, if any. //! \param[out] result Upon success, set to the decoded value read from the //! buffer. //! //! \return The length in bytes of the varint if the read succeeded, //! `std::nullopt` otherwise. On success, updates `ring_buffer_read_range` //! to reflect the bytes available to read. //! //! The varint can wrap around the end of the ring buffer, in which case the //! read continues at the beginning of the ring buffer (if the ring buffer is //! long enough). template <typename RingBufferArrayType, typename IntegerType> std::optional<Range::Length> ReadBase128VarintFromRingBuffer( const RingBufferArrayType& ring_buffer_data, internal::Range& ring_buffer_read_range, IntegerType& result) { … } //! \brief Writes data from the source buffer into the ring buffer. //! \param[in] source_buffer Buffer from which data will be read. //! \param[in] source_buffer_length The length in bytes of `source_buffer`. //! \param[in] ring_buffer_data The ring buffer into which data will be read. //! \param[in,out] ring_buffer_write_range The range of the data available //! to write. Upon return, set to the remaining range of data available //! to write, if any. //! //! \return `true` if write read succeeded, `false` otherwise. On success, //! updates //! `ring_buffer_write_range` to reflect the bytes written. //! //! The bytes can wrap around the end of the ring buffer, in which case the //! write continues at the beginning of the ring buffer (if the ring buffer is //! long enough). template <typename RingBufferArrayType> bool WriteBytesToRingBuffer(const uint8_t* const source_buffer, Range::Length source_buffer_length, RingBufferArrayType& ring_buffer_data, internal::Range& ring_buffer_write_range) { … } //! \brief Writes a single Base 128 varint-encoded little-endian unsigned //! integer into the ring buffer. //! \param[in] value The value to encode and write into the ring buffer. //! \param[in] ring_buffer_data The ring buffer into which to write. //! \param[in,out] ring_buffer_write_range The range of the data available //! to write. Upon return, set to the remaining range of data available //! to write, if any. //! //! \return The length in bytes of the varint if the write succeeded, //! `std::nullopt` otherwise. On success, updates `write_buffer_read_range` //! to reflect the range available to write, if any. //! //! The varint can wrap around the end of the ring buffer, in which case the //! write continues at the beginning of the ring buffer (if the ring buffer is //! long enough). template <typename RingBufferArrayType, typename IntegerType> std::optional<int> WriteBase128VarintToRingBuffer( IntegerType value, RingBufferArrayType& ring_buffer_data, internal::Range& ring_buffer_write_range) { … } } // namespace internal //! \brief Storage for a ring buffer which can hold up to //! `RingBufferCapacity` //! bytes of Base 128-varint delimited variable-length items. //! //! This struct contains a header immediately followed by the ring buffer //! data. The current read offset and length are stored in `header.data_range`. //! //! The structure of this object is: //! //! `|magic|version|data_offset|data_length|ring_buffer_data|` //! //! To write data to this object, see `LengthDelimitedRingBufferWriter`. //! To read data from this object, see `LengthDelimitedRingBufferReader`. //! //! The bytes of this structure are suitable for direct serialization from //! memory to disk, e.g. as a crashpad::Annotation. template <RingBufferCapacity Capacity> struct RingBufferData final { … }; // Ensure the ring buffer is packed correctly at its default capacity. static_assert …; // Allow just `RingBufferData foo;` to be declared without template arguments // using C++17 class template argument deduction. template < RingBufferCapacity Capacity = internal::kDefaultRingBufferDataCapacity> RingBufferData() -> RingBufferData<Capacity>; //! \brief Reads variable-length data buffers from a `RingBufferData`, //! delimited by Base128 varint-encoded length delimiters. //! //! Holds a reference to a `RingBufferData` with the capacity to hold //! `RingBufferDataType::size()` bytes of variable-length buffers each //! preceded by its length (encoded as a Base128 length varint). //! //! Provides reading capabilities via `Pop()`. template <typename RingBufferDataType> class LengthDelimitedRingBufferReader final { … }; // Allow just `LengthDelimitedRingBufferReader reader(foo);` to be declared // without template arguments using C++17 class template argument deduction. template <typename RingBufferDataType> LengthDelimitedRingBufferReader(RingBufferDataType&) -> LengthDelimitedRingBufferReader<RingBufferDataType>; //! \brief Writes variable-length data buffers to a `RingBufferData`, //! delimited by Base128 varint-encoded length delimiters. //! //! Holds a reference to a `RingBufferData` with the capacity to hold //! `RingBufferDataType::size()` bytes of variable-length buffers each //! preceded by its length (encoded as a Base128 length varint). //! //! Provides writing capabilities via `Push()`. template <typename RingBufferDataType> class LengthDelimitedRingBufferWriter final { … }; // Allow just `LengthDelimitedRingBufferWriter writer(foo);` to be declared // without template arguments using C++17 class template argument deduction. template <typename RingBufferDataType> LengthDelimitedRingBufferWriter(RingBufferDataType&) -> LengthDelimitedRingBufferWriter<RingBufferDataType>; } // namespace crashpad #endif // CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_