File: buffer_iterator.h

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (159 lines) | stat: -rw-r--r-- 5,698 bytes parent folder | download | duplicates (2)
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
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_CONTAINERS_BUFFER_ITERATOR_H_
#define BASE_CONTAINERS_BUFFER_ITERATOR_H_

#include <string.h>

#include <type_traits>

#include "base/bit_cast.h"
#include "base/containers/span.h"
#include "base/numerics/checked_math.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {

// BufferIterator is a bounds-checked container utility to access variable-
// length, heterogeneous structures contained within a buffer. If the data are
// homogeneous, use base::span<> instead.
//
// After being created with a weakly-owned buffer, BufferIterator returns
// pointers to structured data within the buffer. After each method call that
// returns data in the buffer, the iterator position is advanced by the byte
// size of the object (or span of objects) returned. If there are not enough
// bytes remaining in the buffer to return the requested object(s), a nullptr
// or empty span is returned.
//
// This class is similar to base::Pickle, which should be preferred for
// serializing to disk. Pickle versions its header and does not support writing
// structures, which are problematic for serialization due to struct padding and
// version shear concerns.
//
// Example usage:
//
//    std::vector<uint8_t> buffer(4096);
//    if (!ReadSomeData(&buffer, buffer.size())) {
//      LOG(ERROR) << "Failed to read data.";
//      return false;
//    }
//
//    BufferIterator<uint8_t> iterator(buffer);
//    uint32_t* num_items = iterator.Object<uint32_t>();
//    if (!num_items) {
//      LOG(ERROR) << "No num_items field."
//      return false;
//    }
//
//    base::span<const item_struct> items =
//        iterator.Span<item_struct>(*num_items);
//    if (items.size() != *num_items) {
//      LOG(ERROR) << "Not enough items.";
//      return false;
//    }
//
//    // ... validate the objects in |items|.
template <typename B>
class BufferIterator {
 public:
  static_assert(std::is_same_v<std::remove_const_t<B>, char> ||
                    std::is_same_v<std::remove_const_t<B>, unsigned char>,
                "Underlying buffer type must be char-type.");

  BufferIterator() {}
  BufferIterator(B* data, size_t size)
      : BufferIterator(make_span(data, size)) {}
  explicit BufferIterator(span<B> buffer)
      : buffer_(buffer), remaining_(buffer) {}
  ~BufferIterator() {}

  // Returns a pointer to a mutable structure T in the buffer at the current
  // position. On success, the iterator position is advanced by sizeof(T). If
  // there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
  template <typename T,
            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
  T* MutableObject() {
    size_t size = sizeof(T);
    if (size > remaining_.size())
      return nullptr;
    T* t = reinterpret_cast<T*>(remaining_.data());
    remaining_ = remaining_.subspan(size);
    return t;
  }

  // Returns a const pointer to an object of type T in the buffer at the current
  // position.
  template <typename T,
            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
  const T* Object() {
    return MutableObject<const T>();
  }

  // Copies out an object. As compared to using Object, this avoids potential
  // unaligned access which may be undefined behavior.
  template <typename T,
            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
  absl::optional<T> CopyObject() {
    absl::optional<T> t;
    if (remaining_.size() >= sizeof(T)) {
      memcpy(&t.emplace(), remaining_.data(), sizeof(T));
      remaining_ = remaining_.subspan(sizeof(T));
    }
    return t;
  }

  // Returns a span of |count| T objects in the buffer at the current position.
  // On success, the iterator position is advanced by |sizeof(T) * count|. If
  // there are not enough bytes remaining in the buffer to fulfill the request,
  // returns an empty span.
  template <typename T,
            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
  span<T> MutableSpan(size_t count) {
    size_t size;
    if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
      return span<T>();
    if (size > remaining_.size())
      return span<T>();
    auto result = span<T>(reinterpret_cast<T*>(remaining_.data()), count);
    remaining_ = remaining_.subspan(size);
    return result;
  }

  // Returns a span to |count| const objects of type T in the buffer at the
  // current position.
  template <typename T,
            typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
  span<const T> Span(size_t count) {
    return MutableSpan<const T>(count);
  }

  // Resets the iterator position to the absolute offset |to|.
  void Seek(size_t to) { remaining_ = buffer_.subspan(to); }

  // Limits the remaining data to the specified size.
  // Seeking to an absolute offset reverses this.
  void TruncateTo(size_t size) { remaining_ = remaining_.first(size); }

  // Returns the total size of the underlying buffer.
  size_t total_size() const { return buffer_.size(); }

  // Returns the current position in the buffer.
  size_t position() const {
    DCHECK(buffer_.data() <= remaining_.data());
    DCHECK(remaining_.data() <= buffer_.data() + buffer_.size());
    return static_cast<size_t>(remaining_.data() - buffer_.data());
  }

 private:
  // The original buffer that the iterator was constructed with.
  const span<B> buffer_;
  // A subspan of |buffer_| containing the remaining bytes to iterate over.
  span<B> remaining_;
  // Copy and assign allowed.
};

}  // namespace base

#endif  // BASE_CONTAINERS_BUFFER_ITERATOR_H_