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