buffer_iterator.h 5.1 KB

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