aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee/include/byte_slice.h
blob: 57f6c410a4574276adbf1169476a6dba3107b613 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) 2019-2022, 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 <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#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<byte_slice_data, release_byte_slice> storage_;
    span<const std::uint8_t> portion_; // within storage_

    //! Internal use only; use to increase `storage` reference count.
    byte_slice(byte_slice_data* storage, span<const std::uint8_t> portion) noexcept;

    struct adapt_buffer{};

    template<typename T>
    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<span<const std::uint8_t>> sources);

    //! Convert `buffer` into a slice using one allocation for shared count.
    explicit byte_slice(std::vector<std::uint8_t>&& 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<byte_slice_data, release_byte_slice> take_buffer() noexcept;
  };

  //! Alias for a buffer that has space for a `byte_slice` ref count.
  using byte_buffer = std::unique_ptr<std::uint8_t, release_byte_buffer>;

  /*! \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