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 // This header contains implementations of the types in the Emboss Prelude
16 // (UInt, Int, Flag, etc.)
17 #ifndef EMBOSS_RUNTIME_CPP_EMBOSS_PRELUDE_H_
18 #define EMBOSS_RUNTIME_CPP_EMBOSS_PRELUDE_H_
19
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <limits>
26 #include <type_traits>
27 #include <utility>
28
29 #include "runtime/cpp/emboss_cpp_util.h"
30
31 // Forward declarations for optional text processing helpers.
32 namespace emboss {
33 class TextOutputOptions;
34 namespace support {
35 template <class Stream, class View>
36 bool ReadBooleanFromTextStream(View *view, Stream *stream);
37 template <class Stream, class View>
38 void WriteBooleanViewToTextStream(View *view, Stream *stream,
39 const TextOutputOptions &);
40
41 template <class Stream, class View>
42 bool ReadIntegerFromTextStream(View *view, Stream *stream);
43 template <class Stream, class View>
44 void WriteIntegerViewToTextStream(View *view, Stream *stream,
45 const TextOutputOptions &options);
46
47 template <class Stream, class View>
48 bool ReadFloatFromTextStream(View *view, Stream *stream);
49 template <class Stream, class Float>
50 void WriteFloatToTextStream(Float n, Stream *stream,
51 const TextOutputOptions &options);
52 } // namespace support
53 } // namespace emboss
54
55 // This namespace must match the [(cpp) namespace] in the Emboss prelude.
56 namespace emboss {
57 namespace prelude {
58
59 // FlagView is the C++ implementation of the Emboss "Flag" type, which is a
60 // 1-bit value.
61 template <class Parameters, class BitBlock>
62 class FlagView final {
63 public:
64 static_assert(Parameters::kBits == 1, "FlagView must be 1 bit.");
65
FlagView(BitBlock bits)66 explicit FlagView(BitBlock bits) : bit_block_{bits} {}
FlagView()67 FlagView() : bit_block_() {}
68 FlagView(const FlagView &) = default;
69 FlagView(FlagView &&) = default;
70 FlagView &operator=(const FlagView &) = default;
71 FlagView &operator=(FlagView &&) = default;
72 ~FlagView() = default;
73
Read()74 bool Read() const {
75 bool result = bit_block_.ReadUInt();
76 EMBOSS_CHECK(Parameters::ValueIsOk(result));
77 return result;
78 }
UncheckedRead()79 bool UncheckedRead() const { return bit_block_.UncheckedReadUInt(); }
Write(bool value)80 void Write(bool value) const {
81 const bool result = TryToWrite(value);
82 (void)result;
83 EMBOSS_CHECK(result);
84 }
TryToWrite(bool value)85 bool TryToWrite(bool value) const {
86 if (!CouldWriteValue(value)) return false;
87 if (!IsComplete()) return false;
88 bit_block_.WriteUInt(value);
89 return true;
90 }
CouldWriteValue(bool value)91 static constexpr bool CouldWriteValue(bool value) {
92 return Parameters::ValueIsOk(value);
93 }
UncheckedWrite(bool value)94 void UncheckedWrite(bool value) const {
95 bit_block_.UncheckedWriteUInt(value);
96 }
97
98 template <typename OtherView>
CopyFrom(const OtherView & other)99 void CopyFrom(const OtherView &other) const {
100 Write(other.Read());
101 }
102 template <typename OtherView>
UncheckedCopyFrom(const OtherView & other)103 void UncheckedCopyFrom(const OtherView &other) const {
104 UncheckedWrite(other.UncheckedRead());
105 }
106 template <typename OtherView>
TryToCopyFrom(const OtherView & other)107 bool TryToCopyFrom(const OtherView &other) const {
108 return TryToWrite(other.Read());
109 }
110
Ok()111 bool Ok() const {
112 return IsComplete() && Parameters::ValueIsOk(UncheckedRead());
113 }
114 template <class OtherBitBlock>
Equals(const FlagView<Parameters,OtherBitBlock> & other)115 bool Equals(const FlagView<Parameters, OtherBitBlock> &other) const {
116 return Read() == other.Read();
117 }
118 template <class OtherBitBlock>
UncheckedEquals(const FlagView<Parameters,OtherBitBlock> & other)119 bool UncheckedEquals(const FlagView<Parameters, OtherBitBlock> &other) const {
120 return UncheckedRead() == other.UncheckedRead();
121 }
IsComplete()122 bool IsComplete() const {
123 return bit_block_.Ok() && bit_block_.SizeInBits() > 0;
124 }
125
126 template <class Stream>
UpdateFromTextStream(Stream * stream)127 bool UpdateFromTextStream(Stream *stream) const {
128 return ::emboss::support::ReadBooleanFromTextStream(this, stream);
129 }
130
131 template <class Stream>
WriteToTextStream(Stream * stream,const::emboss::TextOutputOptions & options)132 void WriteToTextStream(Stream *stream,
133 const ::emboss::TextOutputOptions &options) const {
134 ::emboss::support::WriteBooleanViewToTextStream(this, stream, options);
135 }
136
IsAggregate()137 static constexpr bool IsAggregate() { return false; }
138
139 private:
140 BitBlock bit_block_;
141 };
142
143 // UIntView is a view for UInts inside of bitfields.
144 template <class Parameters, class BitViewType>
145 class UIntView final {
146 public:
147 using ValueType = typename ::emboss::support::LeastWidthInteger<
148 Parameters::kBits>::Unsigned;
149
150 static_assert(
151 Parameters::kBits <= sizeof(ValueType) * 8,
152 "UIntView requires sizeof(ValueType) * 8 >= Parameters::kBits.");
153
154 template <typename... Args>
UIntView(Args &&...args)155 explicit UIntView(Args &&...args) : buffer_{::std::forward<Args>(args)...} {}
UIntView()156 UIntView() : buffer_() {}
157 UIntView(const UIntView &) = default;
158 UIntView(UIntView &&) = default;
159 UIntView &operator=(const UIntView &) = default;
160 UIntView &operator=(UIntView &&) = default;
161 ~UIntView() = default;
162
Read()163 ValueType Read() const {
164 ValueType result = static_cast<ValueType>(buffer_.ReadUInt());
165 EMBOSS_CHECK(Parameters::ValueIsOk(result));
166 return result;
167 }
UncheckedRead()168 ValueType UncheckedRead() const { return buffer_.UncheckedReadUInt(); }
169
170 // The Write, TryToWrite, and CouldWriteValue methods are templated in order
171 // to avoid surprises due to implicit narrowing.
172 //
173 // In C++, you can pass (say) an `int` to a function expecting `uint8_t`, and
174 // the compiler will silently cast the `int` to `uint8_t`, which can change
175 // the value. Even with fairly aggressive warnings, something like this will
176 // silently compile, and print `256 is not >= 128!`:
177 //
178 // bool is_big_uint8(uint8_t value) { return value >= 128; }
179 // bool is_big(uint32_t value) { return is_big_uint8(value); }
180 // int main() {
181 // assert(!is_big(256)); // big is truncated to 0.
182 // std::cout << 256 << " is not >= 128!\n";
183 // return 0;
184 // }
185 //
186 // (Most compilers will give a warning when directly passing a *constant* that
187 // gets truncated; for example, GCC will throw -Woverflow on
188 // `is_big_uint8(256U)`.)
189 template <typename IntT,
190 typename = typename ::std::enable_if<
191 (::std::numeric_limits<typename ::std::remove_cv<
192 typename ::std::remove_reference<IntT>::type>::type>::
193 is_integer &&
194 !::std::is_same<bool, typename ::std::remove_cv<
195 typename ::std::remove_reference<
196 IntT>::type>::type>::value) ||
197 ::std::is_enum<IntT>::value>::type>
Write(IntT value)198 void Write(IntT value) const {
199 const bool result = TryToWrite(value);
200 (void)result;
201 EMBOSS_CHECK(result);
202 }
203
204 template <typename IntT,
205 typename = typename ::std::enable_if<
206 (::std::numeric_limits<typename ::std::remove_cv<
207 typename ::std::remove_reference<IntT>::type>::type>::
208 is_integer &&
209 !::std::is_same<bool, typename ::std::remove_cv<
210 typename ::std::remove_reference<
211 IntT>::type>::type>::value) ||
212 ::std::is_enum<IntT>::value>::type>
TryToWrite(IntT value)213 bool TryToWrite(IntT value) const {
214 if (!CouldWriteValue(value)) return false;
215 if (!IsComplete()) return false;
216 buffer_.WriteUInt(static_cast<ValueType>(value));
217 return true;
218 }
219
220 template <typename IntT,
221 typename = typename ::std::enable_if<
222 (::std::numeric_limits<typename ::std::remove_cv<
223 typename ::std::remove_reference<IntT>::type>::type>::
224 is_integer &&
225 !::std::is_same<bool, typename ::std::remove_cv<
226 typename ::std::remove_reference<
227 IntT>::type>::type>::value) ||
228 ::std::is_enum<IntT>::value>::type>
CouldWriteValue(IntT value)229 static constexpr bool CouldWriteValue(IntT value) {
230 // Implicit conversions are doing some work here, but the upshot is that the
231 // value must be at least 0, and at most (2**kBits)-1. The clause to
232 // compute (2**kBits)-1 should not be "simplified" further.
233 //
234 // Because of C++ implicit integer promotions, the (2**kBits)-1 computation
235 // works differently when `ValueType` is smaller than `unsigned int` than it
236 // does when `ValueType` is at least as big as `unsigned int`.
237 //
238 // For example, when `ValueType` is `uint8_t` and `kBits` is 8:
239 //
240 // 1. `static_cast<ValueType>(1)` becomes `uint8_t(1)`.
241 // 2. `uint8_t(1) << (kBits - 1)` is `uint8_t(1) << 7`.
242 // 3. The shift operator `<<` promotes its left operand to `unsigned`,
243 // giving `unsigned(1) << 7`.
244 // 4. `unsigned(1) << 7` becomes `unsigned(0x80)`.
245 // 5. `unsigned(0x80) << 1` becomes `unsigned(0x100)`.
246 // 6. Finally, `unsigned(0x100) - 1` is `unsigned(0xff)`.
247 //
248 // (Note that the cases where `kBits` is less than `sizeof(ValueType) * 8`
249 // are very similar.)
250 //
251 // When `ValueType` is `uint32_t`, `unsigned` is 32 bits, and `kBits` is 32:
252 //
253 // 1. `static_cast<ValueType>(1)` becomes `uint32_t(1)`.
254 // 2. `uint32_t(1) << (kBits - 1)` is `uint32_t(1) << 31`.
255 // 3. The shift operator `<<` does *not* further promote `uint32_t`.
256 // 4. `uint32_t(1) << 31` becomes `uint32_t(0x80000000)`. Note that
257 // `uint32_t(1) << 32` would be undefined behavior (shift of >= the
258 // size of the left operand type), which is why the shift is broken
259 // into two parts.
260 // 5. `uint32_t(0x80000000) << 1` overflows, leaving `uint32_t(0)`.
261 // 6. `uint32_t(0) - 1` underflows, leaving `uint32_t(0xffffffff)`.
262 //
263 // Because unsigned overflow and underflow are defined to be modulo 2**N,
264 // where N is the number of bits in the type, this is entirely
265 // standards-compliant.
266 return value >= 0 &&
267 static_cast</**/ ::std::uint64_t>(value) <=
268 ((static_cast<ValueType>(1) << (Parameters::kBits - 1)) << 1) -
269 1 &&
270 Parameters::ValueIsOk(value);
271 }
UncheckedWrite(ValueType value)272 void UncheckedWrite(ValueType value) const {
273 buffer_.UncheckedWriteUInt(value);
274 }
275
276 template <typename OtherView>
CopyFrom(const OtherView & other)277 void CopyFrom(const OtherView &other) const {
278 Write(other.Read());
279 }
280 template <typename OtherView>
UncheckedCopyFrom(const OtherView & other)281 void UncheckedCopyFrom(const OtherView &other) const {
282 UncheckedWrite(other.UncheckedRead());
283 }
284 template <typename OtherView>
TryToCopyFrom(const OtherView & other)285 bool TryToCopyFrom(const OtherView &other) const {
286 return other.Ok() && TryToWrite(other.Read());
287 }
288
289 // All bit patterns in the underlying buffer are valid, so Ok() is always
290 // true if IsComplete() is true.
Ok()291 bool Ok() const {
292 return IsComplete() && Parameters::ValueIsOk(UncheckedRead());
293 }
294 template <class OtherBitViewType>
Equals(const UIntView<Parameters,OtherBitViewType> & other)295 bool Equals(const UIntView<Parameters, OtherBitViewType> &other) const {
296 return Read() == other.Read();
297 }
298 template <class OtherBitViewType>
UncheckedEquals(const UIntView<Parameters,OtherBitViewType> & other)299 bool UncheckedEquals(
300 const UIntView<Parameters, OtherBitViewType> &other) const {
301 return UncheckedRead() == other.UncheckedRead();
302 }
IsComplete()303 bool IsComplete() const {
304 return buffer_.Ok() && buffer_.SizeInBits() >= Parameters::kBits;
305 }
306
307 template <class Stream>
UpdateFromTextStream(Stream * stream)308 bool UpdateFromTextStream(Stream *stream) const {
309 return support::ReadIntegerFromTextStream(this, stream);
310 }
311
312 template <class Stream>
WriteToTextStream(Stream * stream,::emboss::TextOutputOptions & options)313 void WriteToTextStream(Stream *stream,
314 ::emboss::TextOutputOptions &options) const {
315 support::WriteIntegerViewToTextStream(this, stream, options);
316 }
317
IsAggregate()318 static constexpr bool IsAggregate() { return false; }
319
SizeInBits()320 static constexpr int SizeInBits() { return Parameters::kBits; }
321
322 private:
323 BitViewType buffer_;
324 };
325
326 // IntView is a view for Ints inside of bitfields.
327 template <class Parameters, class BitViewType>
328 class IntView final {
329 public:
330 using ValueType =
331 typename ::emboss::support::LeastWidthInteger<Parameters::kBits>::Signed;
332
333 static_assert(Parameters::kBits <= sizeof(ValueType) * 8,
334 "IntView requires sizeof(ValueType) * 8 >= Parameters::kBits.");
335
336 template <typename... Args>
IntView(Args &&...args)337 explicit IntView(Args &&...args) : buffer_{::std::forward<Args>(args)...} {}
IntView()338 IntView() : buffer_() {}
339 IntView(const IntView &) = default;
340 IntView(IntView &&) = default;
341 IntView &operator=(const IntView &) = default;
342 IntView &operator=(IntView &&) = default;
343 ~IntView() = default;
344
Read()345 ValueType Read() const {
346 ValueType value = ConvertToSigned(buffer_.ReadUInt());
347 EMBOSS_CHECK(Parameters::ValueIsOk(value));
348 return value;
349 }
UncheckedRead()350 ValueType UncheckedRead() const {
351 return ConvertToSigned(buffer_.UncheckedReadUInt());
352 }
353 // As with UIntView, above, Write, TryToWrite, and CouldWriteValue need to be
354 // templated in order to avoid surprises due to implicit narrowing
355 // conversions.
356 template <typename IntT,
357 typename = typename ::std::enable_if<
358 (::std::numeric_limits<typename ::std::remove_cv<
359 typename ::std::remove_reference<IntT>::type>::type>::
360 is_integer &&
361 !::std::is_same<bool, typename ::std::remove_cv<
362 typename ::std::remove_reference<
363 IntT>::type>::type>::value) ||
364 ::std::is_enum<IntT>::value>::type>
Write(IntT value)365 void Write(IntT value) const {
366 const bool result = TryToWrite(value);
367 (void)result;
368 EMBOSS_CHECK(result);
369 }
370
371 template <typename IntT,
372 typename = typename ::std::enable_if<
373 (::std::numeric_limits<typename ::std::remove_cv<
374 typename ::std::remove_reference<IntT>::type>::type>::
375 is_integer &&
376 !::std::is_same<bool, typename ::std::remove_cv<
377 typename ::std::remove_reference<
378 IntT>::type>::type>::value) ||
379 ::std::is_enum<IntT>::value>::type>
TryToWrite(IntT value)380 bool TryToWrite(IntT value) const {
381 if (!CouldWriteValue(value)) return false;
382 if (!IsComplete()) return false;
383 buffer_.WriteUInt(::emboss::support::MaskToNBits(
384 static_cast<typename BitViewType::ValueType>(value),
385 Parameters::kBits));
386 return true;
387 }
388
389 template <typename IntT,
390 typename = typename ::std::enable_if<
391 (::std::numeric_limits<typename ::std::remove_cv<
392 typename ::std::remove_reference<IntT>::type>::type>::
393 is_integer &&
394 !::std::is_same<bool, typename ::std::remove_cv<
395 typename ::std::remove_reference<
396 IntT>::type>::type>::value) ||
397 ::std::is_enum<IntT>::value>::type>
CouldWriteValue(IntT value)398 static constexpr bool CouldWriteValue(IntT value) {
399 // This effectively checks that value >= -(2**(kBits-1) and value <=
400 // (2**(kBits-1))-1.
401 //
402 // This has to be done somewhat piecemeal, in order to avoid various bits of
403 // undefined and implementation-defined behavior.
404 //
405 // First, if IntT is an unsigned type, the check that value >=
406 // -(2**(kBits-1)) is skipped, in order to avoid any signed <-> unsigned
407 // conversions.
408 //
409 // Second, if kBits is 1, then the limits -1 and 0 are explicit, so that
410 // there is never a shift by -1 (which is undefined behavior).
411 //
412 // Third, the shifts are by (kBits - 2), so that they do not alter sign
413 // bits. To get the final bounds, we use a bit of addition and
414 // multiplication. For example, for 8 bits, the lower bound is (1 << 6) *
415 // -2, which is 64 * -2, which is -128. The corresponding upper bound is
416 // ((1 << 6) - 1) * 2 + 1, which is (64 - 1) * 2 + 1, which is 63 * 2 + 1,
417 // which is 126 + 1, which is 127. The upper bound must be computed in
418 // multiple steps like this in order to avoid overflow.
419 return (!::std::is_signed<typename ::std::remove_cv<
420 typename ::std::remove_reference<IntT>::type>::type>::value ||
421 static_cast</**/ ::std::int64_t>(value) >=
422 (Parameters::kBits == 1
423 ? -1
424 : (static_cast<ValueType>(1) << (Parameters::kBits - 2)) *
425 -2)) &&
426 value <=
427 (Parameters::kBits == 1
428 ? 0
429 : ((static_cast<ValueType>(1) << (Parameters::kBits - 2)) -
430 1) * 2 +
431 1) &&
432 Parameters::ValueIsOk(value);
433 }
434
UncheckedWrite(ValueType value)435 void UncheckedWrite(ValueType value) const {
436 buffer_.UncheckedWriteUInt(::emboss::support::MaskToNBits(
437 static_cast<typename BitViewType::ValueType>(value),
438 Parameters::kBits));
439 }
440
441 template <typename OtherView>
CopyFrom(const OtherView & other)442 void CopyFrom(const OtherView &other) const {
443 Write(other.Read());
444 }
445 template <typename OtherView>
UncheckedCopyFrom(const IntView & other)446 void UncheckedCopyFrom(const IntView &other) const {
447 UncheckedWrite(other.UncheckedRead());
448 }
449 template <typename OtherView>
TryToCopyFrom(const OtherView & other)450 bool TryToCopyFrom(const OtherView &other) const {
451 return other.Ok() && TryToWrite(other.Read());
452 }
453
454 // All bit patterns in the underlying buffer are valid, so Ok() is always
455 // true if IsComplete() is true.
Ok()456 bool Ok() const {
457 return IsComplete() && Parameters::ValueIsOk(UncheckedRead());
458 }
459 template <class OtherBitViewType>
Equals(const IntView<Parameters,OtherBitViewType> & other)460 bool Equals(const IntView<Parameters, OtherBitViewType> &other) const {
461 return Read() == other.Read();
462 }
463 template <class OtherBitViewType>
UncheckedEquals(const IntView<Parameters,OtherBitViewType> & other)464 bool UncheckedEquals(
465 const IntView<Parameters, OtherBitViewType> &other) const {
466 return UncheckedRead() == other.UncheckedRead();
467 }
IsComplete()468 bool IsComplete() const {
469 return buffer_.Ok() && buffer_.SizeInBits() >= Parameters::kBits;
470 }
471
472 template <class Stream>
UpdateFromTextStream(Stream * stream)473 bool UpdateFromTextStream(Stream *stream) const {
474 return support::ReadIntegerFromTextStream(this, stream);
475 }
476
477 template <class Stream>
WriteToTextStream(Stream * stream,::emboss::TextOutputOptions & options)478 void WriteToTextStream(Stream *stream,
479 ::emboss::TextOutputOptions &options) const {
480 support::WriteIntegerViewToTextStream(this, stream, options);
481 }
482
IsAggregate()483 static constexpr bool IsAggregate() { return false; }
484
SizeInBits()485 static constexpr int SizeInBits() { return Parameters::kBits; }
486
487 private:
ConvertToSigned(typename BitViewType::ValueType data)488 static ValueType ConvertToSigned(typename BitViewType::ValueType data) {
489 static_assert(sizeof(ValueType) <= sizeof(typename BitViewType::ValueType),
490 "Integer types wider than BitViewType::ValueType are not "
491 "supported.");
492 #if EMBOSS_SYSTEM_IS_TWOS_COMPLEMENT
493 // static_cast from unsigned to signed is implementation-defined when the
494 // value does not fit in the signed type (in this case, when the final value
495 // should be negative). Most implementations use a reasonable definition,
496 // so on most systems we can just cast.
497 //
498 // If the integer does not take up the full width of ValueType, it needs to
499 // be sign-extended until it does. The easiest way to do this is to shift
500 // until the sign bit is in the topmost position, then cast to signed, then
501 // shift back. The shift back will copy the sign bit.
502 return static_cast<ValueType>(
503 data << (sizeof(ValueType) * 8 - Parameters::kBits)) >>
504 (sizeof(ValueType) * 8 - Parameters::kBits);
505 #else
506 // Otherwise, in order to convert without running into
507 // implementation-defined behavior, first mask out the sign bit. This
508 // results in (final result MOD 2 ** (width of int in bits - 1)). That
509 // value can be safely converted to the signed ValueType.
510 //
511 // Finally, if the sign bit was set, subtract (2 ** (width of int in bits -
512 // 2)) twice.
513 //
514 // The 1-bit signed integer case must be handled separately, but it is
515 // (fortunately) quite easy to enumerate both possible values.
516 if (Parameters::kBits == 1) {
517 if (data == 0) {
518 return 0;
519 } else if (data == 1) {
520 return -1;
521 } else {
522 EMBOSS_CHECK(false);
523 return -1; // Return value if EMBOSS_CHECK is disabled.
524 }
525 } else {
526 typename BitViewType::ValueType sign_bit =
527 static_cast<typename BitViewType::ValueType>(1)
528 << (Parameters::kBits - 1);
529 typename BitViewType::ValueType mask = sign_bit - 1;
530 typename BitViewType::ValueType data_mod2_to_n = mask & data;
531 ValueType result_sign_bit =
532 static_cast<ValueType>((data & sign_bit) >> 1);
533 return data_mod2_to_n - result_sign_bit - result_sign_bit;
534 }
535 #endif
536 }
537
538 BitViewType buffer_;
539 };
540
541 // The maximum Binary-Coded Decimal (BCD) value that fits in a particular number
542 // of bits.
543 template <typename ValueType>
MaxBcd(int bits)544 constexpr inline ValueType MaxBcd(int bits) {
545 return bits < 4 ? (1 << bits) - 1
546 : 10 * (MaxBcd<ValueType>(bits - 4) + 1) - 1;
547 }
548
549 template <typename ValueType>
IsBcd(ValueType x)550 inline bool IsBcd(ValueType x) {
551 // Adapted from:
552 // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
553 //
554 // This determines if any nibble has a value greater than 9. It does
555 // this by treating operations on the n-bit value as parallel operations
556 // on n/4 4-bit values.
557 //
558 // The result is computed in the high bit of each nibble: if any of those
559 // bits is set in the end, then at least one nibble had a value in the
560 // range 10-15.
561 //
562 // The first check is subtle: ~x is equivalent to (nibble = 15 - nibble).
563 // Then, 6 is subtracted from each nibble. This operation will underflow
564 // if the original value was more than 9, leaving the high bit of the
565 // nibble set. It will also leave the high bit of the nibble set
566 // (without underflow) if the original value was 0 or 1.
567 //
568 // The second check is just x: the high bit of each nibble in x is set if
569 // that nibble's value is 8-15.
570 //
571 // Thus, the first check leaves the high bit set in any nibble with the
572 // value 0, 1, or 10-15, and the second check leaves the high bit set in
573 // any nibble with the value 8-15. Bitwise-anding these results, high
574 // bits are only set if the original value was 10-15.
575 //
576 // The underflow condition in the first check can screw up the condition
577 // for nibbles in higher positions than the underflowing nibble. This
578 // cannot affect the overall boolean result, because the underflow
579 // condition only happens if a nibble was greater than 9, and therefore
580 // *that* nibble's final value will be nonzero, and therefore the whole
581 // result will be nonzero, no matter what happens in the higher-order
582 // nibbles.
583 //
584 // A couple of examples in 16 bit:
585 //
586 // x = 0x09a8
587 // (~0x09a8 - 0x6666) & 0x09a8 & 0x8888
588 // ( 0xf657 - 0x6666) & 0x09a8 & 0x8888
589 // 0x8ff1 & 0x09a8 & 0x8888
590 // 0x09a0 & 0x8888
591 // 0x0880 Note the underflow into nibble 2
592 //
593 // x = 0x1289
594 // (~0x1289 - 0x6666) & 0x1289 & 0x8888
595 // ( 0xed76 - 0x6666) & 0x1289 & 0x8888
596 // 0x8710 & 0x1289 & 0x8888
597 // 0x0200 & 0x8888
598 // 0x0000
599 static_assert(!::std::is_signed<ValueType>::value,
600 "IsBcd only works on unsigned values.");
601 if (sizeof(ValueType) < sizeof(unsigned)) {
602 // For types with lower integer conversion rank than unsigned int, integer
603 // promotion rules cause many implicit conversions to signed int in the math
604 // below, which makes the math go wrong. Rather than add a dozen explicit
605 // casts back to ValueType, just do the math as 'unsigned'.
606 return IsBcd<unsigned>(x);
607 } else {
608 return ((~x - (~ValueType{0} / 0xf * 0x6 /* 0x6666...6666 */)) & x &
609 (~ValueType{0} / 0xf * 0x8 /* 0x8888...8888 */)) == 0;
610 }
611 }
612
613 // Base template for Binary-Coded Decimal (BCD) unsigned integer readers.
614 template <class Parameters, class BitViewType>
615 class BcdView final {
616 public:
617 using ValueType = typename ::emboss::support::LeastWidthInteger<
618 Parameters::kBits>::Unsigned;
619
620 static_assert(Parameters::kBits <= sizeof(ValueType) * 8,
621 "BcdView requires sizeof(ValueType) * 8 >= Parameters::kBits.");
622
623 template <typename... Args>
BcdView(Args &&...args)624 explicit BcdView(Args &&...args) : buffer_{::std::forward<Args>(args)...} {}
BcdView()625 BcdView() : buffer_() {}
626 BcdView(const BcdView &) = default;
627 BcdView(BcdView &&) = default;
628 BcdView &operator=(const BcdView &) = default;
629 BcdView &operator=(BcdView &&) = default;
630 ~BcdView() = default;
631
Read()632 ValueType Read() const {
633 EMBOSS_CHECK(Ok());
634 return ConvertToBinary(buffer_.ReadUInt());
635 }
UncheckedRead()636 ValueType UncheckedRead() const {
637 return ConvertToBinary(buffer_.UncheckedReadUInt());
638 }
Write(ValueType value)639 void Write(ValueType value) const {
640 const bool result = TryToWrite(value);
641 (void)result;
642 EMBOSS_CHECK(result);
643 }
TryToWrite(ValueType value)644 bool TryToWrite(ValueType value) const {
645 if (!CouldWriteValue(value)) return false;
646 if (!IsComplete()) return false;
647 buffer_.WriteUInt(ConvertToBcd(value));
648 return true;
649 }
CouldWriteValue(ValueType value)650 static constexpr bool CouldWriteValue(ValueType value) {
651 return value <= MaxValue() && Parameters::ValueIsOk(value);
652 }
UncheckedWrite(ValueType value)653 void UncheckedWrite(ValueType value) const {
654 buffer_.UncheckedWriteUInt(ConvertToBcd(value));
655 }
656
657 template <class Stream>
UpdateFromTextStream(Stream * stream)658 bool UpdateFromTextStream(Stream *stream) const {
659 return support::ReadIntegerFromTextStream(this, stream);
660 }
661
662 template <class Stream>
WriteToTextStream(Stream * stream,::emboss::TextOutputOptions & options)663 void WriteToTextStream(Stream *stream,
664 ::emboss::TextOutputOptions &options) const {
665 // TODO(bolms): This shares the numeric_base() option with IntView and
666 // UIntView (and EnumView, for unknown enum values). It seems like an end
667 // user might prefer to see BCD values in decimal, even if they want to see
668 // values of other numeric types in hex or binary. It seems like there
669 // could be some fancy C++ trickery to allow separate options for separate
670 // view types.
671 support::WriteIntegerViewToTextStream(this, stream, options);
672 }
673
IsAggregate()674 static constexpr bool IsAggregate() { return false; }
675
676 template <typename OtherView>
CopyFrom(const OtherView & other)677 void CopyFrom(const OtherView &other) const {
678 Write(other.Read());
679 }
680 template <typename OtherView>
UncheckedCopyFrom(const OtherView & other)681 void UncheckedCopyFrom(const OtherView &other) const {
682 UncheckedWrite(other.UncheckedRead());
683 }
684 template <typename OtherView>
TryToCopyFrom(const OtherView & other)685 bool TryToCopyFrom(const OtherView &other) const {
686 return other.Ok() && TryToWrite(other.Read());
687 }
688
Ok()689 bool Ok() const {
690 if (!IsComplete()) return false;
691 if (!IsBcd(buffer_.ReadUInt())) return false;
692 if (!Parameters::ValueIsOk(UncheckedRead())) return false;
693 return true;
694 }
695 template <class OtherBitViewType>
Equals(const BcdView<Parameters,OtherBitViewType> & other)696 bool Equals(const BcdView<Parameters, OtherBitViewType> &other) const {
697 return Read() == other.Read();
698 }
699 template <class OtherBitViewType>
UncheckedEquals(const BcdView<Parameters,OtherBitViewType> & other)700 bool UncheckedEquals(
701 const BcdView<Parameters, OtherBitViewType> &other) const {
702 return UncheckedRead() == other.UncheckedRead();
703 }
IsComplete()704 bool IsComplete() const {
705 return buffer_.Ok() && buffer_.SizeInBits() >= Parameters::kBits;
706 }
707
SizeInBits()708 static constexpr int SizeInBits() { return Parameters::kBits; }
709
710 private:
ConvertToBinary(ValueType bcd_value)711 static ValueType ConvertToBinary(ValueType bcd_value) {
712 ValueType result = 0;
713 ValueType multiplier = 1;
714 for (int shift = 0; shift < Parameters::kBits; shift += 4) {
715 result += ((bcd_value >> shift) & 0xf) * multiplier;
716 multiplier *= 10;
717 }
718 return result;
719 }
720
ConvertToBcd(ValueType value)721 static ValueType ConvertToBcd(ValueType value) {
722 ValueType bcd_value = 0;
723 for (int shift = 0; shift < Parameters::kBits; shift += 4) {
724 bcd_value |= (value % 10) << shift;
725 value /= 10;
726 }
727 return bcd_value;
728 }
729
MaxValue()730 static constexpr ValueType MaxValue() {
731 return MaxBcd<ValueType>(Parameters::kBits);
732 }
733
734 BitViewType buffer_;
735 };
736
737 // FloatView is the view for the Emboss Float type.
738 template <class Parameters, class BitViewType>
739 class FloatView final {
740 static_assert(Parameters::kBits == 32 || Parameters::kBits == 64,
741 "Only 32- and 64-bit floats are currently supported.");
742
743 public:
744 using ValueType = typename support::FloatType<Parameters::kBits>::Type;
745
746 template <typename... Args>
FloatView(Args &&...args)747 explicit FloatView(Args &&...args) : buffer_{::std::forward<Args>(args)...} {}
FloatView()748 FloatView() : buffer_() {}
749 FloatView(const FloatView &) = default;
750 FloatView(FloatView &&) = default;
751 FloatView &operator=(const FloatView &) = default;
752 FloatView &operator=(FloatView &&) = default;
753 ~FloatView() = default;
754
Read()755 ValueType Read() const { return ConvertToFloat(buffer_.ReadUInt()); }
UncheckedRead()756 ValueType UncheckedRead() const {
757 return ConvertToFloat(buffer_.UncheckedReadUInt());
758 }
Write(ValueType value)759 void Write(ValueType value) const {
760 const bool result = TryToWrite(value);
761 (void)result;
762 EMBOSS_CHECK(result);
763 }
TryToWrite(ValueType value)764 bool TryToWrite(ValueType value) const {
765 if (!CouldWriteValue(value)) return false;
766 if (!IsComplete()) return false;
767 buffer_.WriteUInt(ConvertToUInt(value));
768 return true;
769 }
CouldWriteValue(ValueType value)770 static constexpr bool CouldWriteValue(ValueType value) {
771 // Avoid unused parameters error:
772 static_cast<void>(value);
773 return true;
774 }
UncheckedWrite(ValueType value)775 void UncheckedWrite(ValueType value) const {
776 buffer_.UncheckedWriteUInt(ConvertToUInt(value));
777 }
778
779 template <typename OtherView>
CopyFrom(const OtherView & other)780 void CopyFrom(const OtherView &other) const {
781 Write(other.Read());
782 }
783 template <typename OtherView>
UncheckedCopyFrom(const OtherView & other)784 void UncheckedCopyFrom(const OtherView &other) const {
785 UncheckedWrite(other.UncheckedRead());
786 }
787 template <typename OtherView>
TryToCopyFrom(const OtherView & other)788 bool TryToCopyFrom(const OtherView &other) const {
789 return other.Ok() && TryToWrite(other.Read());
790 }
791
792 // All bit patterns in the underlying buffer are valid, so Ok() is always
793 // true if IsComplete() is true.
Ok()794 bool Ok() const { return IsComplete(); }
795 template <class OtherBitViewType>
Equals(const FloatView<Parameters,OtherBitViewType> & other)796 bool Equals(const FloatView<Parameters, OtherBitViewType> &other) const {
797 return Read() == other.Read();
798 }
799 template <class OtherBitViewType>
UncheckedEquals(const FloatView<Parameters,OtherBitViewType> & other)800 bool UncheckedEquals(
801 const FloatView<Parameters, OtherBitViewType> &other) const {
802 return UncheckedRead() == other.UncheckedRead();
803 }
IsComplete()804 bool IsComplete() const {
805 return buffer_.Ok() && buffer_.SizeInBits() >= Parameters::kBits;
806 }
807
808 template <class Stream>
UpdateFromTextStream(Stream * stream)809 bool UpdateFromTextStream(Stream *stream) const {
810 return support::ReadFloatFromTextStream(this, stream);
811 }
812
813 template <class Stream>
WriteToTextStream(Stream * stream,::emboss::TextOutputOptions & options)814 void WriteToTextStream(Stream *stream,
815 ::emboss::TextOutputOptions &options) const {
816 support::WriteFloatToTextStream(Read(), stream, options);
817 }
818
IsAggregate()819 static constexpr bool IsAggregate() { return false; }
820
SizeInBits()821 static constexpr int SizeInBits() { return Parameters::kBits; }
822
823 private:
824 using UIntType = typename support::FloatType<Parameters::kBits>::UIntType;
ConvertToFloat(UIntType bits)825 static ValueType ConvertToFloat(UIntType bits) {
826 // TODO(bolms): This method assumes a few things that are not always
827 // strictly true; e.g., that uint32_t and float have the same endianness.
828 ValueType result;
829 memcpy(static_cast<void *>(&result), static_cast<void *>(&bits),
830 sizeof result);
831 return result;
832 }
833
ConvertToUInt(ValueType value)834 static UIntType ConvertToUInt(ValueType value) {
835 // TODO(bolms): This method assumes a few things that are not always
836 // strictly true; e.g., that uint32_t and float have the same endianness.
837 UIntType bits;
838 memcpy(static_cast<void *>(&bits), static_cast<void *>(&value),
839 sizeof bits);
840 return bits;
841 }
842
843 BitViewType buffer_;
844 };
845
846 } // namespace prelude
847 } // namespace emboss
848
849 #endif // EMBOSS_RUNTIME_CPP_EMBOSS_PRELUDE_H_
850