• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_CONTAINERS_BUFFER_ITERATOR_H_
6 #define BASE_CONTAINERS_BUFFER_ITERATOR_H_
7 
8 #include <string.h>
9 
10 #include <type_traits>
11 
12 #include "base/bit_cast.h"
13 #include "base/containers/span.h"
14 #include "base/numerics/checked_math.h"
15 #include "third_party/abseil-cpp/absl/types/optional.h"
16 
17 namespace base {
18 
19 // BufferIterator is a bounds-checked container utility to access variable-
20 // length, heterogeneous structures contained within a buffer. If the data are
21 // homogeneous, use base::span<> instead.
22 //
23 // After being created with a weakly-owned buffer, BufferIterator returns
24 // pointers to structured data within the buffer. After each method call that
25 // returns data in the buffer, the iterator position is advanced by the byte
26 // size of the object (or span of objects) returned. If there are not enough
27 // bytes remaining in the buffer to return the requested object(s), a nullptr
28 // or empty span is returned.
29 //
30 // This class is similar to base::Pickle, which should be preferred for
31 // serializing to disk. Pickle versions its header and does not support writing
32 // structures, which are problematic for serialization due to struct padding and
33 // version shear concerns.
34 //
35 // Example usage:
36 //
37 //    std::vector<uint8_t> buffer(4096);
38 //    if (!ReadSomeData(&buffer, buffer.size())) {
39 //      LOG(ERROR) << "Failed to read data.";
40 //      return false;
41 //    }
42 //
43 //    BufferIterator<uint8_t> iterator(buffer);
44 //    uint32_t* num_items = iterator.Object<uint32_t>();
45 //    if (!num_items) {
46 //      LOG(ERROR) << "No num_items field."
47 //      return false;
48 //    }
49 //
50 //    base::span<const item_struct> items =
51 //        iterator.Span<item_struct>(*num_items);
52 //    if (items.size() != *num_items) {
53 //      LOG(ERROR) << "Not enough items.";
54 //      return false;
55 //    }
56 //
57 //    // ... validate the objects in |items|.
58 template <typename B>
59 class BufferIterator {
60  public:
61   static_assert(std::is_same<std::remove_const_t<B>, char>::value ||
62                     std::is_same<std::remove_const_t<B>, unsigned char>::value,
63                 "Underlying buffer type must be char-type.");
64 
BufferIterator()65   BufferIterator() {}
BufferIterator(B * data,size_t size)66   BufferIterator(B* data, size_t size)
67       : BufferIterator(make_span(data, size)) {}
BufferIterator(span<B> buffer)68   explicit BufferIterator(span<B> buffer)
69       : buffer_(buffer), remaining_(buffer) {}
~BufferIterator()70   ~BufferIterator() {}
71 
72   // Returns a pointer to a mutable structure T in the buffer at the current
73   // position. On success, the iterator position is advanced by sizeof(T). If
74   // there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
75   template <typename T,
76             typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
MutableObject()77   T* MutableObject() {
78     size_t size = sizeof(T);
79     if (size > remaining_.size())
80       return nullptr;
81     T* t = reinterpret_cast<T*>(remaining_.data());
82     remaining_ = remaining_.subspan(size);
83     return t;
84   }
85 
86   // Returns a const pointer to an object of type T in the buffer at the current
87   // position.
88   template <typename T,
89             typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
Object()90   const T* Object() {
91     return MutableObject<const T>();
92   }
93 
94   // Copies out an object. As compared to using Object, this avoids potential
95   // unaligned access which may be undefined behavior.
96   template <typename T,
97             typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
CopyObject()98   absl::optional<T> CopyObject() {
99     absl::optional<T> t;
100     if (remaining_.size() >= sizeof(T)) {
101       memcpy(&t.emplace(), remaining_.data(), sizeof(T));
102       remaining_ = remaining_.subspan(sizeof(T));
103     }
104     return t;
105   }
106 
107   // Returns a span of |count| T objects in the buffer at the current position.
108   // On success, the iterator position is advanced by |sizeof(T) * count|. If
109   // there are not enough bytes remaining in the buffer to fulfill the request,
110   // returns an empty span.
111   template <typename T,
112             typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
MutableSpan(size_t count)113   span<T> MutableSpan(size_t count) {
114     size_t size;
115     if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
116       return span<T>();
117     if (size > remaining_.size())
118       return span<T>();
119     auto result = span<T>(reinterpret_cast<T*>(remaining_.data()), count);
120     remaining_ = remaining_.subspan(size);
121     return result;
122   }
123 
124   // Returns a span to |count| const objects of type T in the buffer at the
125   // current position.
126   template <typename T,
127             typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
Span(size_t count)128   span<const T> Span(size_t count) {
129     return MutableSpan<const T>(count);
130   }
131 
132   // Resets the iterator position to the absolute offset |to|.
Seek(size_t to)133   void Seek(size_t to) { remaining_ = buffer_.subspan(to); }
134 
135   // Limits the remaining data to the specified size.
136   // Seeking to an absolute offset reverses this.
TruncateTo(size_t size)137   void TruncateTo(size_t size) { remaining_ = remaining_.first(size); }
138 
139   // Returns the total size of the underlying buffer.
total_size()140   size_t total_size() const { return buffer_.size(); }
141 
142   // Returns the current position in the buffer.
position()143   size_t position() const {
144     DCHECK(buffer_.data() <= remaining_.data());
145     DCHECK(remaining_.data() <= buffer_.data() + buffer_.size());
146     return static_cast<size_t>(remaining_.data() - buffer_.data());
147   }
148 
149  private:
150   // The original buffer that the iterator was constructed with.
151   const span<B> buffer_;
152   // A subspan of |buffer_| containing the remaining bytes to iterate over.
153   span<B> remaining_;
154   // Copy and assign allowed.
155 };
156 
157 }  // namespace base
158 
159 #endif  // BASE_CONTAINERS_BUFFER_ITERATOR_H_
160