xref: /aosp_15_r20/external/emboss/runtime/cpp/emboss_memory_util.h (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Utilities for efficiently reading and writing to/from memory.
16 #ifndef EMBOSS_RUNTIME_CPP_EMBOSS_MEMORY_UTIL_H_
17 #define EMBOSS_RUNTIME_CPP_EMBOSS_MEMORY_UTIL_H_
18 
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstring>
22 
23 #include "runtime/cpp/emboss_bit_util.h"
24 #include "runtime/cpp/emboss_cpp_types.h"
25 #include "runtime/cpp/emboss_defines.h"
26 
27 namespace emboss {
28 namespace support {
29 
30 // MemoryAccessor reads and writes big- and little-endian unsigned integers in
31 // and out of memory, using optimized routines where possible.
32 //
33 // The default MemoryAccessor just proxies to the MemoryAccessor with the
34 // next-smallest alignment and equivalent offset: MemoryAccessor<C, 8, 0, 32>
35 // and MemoryAccessor<C, 8, 4, 32> will proxy to MemoryAccessor<C, 4, 0, 32>,
36 // since an 8-byte-aligned pointer is also 4-byte-aligned, as is a pointer that
37 // is 4 bytes away from 8-byte alignment.
38 template <typename CharT, ::std::size_t kAlignment, ::std::size_t kOffset,
39           ::std::size_t kBits>
40 struct MemoryAccessor {
41   static_assert(IsPowerOfTwo(kAlignment),
42                 "MemoryAccessor requires power-of-two alignment.");
43   static_assert(
44       kOffset < kAlignment,
45       "MemoryAccessor requires offset to be strictly less than alignment.");
46 
47   using ChainedAccessor =
48       MemoryAccessor<CharT, kAlignment / 2, kOffset % (kAlignment / 2), kBits>;
49   using Unsigned = typename LeastWidthInteger<kBits>::Unsigned;
ReadLittleEndianUIntMemoryAccessor50   static inline Unsigned ReadLittleEndianUInt(const CharT *bytes) {
51     return ChainedAccessor::ReadLittleEndianUInt(bytes);
52   }
WriteLittleEndianUIntMemoryAccessor53   static inline void WriteLittleEndianUInt(CharT *bytes, Unsigned value) {
54     ChainedAccessor::WriteLittleEndianUInt(bytes, value);
55   }
ReadBigEndianUIntMemoryAccessor56   static inline Unsigned ReadBigEndianUInt(const CharT *bytes) {
57     return ChainedAccessor::ReadBigEndianUInt(bytes);
58   }
WriteBigEndianUIntMemoryAccessor59   static inline void WriteBigEndianUInt(CharT *bytes, Unsigned value) {
60     ChainedAccessor::WriteBigEndianUInt(bytes, value);
61   }
62 };
63 
64 // The least-aligned case for MemoryAccessor is 8-bit alignment, and the default
65 // version of MemoryAccessor will devolve to this one if there is no more
66 // specific override.
67 //
68 // If the system byte order is known, then these routines can use memcpy and
69 // (possibly) a byte swap; otherwise they can read individual bytes and
70 // shift+or them together in the appropriate order.  I (bolms@) haven't found a
71 // compiler that will optimize the multiple reads, shifts, and ors into a single
72 // read, so the memcpy version is *much* faster for 32-bit and larger reads.
73 template <typename CharT, ::std::size_t kBits>
74 struct MemoryAccessor<CharT, 1, 0, kBits> {
75   static_assert(kBits % 8 == 0,
76                 "MemoryAccessor can only read and write whole-byte values.");
77   static_assert(IsAliasSafe<CharT>::value,
78                 "MemoryAccessor can only be used on pointers to char types.");
79 
80   using Unsigned = typename LeastWidthInteger<kBits>::Unsigned;
81 
82 #if defined(EMBOSS_LITTLE_ENDIAN_TO_NATIVE)
83   static inline Unsigned ReadLittleEndianUInt(const CharT *bytes) {
84     Unsigned result = 0;
85     ::std::memcpy(&result, bytes, kBits / 8);
86     return EMBOSS_LITTLE_ENDIAN_TO_NATIVE(result);
87   }
88 #else
89   static inline Unsigned ReadLittleEndianUInt(const CharT *bytes) {
90     Unsigned result = 0;
91     for (decltype(kBits) i = 0; i < kBits / 8; ++i) {
92       result |=
93           static_cast<Unsigned>(static_cast</**/ ::std::uint8_t>(bytes[i]))
94           << i * 8;
95     }
96     return result;
97   }
98 #endif
99 
100 #if defined(EMBOSS_NATIVE_TO_LITTLE_ENDIAN)
101   static inline void WriteLittleEndianUInt(CharT *bytes, Unsigned value) {
102     value = EMBOSS_NATIVE_TO_LITTLE_ENDIAN(value);
103     ::std::memcpy(bytes, &value, kBits / 8);
104   }
105 #else
106   static inline void WriteLittleEndianUInt(CharT *bytes, Unsigned value) {
107     for (decltype(kBits) i = 0; i < kBits / 8; ++i) {
108       bytes[i] = static_cast<CharT>(static_cast</**/ ::std::uint8_t>(value));
109       if (sizeof value > 1) {
110         // Shifting an 8-bit type by 8 bits is undefined behavior, so skip this
111         // step for uint8_t.
112         value >>= 8;
113       }
114     }
115   }
116 #endif
117 
118 #if defined(EMBOSS_BIG_ENDIAN_TO_NATIVE)
119   static inline Unsigned ReadBigEndianUInt(const CharT *bytes) {
120     Unsigned result = 0;
121     // When a big-endian source integer is smaller than the result, the source
122     // bytes must be copied into the final bytes of the destination.  This is
123     // true whether the host is big- or little-endian.
124     //
125     // For a little-endian host:
126     //
127     // source (big-endian value 0x112233):
128     //
129     //   byte 0   byte 1   byte 2
130     // +--------+--------+--------+
131     // | 0x11   | 0x22   | 0x33   |
132     // +--------+--------+--------+
133     //
134     // result after memcpy (host-interpreted value 0x33221100):
135     //
136     //   byte 0   byte 1   byte 2   byte 3
137     // +--------+--------+--------+--------+
138     // | 0x00   | 0x11   | 0x22   | 0x33   |
139     // +--------+--------+--------+--------+
140     //
141     // result after 32-bit byte swap (host-interpreted value 0x112233):
142     //
143     //   byte 0   byte 1   byte 2   byte 3
144     // +--------+--------+--------+--------+
145     // | 0x33   | 0x22   | 0x11   | 0x00   |
146     // +--------+--------+--------+--------+
147     //
148     // For a big-endian host:
149     //
150     // source (value 0x112233):
151     //
152     //   byte 0   byte 1   byte 2
153     // +--------+--------+--------+
154     // | 0x11   | 0x22   | 0x33   |
155     // +--------+--------+--------+
156     //
157     // result after memcpy (value 0x112233) -- no byte swap needed:
158     //
159     //   byte 0   byte 1   byte 2   byte 3
160     // +--------+--------+--------+--------+
161     // | 0x00   | 0x11   | 0x22   | 0x33   |
162     // +--------+--------+--------+--------+
163     ::std::memcpy(reinterpret_cast<char *>(&result) + sizeof result - kBits / 8,
164                   bytes, kBits / 8);
165     result = EMBOSS_BIG_ENDIAN_TO_NATIVE(result);
166     return result;
167   }
168 #else
169   static inline Unsigned ReadBigEndianUInt(const CharT *bytes) {
170     Unsigned result = 0;
171     for (decltype(kBits) i = 0; i < kBits / 8; ++i) {
172       result |=
173           static_cast<Unsigned>(static_cast</**/ ::std::uint8_t>(bytes[i]))
174           << (kBits - 8 - i * 8);
175     }
176     return result;
177   }
178 #endif
179 
180 #if defined(EMBOSS_NATIVE_TO_BIG_ENDIAN)
181   static inline void WriteBigEndianUInt(CharT *bytes, Unsigned value) {
182     value = EMBOSS_NATIVE_TO_BIG_ENDIAN(value);
183     ::std::memcpy(bytes,
184                   reinterpret_cast<char *>(&value) + sizeof value - kBits / 8,
185                   kBits / 8);
186   }
187 #else
188   static inline void WriteBigEndianUInt(CharT *bytes, Unsigned value) {
189     for (decltype(kBits) i = 0; i < kBits / 8; ++i) {
190       bytes[kBits / 8 - 1 - i] =
191           static_cast<CharT>(static_cast</**/ ::std::uint8_t>(value));
192       if (sizeof value > 1) {
193         // Shifting an 8-bit type by 8 bits is undefined behavior, so skip this
194         // step for uint8_t.
195         value >>= 8;
196       }
197     }
198   }
199 #endif
200 };
201 
202 // Specialization of UIntMemoryAccessor for 16- 32- and 64-bit-aligned reads and
203 // writes, using EMBOSS_ALIAS_SAFE_POINTER_CAST instead of memcpy.
204 #if defined(EMBOSS_ALIAS_SAFE_POINTER_CAST) && \
205     defined(EMBOSS_LITTLE_ENDIAN_TO_NATIVE) && \
206     defined(EMBOSS_BIG_ENDIAN_TO_NATIVE) &&    \
207     defined(EMBOSS_NATIVE_TO_LITTLE_ENDIAN) && \
208     defined(EMBOSS_NATIVE_TO_BIG_ENDIAN)
209 template <typename CharT>
210 struct MemoryAccessor<CharT, 8, 0, 64> {
211   static inline ::std::uint64_t ReadLittleEndianUInt(const CharT *bytes) {
212     return EMBOSS_LITTLE_ENDIAN_TO_NATIVE(
213         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint64_t, bytes));
214   }
215 
216   static inline void WriteLittleEndianUInt(CharT *bytes,
217                                            ::std::uint64_t value) {
218     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint64_t, bytes) =
219         EMBOSS_NATIVE_TO_LITTLE_ENDIAN(value);
220   }
221 
222   static inline ::std::uint64_t ReadBigEndianUInt(const CharT *bytes) {
223     return EMBOSS_BIG_ENDIAN_TO_NATIVE(
224         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint64_t, bytes));
225   }
226 
227   static inline void WriteBigEndianUInt(CharT *bytes, ::std::uint64_t value) {
228     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint64_t, bytes) =
229         EMBOSS_NATIVE_TO_BIG_ENDIAN(value);
230   }
231 };
232 
233 template <typename CharT>
234 struct MemoryAccessor<CharT, 4, 0, 32> {
235   static inline ::std::uint32_t ReadLittleEndianUInt(const CharT *bytes) {
236     return EMBOSS_LITTLE_ENDIAN_TO_NATIVE(
237         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint32_t, bytes));
238   }
239 
240   static inline void WriteLittleEndianUInt(CharT *bytes,
241                                            ::std::uint32_t value) {
242     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint32_t, bytes) =
243         EMBOSS_NATIVE_TO_LITTLE_ENDIAN(value);
244   }
245 
246   static inline ::std::uint32_t ReadBigEndianUInt(const CharT *bytes) {
247     return EMBOSS_BIG_ENDIAN_TO_NATIVE(
248         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint32_t, bytes));
249   }
250 
251   static inline void WriteBigEndianUInt(CharT *bytes, ::std::uint32_t value) {
252     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint32_t, bytes) =
253         EMBOSS_NATIVE_TO_BIG_ENDIAN(value);
254   }
255 };
256 
257 template <typename CharT>
258 struct MemoryAccessor<CharT, 2, 0, 16> {
259   static inline ::std::uint16_t ReadLittleEndianUInt(const CharT *bytes) {
260     return EMBOSS_LITTLE_ENDIAN_TO_NATIVE(
261         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint16_t, bytes));
262   }
263 
264   static inline void WriteLittleEndianUInt(CharT *bytes,
265                                            ::std::uint16_t value) {
266     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint16_t, bytes) =
267         EMBOSS_NATIVE_TO_LITTLE_ENDIAN(value);
268   }
269 
270   static inline ::std::uint16_t ReadBigEndianUInt(const CharT *bytes) {
271     return EMBOSS_BIG_ENDIAN_TO_NATIVE(
272         *EMBOSS_ALIAS_SAFE_POINTER_CAST(const ::std::uint16_t, bytes));
273   }
274 
275   static inline void WriteBigEndianUInt(CharT *bytes, ::std::uint16_t value) {
276     *EMBOSS_ALIAS_SAFE_POINTER_CAST(::std::uint16_t, bytes) =
277         EMBOSS_NATIVE_TO_BIG_ENDIAN(value);
278   }
279 };
280 #endif  // defined(EMBOSS_ALIAS_SAFE_POINTER_CAST) &&
281         // defined(EMBOSS_LITTLE_ENDIAN_TO_NATIVE) &&
282         // defined(EMBOSS_BIG_ENDIAN_TO_NATIVE) &&
283         // defined(EMBOSS_NATIVE_TO_LITTLE_ENDIAN) &&
284         // defined(EMBOSS_NATIVE_TO_BIG_ENDIAN)
285 
286 // This is the Euclidean GCD algorithm, in C++11-constexpr-safe form.  The
287 // initial is-b-greater-than-a-if-so-swap is omitted, since gcd(b % a, a) is the
288 // same as gcd(b, a) when a > b.
289 inline constexpr ::std::size_t GreatestCommonDivisor(::std::size_t a,
290                                                      ::std::size_t b) {
291   return a == 0 ? b : GreatestCommonDivisor(b % a, a);
292 }
293 
294 // ContiguousBuffer is a direct view of a fixed number of contiguous bytes in
295 // memory.  If Byte is a const type, it will be a read-only view; if Byte is
296 // non-const, then writes will be allowed.
297 //
298 // The kAlignment and kOffset parameters are used to optimize certain reads and
299 // writes.  static_cast<uintptr_t>(bytes_) % kAlignment must equal kOffset.
300 //
301 // This class is used extensively by generated code, and is not intended to be
302 // heavily used by hand-written code -- some interfaces can be tricky to call
303 // correctly.
304 template <typename Byte, ::std::size_t kAlignment, ::std::size_t kOffset>
305 class ContiguousBuffer final {
306   // There aren't many systems with non-8-bit chars, and a quirk of POSIX
307   // requires that POSIX C systems have CHAR_BIT == 8, but some DSPs use wider
308   // chars.
309   static_assert(CHAR_BIT == 8, "ContiguousBuffer requires 8-bit chars.");
310 
311   // ContiguousBuffer assumes that its backing store is byte-oriented.  The
312   // previous check ensures that chars are 8 bits, and this one ensures that the
313   // backing store uses chars.
314   //
315   // Note that this check is explicitly that Byte is one of the three standard
316   // char types, and not that (say) it is a one-byte type with an assignment
317   // operator that can be static_cast<> to and from uint8_t.  I (bolms@) have
318   // chosen to lock it down to just char types to avoid running afoul of strict
319   // aliasing rules anywhere.
320   //
321   // Of somewhat academic interest, uint8_t is not required to be a char type
322   // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66110#c10), though it is
323   // unlikely that any compiler vendor will actually change it, as there is
324   // probably enough real-world code that relies on uint8_t being allowed to
325   // alias.
326   static_assert(IsAliasSafe<Byte>::value,
327                 "ContiguousBuffer requires char type.");
328 
329   // Because real-world processors only care about power-of-2 alignments,
330   // ContiguousBuffer only supports power-of-2 alignments.  Note that
331   // GetOffsetStorage can handle non-power-of-2 alignments.
332   static_assert(IsPowerOfTwo(kAlignment),
333                 "ContiguousBuffer requires power-of-two alignment.");
334 
335   // To avoid template variant explosion, ContiguousBuffer requires kOffset to
336   // be strictly less than kAlignment.  Users of ContiguousBuffer are expected
337   // to take the modulus of kOffset by kAlignment before passing it in as a
338   // parameter.
339   static_assert(
340       kOffset < kAlignment,
341       "ContiguousBuffer requires offset to be strictly less than alignment.");
342 
343  public:
344   using ByteType = Byte;
345   // OffsetStorageType<kSubAlignment, kSubOffset> is the return type of
346   // GetOffsetStorage<kSubAlignment, kSubOffset>(...).  This is used in a number
347   // of places in generated code to specify deeply-nested template values.
348   //
349   // In theory, anything that cared about this type could use
350   // decltype(declval(ContiguousBuffer<...>).GetOffsetStorage<kSubAlignment,
351   // kSubOffset>(0, 0)) instead, but that is much more cumbersome, and it
352   // appears that at least some versions of GCC do not handle it correctly.
353   template </**/ ::std::size_t kSubAlignment, ::std::size_t kSubOffset>
354   using OffsetStorageType =
355       ContiguousBuffer<Byte, GreatestCommonDivisor(kAlignment, kSubAlignment),
356                        (kOffset + kSubOffset) %
357                            GreatestCommonDivisor(kAlignment, kSubAlignment)>;
358 
359   // Constructs a default ContiguousBuffer.
360   ContiguousBuffer() : bytes_(nullptr), size_(0) {}
361 
362   // Constructs a ContiguousBuffer from a contiguous container type over some
363   // `char` type, such as std::string, std::vector<signed char>,
364   // std::array<unsigned char, N>, or std::string_view.
365   //
366   // This template is only enabled if:
367   //
368   // 1. bytes->data() returns a pointer to some char type.
369   // 2. Byte is at least as cv-qualified as decltype(*bytes->data()).
370   //
371   // The first requirement means that this constructor won't work on, e.g.,
372   // std::vector<int> -- this is mostly a precautionary measure, since
373   // ContiguousBuffer only uses alias-safe operations anyway.
374   //
375   // The second requirement means that const and volatile are respected in the
376   // expected way: a ContiguousBuffer<const unsigned char, ...> may be
377   // initialized from std::vector<char>, but a ContiguousBuffer<unsigned char,
378   // ...> may not be initialized from std::string_view.
379   template <
380       typename T,
381       typename = typename ::std::enable_if<
382           IsAliasSafe<typename ::std::remove_cv<
383               typename ::std::remove_reference<decltype(*(
384                   ::std::declval<T>().data()))>::type>::type>::value && ::std::
385               is_same<typename AddSourceCV<
386                           decltype(*::std::declval<T>().data()), Byte>::Type,
387                       Byte>::value>::type>
388   explicit ContiguousBuffer(T *bytes)
389       : bytes_{reinterpret_cast<Byte *>(bytes->data())}, size_{bytes->size()} {
390     if (bytes != nullptr)
391       EMBOSS_DCHECK_POINTER_ALIGNMENT(bytes, kAlignment, kOffset);
392   }
393 
394   // Constructs a ContiguousBuffer from a pointer to a char type and a size.  As
395   // with the constructor from a container, above, Byte must be at least as
396   // cv-qualified as T.
397   template <typename T,
398             typename = typename ::std::enable_if<
399                 IsAliasSafe<T>::value && ::std::is_same<
400                     typename AddSourceCV<T, Byte>::Type, Byte>::value>>
401   explicit ContiguousBuffer(T *bytes, ::std::size_t size)
402       : bytes_{reinterpret_cast<Byte *>(bytes)},
403         size_{bytes == nullptr ? 0 : size} {
404     if (bytes != nullptr)
405       EMBOSS_DCHECK_POINTER_ALIGNMENT(bytes, kAlignment, kOffset);
406   }
407 
408   // Constructs a ContiguousBuffer from nullptr.  Equivalent to
409   // ContiguousBuffer().
410   //
411   // TODO(bolms): Update callers and remove this constructor.
412   explicit ContiguousBuffer(::std::nullptr_t) : bytes_{nullptr}, size_{0} {}
413 
414   // Implicitly construct or assign a ContiguousBuffer from a ContiguousBuffer.
415   ContiguousBuffer(const ContiguousBuffer &other) = default;
416   ContiguousBuffer& operator=(const ContiguousBuffer& other) = default;
417 
418   // Explicitly construct a ContiguousBuffers from another, compatible
419   // ContiguousBuffer.  A compatible ContiguousBuffer has an
420   // equally-or-less-cv-qualified Byte type, an alignment that is an exact
421   // multiple of this ContiguousBuffer's alignment, and an offset that is the
422   // same when reduced to this ContiguousBuffer's alignment.
423   //
424   // The final !::std::is_same<...> clause prevents this constructor from
425   // overlapping with the *implicit* copy constructor.
426   template <
427       typename OtherByte, ::std::size_t kOtherAlignment,
428       ::std::size_t kOtherOffset,
429       typename = typename ::std::enable_if<
430           kOtherAlignment % kAlignment == 0 &&
431           kOtherOffset % kAlignment ==
432               kOffset && ::std::is_same<
433                   typename AddSourceCV<OtherByte, Byte>::Type, Byte>::value &&
434           !::std::is_same<ContiguousBuffer,
435                           ContiguousBuffer<OtherByte, kOtherAlignment,
436                                            kOtherOffset>>::value>::type>
437   explicit ContiguousBuffer(
438       const ContiguousBuffer<OtherByte, kOtherAlignment, kOtherOffset> &other)
439       : bytes_{reinterpret_cast<Byte *>(other.data())},
440         size_{other.SizeInBytes()} {}
441 
442   // Compare a ContiguousBuffers to another, compatible ContiguousBuffer.
443   template <typename OtherByte, ::std::size_t kOtherAlignment,
444             ::std::size_t kOtherOffset,
445             typename = typename ::std::enable_if<
446                 kOtherAlignment % kAlignment == 0 &&
447                 kOtherOffset % kAlignment ==
448                     kOffset && ::std::is_same<
449                         typename AddSourceCV<OtherByte, Byte>::Type,
450                         Byte>::value>::type>
451   bool operator==(const ContiguousBuffer<OtherByte, kOtherAlignment,
452                                          kOtherOffset> &other) const {
453     return bytes_ == reinterpret_cast<Byte *>(other.data()) &&
454            size_ == other.SizeInBytes();
455   }
456 
457   // Compare a ContiguousBuffers to another, compatible ContiguousBuffer.
458   template <typename OtherByte, ::std::size_t kOtherAlignment,
459             ::std::size_t kOtherOffset,
460             typename = typename ::std::enable_if<
461                 kOtherAlignment % kAlignment == 0 &&
462                 kOtherOffset % kAlignment ==
463                     kOffset && ::std::is_same<
464                         typename AddSourceCV<OtherByte, Byte>::Type,
465                         Byte>::value>::type>
466   bool operator!=(const ContiguousBuffer<OtherByte, kOtherAlignment,
467                                          kOtherOffset> &other) const {
468     return !(*this == other);
469   }
470 
471   // Assignment from a compatible ContiguousBuffer.
472   template <typename OtherByte, ::std::size_t kOtherAlignment,
473             ::std::size_t kOtherOffset,
474             typename = typename ::std::enable_if<
475                 kOtherAlignment % kAlignment == 0 &&
476                 kOtherOffset % kAlignment ==
477                     kOffset && ::std::is_same<
478                         typename AddSourceCV<OtherByte, Byte>::Type,
479                         Byte>::value>::type>
480   ContiguousBuffer &operator=(
481       const ContiguousBuffer<OtherByte, kOtherAlignment, kOtherOffset> &other) {
482     bytes_ = reinterpret_cast<Byte *>(other.data());
483     size_ = other.SizeInBytes();
484     return *this;
485   }
486 
487   // GetOffsetStorage returns a new ContiguousBuffer that is a subsection of
488   // this ContiguousBuffer, with appropriate alignment assertions.  The new
489   // ContiguousBuffer will point to a region `offset` bytes into the original
490   // ContiguousBuffer, with a size of `max(size, original_size - offset)`.
491   //
492   // The kSubAlignment and kSubOffset template parameters act as assertions
493   // about the value of `offset`: `offset % (kSubAlignment / 8) - (kSubOffset /
494   // 8)` must be zero.  That is, if `kSubAlignment` is 16 and `kSubOffset` is 8,
495   // then `offset` may be 1, 3, 5, 7, etc.
496   //
497   // As a special case, if `kSubAlignment` is 0, then `offset` must exactly
498   // equal `kSubOffset`.
499   //
500   // This method is used by generated structure views to get backing buffers for
501   // views of their fields; the code generator can determine proper values for
502   // `kSubAlignment` and `kSubOffset`.
503   template </**/ ::std::size_t kSubAlignment, ::std::size_t kSubOffset>
504   OffsetStorageType<kSubAlignment, kSubOffset> GetOffsetStorage(
505       ::std::size_t offset, ::std::size_t size) const {
506     static_assert(kSubAlignment == 0 || kSubAlignment > kSubOffset,
507                   "kSubAlignment must be greater than kSubOffset.");
508     // Emboss provides a fast, unchecked path for reads and writes like:
509     //
510     // view.field().subfield().UncheckedWrite().
511     //
512     // Each of .field() and .subfield() call GetOffsetStorage(), so
513     // GetOffsetStorage() must be small and fast.
514     if (kSubAlignment == 0) {
515       EMBOSS_DCHECK_EQ(offset, kSubOffset);
516     } else {
517       // The weird ?:, below, silences -Werror=div-by-zero on versions of GCC
518       // that aren't smart enough to figure out that kSubAlignment can't be zero
519       // in this branch.
520       EMBOSS_DCHECK_EQ(offset % (kSubAlignment == 0 ? 1 : kSubAlignment),
521                        kSubOffset);
522     }
523     using ResultStorageType = OffsetStorageType<kSubAlignment, kSubOffset>;
524     return bytes_ == nullptr
525                ? ResultStorageType{nullptr}
526                : ResultStorageType{
527                      bytes_ + offset,
528                      size_ < offset ? 0 : ::std::min(size, size_ - offset)};
529   }
530 
531   // ReadLittleEndianUInt, ReadBigEndianUInt, and the unchecked versions thereof
532   // provide efficient multibyte read access to the underlying buffer.  The
533   // kBits template parameter should always equal the buffer size when these are
534   // called.
535   //
536   // Generally, types other than unsigned integers can be relatively efficiently
537   // converted from unsigned integers, and views should use Read...UInt to read
538   // the raw value, then convert.
539   //
540   // Read...UInt always reads the entire buffer; to read a smaller section, use
541   // GetOffsetStorage first.
542   template </**/ ::std::size_t kBits>
543   typename LeastWidthInteger<kBits>::Unsigned ReadLittleEndianUInt() const {
544     EMBOSS_CHECK_EQ(SizeInBytes() * 8, kBits);
545     EMBOSS_CHECK_POINTER_ALIGNMENT(bytes_, kAlignment, kOffset);
546     return UncheckedReadLittleEndianUInt<kBits>();
547   }
548   template </**/ ::std::size_t kBits>
549   typename LeastWidthInteger<kBits>::Unsigned UncheckedReadLittleEndianUInt()
550       const {
551     static_assert(kBits % 8 == 0,
552                   "ContiguousBuffer::ReadLittleEndianUInt() can only read "
553                   "whole-byte values.");
554     return MemoryAccessor<Byte, kAlignment, kOffset,
555                           kBits>::ReadLittleEndianUInt(bytes_);
556   }
557   template </**/ ::std::size_t kBits>
558   typename LeastWidthInteger<kBits>::Unsigned ReadBigEndianUInt() const {
559     EMBOSS_CHECK_EQ(SizeInBytes() * 8, kBits);
560     EMBOSS_CHECK_POINTER_ALIGNMENT(bytes_, kAlignment, kOffset);
561     return UncheckedReadBigEndianUInt<kBits>();
562   }
563   template </**/ ::std::size_t kBits>
564   typename LeastWidthInteger<kBits>::Unsigned UncheckedReadBigEndianUInt()
565       const {
566     static_assert(kBits % 8 == 0,
567                   "ContiguousBuffer::ReadBigEndianUInt() can only read "
568                   "whole-byte values.");
569     return MemoryAccessor<Byte, kAlignment, kOffset, kBits>::ReadBigEndianUInt(
570         bytes_);
571   }
572 
573   // WriteLittleEndianUInt, WriteBigEndianUInt, and the unchecked versions
574   // thereof provide efficient write access to the buffer.  Similar to the Read
575   // methods above, they write the entire buffer from an unsigned integer;
576   // non-unsigned values should be converted to the equivalent bit pattern, then
577   // written, and to write a subsection of the buffer use GetOffsetStorage
578   // first.
579   template </**/ ::std::size_t kBits>
580   void WriteLittleEndianUInt(
581       typename LeastWidthInteger<kBits>::Unsigned value) const {
582     EMBOSS_CHECK_EQ(SizeInBytes() * 8, kBits);
583     EMBOSS_CHECK_POINTER_ALIGNMENT(bytes_, kAlignment, kOffset);
584     UncheckedWriteLittleEndianUInt<kBits>(value);
585   }
586   template </**/ ::std::size_t kBits>
587   void UncheckedWriteLittleEndianUInt(
588       typename LeastWidthInteger<kBits>::Unsigned value) const {
589     static_assert(kBits % 8 == 0,
590                   "ContiguousBuffer::WriteLittleEndianUInt() can only write "
591                   "whole-byte values.");
592     MemoryAccessor<Byte, kAlignment, kOffset, kBits>::WriteLittleEndianUInt(
593         bytes_, value);
594   }
595   template </**/ ::std::size_t kBits>
596   void WriteBigEndianUInt(
597       typename LeastWidthInteger<kBits>::Unsigned value) const {
598     EMBOSS_CHECK_EQ(SizeInBytes() * 8, kBits);
599     EMBOSS_CHECK_POINTER_ALIGNMENT(bytes_, kAlignment, kOffset);
600     return UncheckedWriteBigEndianUInt<kBits>(value);
601   }
602   template </**/ ::std::size_t kBits>
603   void UncheckedWriteBigEndianUInt(
604       typename LeastWidthInteger<kBits>::Unsigned value) const {
605     static_assert(kBits % 8 == 0,
606                   "ContiguousBuffer::WriteBigEndianUInt() can only write "
607                   "whole-byte values.");
608     MemoryAccessor<Byte, kAlignment, kOffset, kBits>::WriteBigEndianUInt(bytes_,
609                                                                          value);
610   }
611 
612   template <typename OtherByte, ::std::size_t kOtherAlignment,
613             ::std::size_t kOtherOffset>
614   void UncheckedCopyFrom(
615       const ContiguousBuffer<OtherByte, kOtherAlignment, kOtherOffset> &other,
616       ::std::size_t size) const {
617     memmove(data(), other.data(), size);
618   }
619   template <typename OtherByte, ::std::size_t kOtherAlignment,
620             ::std::size_t kOtherOffset>
621   void CopyFrom(
622       const ContiguousBuffer<OtherByte, kOtherAlignment, kOtherOffset> &other,
623       ::std::size_t size) const {
624     EMBOSS_CHECK(Ok());
625     EMBOSS_CHECK(other.Ok());
626     // It is OK if either buffer contains extra bytes that are not being copied.
627     EMBOSS_CHECK_GE(SizeInBytes(), size);
628     EMBOSS_CHECK_GE(other.SizeInBytes(), size);
629     UncheckedCopyFrom(other, size);
630   }
631   template <typename OtherByte, ::std::size_t kOtherAlignment,
632             ::std::size_t kOtherOffset>
633   bool TryToCopyFrom(
634       const ContiguousBuffer<OtherByte, kOtherAlignment, kOtherOffset> &other,
635       ::std::size_t size) const {
636     if (Ok() && other.Ok() && SizeInBytes() >= size &&
637         other.SizeInBytes() >= size) {
638       UncheckedCopyFrom(other, size);
639       return true;
640     }
641     return false;
642   }
643   ::std::size_t SizeInBytes() const { return size_; }
644   bool Ok() const { return bytes_ != nullptr; }
645   Byte *data() const { return bytes_; }
646   Byte *begin() const { return bytes_; }
647   Byte *end() const { return bytes_ + size_; }
648 
649   // Constructs a string type from the underlying data; mostly intended to be
650   // called as:
651   //
652   //     buffer.ToString<std::string>();
653   //
654   // or:
655   //
656   //     buffer.ToString<std::string_view>();
657   //
658   // ... but it should also work with any similar-enough classes, such as
659   // std::basic_string_view<unsigned char> or Google's absl::string_view.
660   //
661   // Note that this may or may not make a copy of the underlying data,
662   // depending on the behavior of the given string type.
663   template <typename String>
664   typename ::std::enable_if<
665       IsAliasSafe<typename ::std::remove_reference<
666           decltype(*::std::declval<String>().data())>::type>::value,
667       String>::type
668   ToString() const {
669     return String(
670         reinterpret_cast<
671             const typename ::std::remove_reference<typename ::std::remove_cv<
672                 decltype(*::std::declval<String>().data())>::type>::type *>(
673             bytes_),
674         size_);
675   }
676 
677  private:
678   Byte *bytes_ = nullptr;
679   ::std::size_t size_ = 0;
680 };
681 
682 // TODO(bolms): Remove these aliases.
683 using ReadWriteContiguousBuffer = ContiguousBuffer<unsigned char, 1, 0>;
684 using ReadOnlyContiguousBuffer = ContiguousBuffer<const unsigned char, 1, 0>;
685 
686 // LittleEndianByteOrderer is a pass-through adapter for a byte buffer class.
687 // It is used to implement little-endian bit blocks.
688 //
689 // When used by BitBlock, the resulting bits are numbered as if they are
690 // little-endian:
691 //
692 //                      bit addresses of each bit in each byte
693 //           +----+----+----+----+----+----+----+----+----+----+----+----+----
694 // bit in  7 |  7 | 15 | 23 | 31 | 39 | 47 | 55 | 63 | 71 | 79 | 87 | 95 |
695 // byte    6 |  6 | 14 | 22 | 30 | 38 | 46 | 54 | 62 | 70 | 78 | 86 | 94 |
696 //         5 |  5 | 13 | 21 | 29 | 37 | 45 | 53 | 61 | 69 | 77 | 85 | 93 |
697 //         4 |  4 | 12 | 20 | 28 | 36 | 44 | 52 | 60 | 68 | 76 | 84 | 92 |
698 //         3 |  3 | 11 | 19 | 27 | 35 | 43 | 51 | 59 | 67 | 75 | 83 | 91 | ...
699 //         2 |  2 | 10 | 18 | 26 | 34 | 42 | 50 | 58 | 66 | 74 | 82 | 90 |
700 //         1 |  1 |  9 | 17 | 25 | 33 | 41 | 49 | 57 | 65 | 73 | 81 | 89 |
701 //         0 |  0 |  8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 |
702 //           +----+----+----+----+----+----+----+----+----+----+----+----+----
703 //              0    1    2    3    4    5    6    7    8    9   10   11   ...
704 //                                  byte address
705 //
706 // Because endian-specific reads and writes are handled in ContiguousBuffer,
707 // this class exists mostly to translate VerbUInt calls to VerbLittleEndianUInt.
708 template <class BufferT>
709 class LittleEndianByteOrderer final {
710  public:
711   // Type declaration so that BitBlock can use BufferType::BufferType.
712   using BufferType = BufferT;
713 
714   LittleEndianByteOrderer() : buffer_() {}
715   explicit LittleEndianByteOrderer(BufferType buffer) : buffer_{buffer} {}
716   LittleEndianByteOrderer(const LittleEndianByteOrderer &other) = default;
717   LittleEndianByteOrderer(LittleEndianByteOrderer &&other) = default;
718   LittleEndianByteOrderer &operator=(const LittleEndianByteOrderer &other) =
719       default;
720 
721   // LittleEndianByteOrderer just passes straight through to the underlying
722   // buffer.
723   bool Ok() const { return buffer_.Ok(); }
724   ::std::size_t SizeInBytes() const { return buffer_.SizeInBytes(); }
725 
726   template </**/ ::std::size_t kBits>
727   typename LeastWidthInteger<kBits>::Unsigned ReadUInt() const {
728     return buffer_.template ReadLittleEndianUInt<kBits>();
729   }
730   template </**/ ::std::size_t kBits>
731   typename LeastWidthInteger<kBits>::Unsigned UncheckedReadUInt() const {
732     return buffer_.template UncheckedReadLittleEndianUInt<kBits>();
733   }
734   template </**/ ::std::size_t kBits>
735   void WriteUInt(typename LeastWidthInteger<kBits>::Unsigned value) const {
736     buffer_.template WriteLittleEndianUInt<kBits>(value);
737   }
738   template </**/ ::std::size_t kBits>
739   void UncheckedWriteUInt(
740       typename LeastWidthInteger<kBits>::Unsigned value) const {
741     buffer_.template UncheckedWriteLittleEndianUInt<kBits>(value);
742   }
743 
744  private:
745   BufferType buffer_;
746 };
747 
748 // BigEndianByteOrderer is an adapter for a byte buffer class which reverses
749 // the addresses of the underlying byte buffer.  It is used to implement
750 // big-endian bit blocks.
751 //
752 // When used by BitBlock, the resulting bits are numbered with "bit 0" as the
753 // lowest-order bit of the *last* byte in the buffer.  For example, for a
754 // 12-byte buffer, the bit ordering looks like:
755 //
756 //                      bit addresses of each bit in each byte
757 //           +----+----+----+----+----+----+----+----+----+----+----+----+
758 // bit in  7 | 95 | 87 | 79 | 71 | 63 | 55 | 47 | 39 | 31 | 23 | 15 |  7 |
759 // byte    6 | 94 | 86 | 78 | 70 | 62 | 54 | 46 | 38 | 30 | 22 | 14 |  6 |
760 //         5 | 93 | 85 | 77 | 69 | 61 | 53 | 45 | 37 | 29 | 21 | 13 |  5 |
761 //         4 | 92 | 84 | 76 | 68 | 60 | 52 | 44 | 36 | 28 | 20 | 12 |  4 |
762 //         3 | 91 | 83 | 75 | 67 | 59 | 51 | 43 | 35 | 27 | 19 | 11 |  3 |
763 //         2 | 90 | 82 | 74 | 66 | 58 | 50 | 42 | 34 | 26 | 18 | 10 |  2 |
764 //         1 | 89 | 81 | 73 | 65 | 57 | 49 | 41 | 33 | 25 | 17 |  9 |  1 |
765 //         0 | 88 | 80 | 72 | 64 | 56 | 48 | 40 | 32 | 24 | 16 |  8 |  0 |
766 //           +----+----+----+----+----+----+----+----+----+----+----+----+
767 //              0    1    2    3    4    5    6    7    8    9   10   11
768 //                                  byte address
769 //
770 // Note that some big-endian protocols are documented with "bit 0" being the
771 // *high-order* bit of a number, in which case "bit 0" would be the
772 // highest-order bit of the first byte in the buffer.  The "bit 0 is the
773 // high-order bit" style seems to be more common in older documents (e.g., RFCs
774 // 791 and 793, for IP and TCP), while the Emboss-style "bit 0 is in the last
775 // byte" seems to be more common in newer documents (e.g., the hardware user
776 // manuals bolms@ examined).
777 // TODO(bolms): Examine more documents to see if the old vs new pattern holds.
778 //
779 // Because endian-specific reads and writes are handled in ContiguousBuffer,
780 // this class exists mostly to translate VerbUInt calls to VerbBigEndianUInt.
781 template <class BufferT>
782 class BigEndianByteOrderer final {
783  public:
784   // Type declaration so that BitBlock can use BufferType::BufferType.
785   using BufferType = BufferT;
786 
787   BigEndianByteOrderer() : buffer_() {}
788   explicit BigEndianByteOrderer(BufferType buffer) : buffer_{buffer} {}
789   BigEndianByteOrderer(const BigEndianByteOrderer &other) = default;
790   BigEndianByteOrderer(BigEndianByteOrderer &&other) = default;
791   BigEndianByteOrderer &operator=(const BigEndianByteOrderer &other) = default;
792 
793   // Ok() and SizeInBytes() get passed through with no changes.
794   bool Ok() const { return buffer_.Ok(); }
795   ::std::size_t SizeInBytes() const { return buffer_.SizeInBytes(); }
796 
797   template </**/ ::std::size_t kBits>
798   typename LeastWidthInteger<kBits>::Unsigned ReadUInt() const {
799     return buffer_.template ReadBigEndianUInt<kBits>();
800   }
801   template </**/ ::std::size_t kBits>
802   typename LeastWidthInteger<kBits>::Unsigned UncheckedReadUInt() const {
803     return buffer_.template UncheckedReadBigEndianUInt<kBits>();
804   }
805   template </**/ ::std::size_t kBits>
806   void WriteUInt(typename LeastWidthInteger<kBits>::Unsigned value) const {
807     buffer_.template WriteBigEndianUInt<kBits>(value);
808   }
809   template </**/ ::std::size_t kBits>
810   void UncheckedWriteUInt(
811       typename LeastWidthInteger<kBits>::Unsigned value) const {
812     buffer_.template UncheckedWriteBigEndianUInt<kBits>(value);
813   }
814 
815  private:
816   BufferType buffer_;
817 };
818 
819 // NullByteOrderer is a pass-through adapter for a byte buffer class.  It is
820 // used to implement single-byte bit blocks, where byte order does not matter.
821 //
822 // Technically, it should be valid to swap in BigEndianByteOrderer or
823 // LittleEndianByteOrderer anywhere that NullByteOrderer is used, but
824 // NullByteOrderer contains a few extra CHECKs to ensure it is being used
825 // correctly.
826 template <class BufferT>
827 class NullByteOrderer final {
828  public:
829   // Type declaration so that BitBlock can use BufferType::BufferType.
830   using BufferType = BufferT;
831 
832   NullByteOrderer() : buffer_() {}
833   explicit NullByteOrderer(BufferType buffer) : buffer_{buffer} {}
834   NullByteOrderer(const NullByteOrderer &other) = default;
835   NullByteOrderer(NullByteOrderer &&other) = default;
836   NullByteOrderer &operator=(const NullByteOrderer &other) = default;
837 
838   bool Ok() const { return buffer_.Ok(); }
839   ::std::size_t SizeInBytes() const { return Ok() ? 1 : 0; }
840 
841   template </**/ ::std::size_t kBits>
842   typename LeastWidthInteger<kBits>::Unsigned ReadUInt() const {
843     static_assert(kBits == 8, "NullByteOrderer may only read 8-bit values.");
844     return buffer_.template ReadLittleEndianUInt<kBits>();
845   }
846   template </**/ ::std::size_t kBits>
847   typename LeastWidthInteger<kBits>::Unsigned UncheckedReadUInt() const {
848     static_assert(kBits == 8, "NullByteOrderer may only read 8-bit values.");
849     return buffer_.template UncheckedReadLittleEndianUInt<kBits>();
850   }
851   template </**/ ::std::size_t kBits>
852   void WriteUInt(typename LeastWidthInteger<kBits>::Unsigned value) const {
853     static_assert(kBits == 8, "NullByteOrderer may only read 8-bit values.");
854     buffer_.template WriteBigEndianUInt<kBits>(value);
855   }
856   template </**/ ::std::size_t kBits>
857   void UncheckedWriteUInt(
858       typename LeastWidthInteger<kBits>::Unsigned value) const {
859     static_assert(kBits == 8, "NullByteOrderer may only read 8-bit values.");
860     buffer_.template UncheckedWriteBigEndianUInt<kBits>(value);
861   }
862 
863  private:
864   BufferType buffer_;
865 };
866 
867 // OffsetBitBlock is a filter on another BitBlock class, which adds a fixed
868 // offset to reads from underlying bit block.  This is used by Emboss generated
869 // classes to read bitfields: the parent provides an OffsetBitBlock of its
870 // buffer to the child's view.
871 //
872 // OffsetBitBlock is always statically sized, but because
873 // BitBlock::GetOffsetStorage and OffsetBitBlock::GetOffsetStorage must have the
874 // same signature as ContiguousBuffer::GetOffsetStorage, OffsetBitBlock's size
875 // parameter must be a runtime value.
876 //
877 // TODO(bolms): Figure out how to add size as a template parameter to
878 // OffsetBitBlock.
879 template <class UnderlyingBitBlockType>
880 class OffsetBitBlock final {
881  public:
882   using ValueType = typename UnderlyingBitBlockType::ValueType;
883   // Bit blocks do not use alignment information, but generated code expects bit
884   // blocks to have the same methods and types as byte blocks, so even though
885   // kNewAlignment and kNewOffset are unused, they must be present as template
886   // parameters.
887   template </**/ ::std::size_t kNewAlignment, ::std::size_t kNewOffset>
888   using OffsetStorageType = OffsetBitBlock<UnderlyingBitBlockType>;
889 
890   OffsetBitBlock() : bit_block_(), offset_(0), size_(0), ok_(false) {}
891   explicit OffsetBitBlock(UnderlyingBitBlockType bit_block,
892                           ::std::size_t offset, ::std::size_t size, bool ok)
893       : bit_block_{bit_block},
894         offset_{static_cast</**/ ::std::uint8_t>(offset)},
895         size_{static_cast</**/ ::std::uint8_t>(size)},
896         ok_{offset == offset_ && size == size_ && ok} {}
897   OffsetBitBlock(const OffsetBitBlock &other) = default;
898   OffsetBitBlock &operator=(const OffsetBitBlock &other) = default;
899 
900   template </**/ ::std::size_t kNewAlignment, ::std::size_t kNewOffset>
901   OffsetStorageType<kNewAlignment, kNewOffset> GetOffsetStorage(
902       ::std::size_t offset, ::std::size_t size) const {
903     return OffsetStorageType<kNewAlignment, kNewOffset>{
904         bit_block_, offset_ + offset, size, ok_ && offset + size <= size_};
905   }
906 
907   // ReadUInt reads the entire underlying bit block, then shifts and masks to
908   // the appropriate size.
909   ValueType ReadUInt() const {
910     EMBOSS_CHECK_GE(bit_block_.SizeInBits(), offset_ + size_);
911     EMBOSS_CHECK_GE(bit_block_.SizeInBits(),
912                     static_cast</**/ ::std::uint64_t>(offset_ + size_));
913     EMBOSS_CHECK(Ok());
914     return MaskToNBits(bit_block_.ReadUInt(), offset_ + size_) >> offset_;
915   }
916   ValueType UncheckedReadUInt() const {
917     return MaskToNBits(bit_block_.UncheckedReadUInt(), offset_ + size_) >>
918            offset_;
919   }
920 
921   // WriteUInt writes the entire underlying bit block; in order to only write
922   // the specific bits that should be changed, the current value is first read,
923   // then masked out and or'ed with the new value, and finally the result is
924   // written back to memory.
925   void WriteUInt(ValueType value) const {
926     EMBOSS_CHECK_EQ(value, MaskToNBits(value, size_));
927     EMBOSS_CHECK(Ok());
928     // OffsetBitBlock::WriteUInt *always* does a read-modify-write because it is
929     // assumed that if the user wanted to read or write the entire value they
930     // would just use the underlying BitBlock directly.  This is mostly true for
931     // code generated by Emboss, which only uses OffsetBitBlock for subfields of
932     // `bits` types; bit-oriented types such as `UInt` will use BitBlock
933     // directly when they are placed directly in a `struct`.
934     bit_block_.WriteUInt(MaskInValue(bit_block_.ReadUInt(), value));
935   }
936   void UncheckedWriteUInt(ValueType value) const {
937     bit_block_.UncheckedWriteUInt(
938         MaskInValue(bit_block_.UncheckedReadUInt(), value));
939   }
940 
941   ::std::size_t SizeInBits() const { return size_; }
942   bool Ok() const { return ok_; }
943 
944  private:
945   ValueType MaskInValue(ValueType original_value, ValueType new_value) const {
946     ValueType original_mask = static_cast<ValueType>(~(
947         MaskToNBits(static_cast<ValueType>(~ValueType{0}), size_) << offset_));
948     return static_cast<ValueType>((original_value & original_mask) |
949                                   (new_value << offset_));
950   }
951 
952   const UnderlyingBitBlockType bit_block_;
953   const ::std::uint8_t offset_;
954   const ::std::uint8_t size_;
955   const ::std::uint8_t ok_;
956 };
957 
958 // BitBlock is a view of a short, fixed-size sequence of bits somewhere in
959 // memory.  Big- and little-endian values are handled by BufferType, which is
960 // typically BigEndianByteOrderer<ContiguousBuffer<...>> or
961 // LittleEndianByteOrderer<ContiguousBuffer<...>>.
962 //
963 // BitBlock is implemented such that it always reads and writes its entire
964 // buffer; unlike ContiguousBuffer for bytes, there is no way to modify part of
965 // the underlying data without doing a read-modify-write of the full value.
966 // This sidesteps a lot of weirdness with converting between bit addresses and
967 // byte addresses for big-endian values, though it does mean that in certain
968 // cases excess bits will be read or written, particularly if care is not taken
969 // in the .emb definition to keep `bits` types to a minimum size.
970 template <class BufferType, ::std::size_t kBufferSizeInBits>
971 class BitBlock final {
972   static_assert(kBufferSizeInBits % 8 == 0,
973                 "BitBlock can only operate on byte buffers.");
974   static_assert(kBufferSizeInBits <= 64,
975                 "BitBlock can only operate on small buffers.");
976 
977  public:
978   using ValueType = typename LeastWidthInteger<kBufferSizeInBits>::Unsigned;
979   // As with OffsetBitBlock::OffsetStorageType, the kNewAlignment and kNewOffset
980   // values are not used, but they must be template parameters so that generated
981   // code can work with both BitBlock and ContiguousBuffer.
982   template </**/ ::std::size_t kNewAlignment, ::std::size_t kNewOffset>
983   using OffsetStorageType =
984       OffsetBitBlock<BitBlock<BufferType, kBufferSizeInBits>>;
985 
986   explicit BitBlock() : buffer_() {}
987   explicit BitBlock(BufferType buffer) : buffer_{buffer} {}
988   explicit BitBlock(typename BufferType::BufferType buffer) : buffer_{buffer} {}
989   BitBlock(const BitBlock &) = default;
990   BitBlock(BitBlock &&) = default;
991   BitBlock &operator=(const BitBlock &) = default;
992   BitBlock &operator=(BitBlock &&) = default;
993   ~BitBlock() = default;
994 
995   static constexpr ::std::size_t Bits() { return kBufferSizeInBits; }
996 
997   template </**/ ::std::size_t kNewAlignment, ::std::size_t kNewOffset>
998   OffsetStorageType<kNewAlignment, kNewOffset> GetOffsetStorage(
999       ::std::size_t offset, ::std::size_t size) const {
1000     return OffsetStorageType<kNewAlignment, kNewOffset>{
1001         *this, offset, size, Ok() && offset + size <= kBufferSizeInBits};
1002   }
1003 
1004   // BitBlock clients must read or write the entire BitBlock value as an
1005   // unsigned integer.  OffsetBitBlock can be used to extract a portion of the
1006   // value via shift and mask, and individual view types such as IntView or
1007   // BcdView are expected to convert ValueType to/from their desired types.
1008   ValueType ReadUInt() const {
1009     return buffer_.template ReadUInt<kBufferSizeInBits>();
1010   }
1011   ValueType UncheckedReadUInt() const {
1012     return buffer_.template UncheckedReadUInt<kBufferSizeInBits>();
1013   }
1014   void WriteUInt(ValueType value) const {
1015     EMBOSS_CHECK_EQ(value, MaskToNBits(value, kBufferSizeInBits));
1016     buffer_.template WriteUInt<kBufferSizeInBits>(value);
1017   }
1018   void UncheckedWriteUInt(ValueType value) const {
1019     buffer_.template UncheckedWriteUInt<kBufferSizeInBits>(value);
1020   }
1021 
1022   ::std::size_t SizeInBits() const { return kBufferSizeInBits; }
1023   bool Ok() const {
1024     return buffer_.Ok() && buffer_.SizeInBytes() * 8 == kBufferSizeInBits;
1025   }
1026 
1027  private:
1028   BufferType buffer_;
1029 };
1030 
1031 }  // namespace support
1032 }  // namespace emboss
1033 
1034 #endif  // EMBOSS_RUNTIME_CPP_EMBOSS_MEMORY_UTIL_H_
1035