xref: /aosp_15_r20/external/emboss/runtime/cpp/emboss_prelude.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 // 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