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