// Copyright (c) 2019-2024, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include #include #include #include #include #include "span.h" namespace epee { struct byte_slice_data; class byte_stream; struct release_byte_slice { //! For use with `zmq_message_init_data`, use second arg for buffer pointer. static void call(void*, void* ptr) noexcept; void operator()(byte_slice_data* ptr) const noexcept { call(nullptr, ptr); } }; //! Frees ref count + buffer allocated internally by `byte_buffer`. struct release_byte_buffer { void operator()(std::uint8_t* buf) const noexcept; }; /*! Inspired by slices in golang. Storage is thread-safe reference counted, allowing for cheap copies or range selection on the bytes. The bytes owned by this class are always immutable. The functions `operator=`, `take_slice` and `remove_prefix` may alter the reference count for the backing store, which will invalidate pointers previously returned if the reference count is zero. Be careful about "caching" pointers in these circumstances. */ class byte_slice { /* A custom reference count is used instead of shared_ptr because it allows for an allocation optimization for the span constructor. This also reduces the size of this class by one pointer. */ std::unique_ptr storage_; span portion_; // within storage_ //! Internal use only; use to increase `storage` reference count. byte_slice(byte_slice_data* storage, span portion) noexcept; struct adapt_buffer{}; template explicit byte_slice(const adapt_buffer, T&& buffer); public: using value_type = std::uint8_t; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = const std::uint8_t*; using const_pointer = const std::uint8_t*; using reference = std::uint8_t; using const_reference = std::uint8_t; using iterator = pointer; using const_iterator = const_pointer; //! Construct empty slice. byte_slice() noexcept : storage_(nullptr), portion_() {} //! Construct empty slice byte_slice(std::nullptr_t) noexcept : byte_slice() {} //! Scatter-gather (copy) multiple `sources` into a single allocated slice. explicit byte_slice(std::initializer_list> sources); //! Convert `buffer` into a slice using one allocation for shared count. explicit byte_slice(std::vector&& buffer); //! Convert `buffer` into a slice using one allocation for shared count. explicit byte_slice(std::string&& buffer); //! Convert `stream` into a slice with zero allocations. explicit byte_slice(byte_stream&& stream, bool shrink = true); byte_slice(byte_slice&& source) noexcept; ~byte_slice() noexcept = default; //! \note May invalidate previously retrieved pointers. byte_slice& operator=(byte_slice&&) noexcept; //! \return A shallow (cheap) copy of the data from `this` slice. byte_slice clone() const noexcept { return {storage_.get(), portion_}; } iterator begin() const noexcept { return portion_.begin(); } const_iterator cbegin() const noexcept { return portion_.begin(); } iterator end() const noexcept { return portion_.end(); } const_iterator cend() const noexcept { return portion_.end(); } bool empty() const noexcept { return storage_ == nullptr; } const std::uint8_t* data() const noexcept { return portion_.data(); } std::size_t size() const noexcept { return portion_.size(); } /*! Drop bytes from the beginning of `this` slice. \note May invalidate previously retrieved pointers. \post `this->size() = this->size() - std::min(this->size(), max_bytes)` \post `if (this->size() <= max_bytes) this->data() = nullptr` \return Number of bytes removed. */ std::size_t remove_prefix(std::size_t max_bytes) noexcept; /*! "Take" bytes from the beginning of `this` slice. \note May invalidate previously retrieved pointers. \post `this->size() = this->size() - std::min(this->size(), max_bytes)` \post `if (this->size() <= max_bytes) this->data() = nullptr` \return Slice containing the bytes removed from `this` slice. */ byte_slice take_slice(std::size_t max_bytes) noexcept; /*! Return a shallow (cheap) copy of a slice from `begin` and `end` offsets. \throw std::out_of_range If `end < begin`. \throw std::out_of_range If `size() < end`. \return Slice starting at `data() + begin` of size `end - begin`. */ byte_slice get_slice(std::size_t begin, std::size_t end) const; //! \post `empty()` \return Ownership of ref-counted buffer. std::unique_ptr take_buffer() noexcept; }; //! Alias for a buffer that has space for a `byte_slice` ref count. using byte_buffer = std::unique_ptr; /*! \return `buf` with a new size of exactly `length`. New bytes not initialized. A `nullptr` is returned on allocation failure. */ byte_buffer byte_buffer_resize(byte_buffer buf, std::size_t length) noexcept; /*! Increase `buf` of size `current` by `more` bytes. \throw std::range_error if `current + more` exceeds `size_t` bounds. \return Buffer of `current + more` bytes. A `nullptr` is returned on allocation failure. */ byte_buffer byte_buffer_increase(byte_buffer buf, std::size_t current, std::size_t more); } // epee