| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // View class template for enums. |
| #ifndef EMBOSS_RUNTIME_CPP_EMBOSS_ENUM_VIEW_H_ |
| #define EMBOSS_RUNTIME_CPP_EMBOSS_ENUM_VIEW_H_ |
| |
| #include <cctype> |
| #include <cstdint> |
| #include <string> |
| #include <utility> |
| |
| #include "runtime/cpp/emboss_text_util.h" |
| #include "runtime/cpp/emboss_view_parameters.h" |
| |
| namespace emboss { |
| namespace support { |
| |
| // EnumView is a view for Enums inside of bitfields. |
| template <class Enum, class Parameters, class BitViewType> |
| class EnumView final { |
| public: |
| using ValueType = typename ::std::remove_cv<Enum>::type; |
| static_assert( |
| Parameters::kBits <= sizeof(ValueType) * 8, |
| "EnumView requires sizeof(ValueType) * 8 >= Parameters::kBits."); |
| template <typename... Args> |
| explicit EnumView(Args &&... args) : buffer_{::std::forward<Args>(args)...} {} |
| EnumView() : buffer_() {} |
| EnumView(const EnumView &) = default; |
| EnumView(EnumView &&) = default; |
| EnumView &operator=(const EnumView &) = default; |
| EnumView &operator=(EnumView &&) = default; |
| ~EnumView() = default; |
| |
| // TODO(bolms): Here and in CouldWriteValue(), the static_casts to ValueType |
| // rely on implementation-defined behavior when ValueType is signed. |
| ValueType Read() const { |
| ValueType result = static_cast<ValueType>(buffer_.ReadUInt()); |
| EMBOSS_CHECK(Parameters::ValueIsOk(result)); |
| return result; |
| } |
| ValueType UncheckedRead() const { |
| return static_cast<ValueType>(buffer_.UncheckedReadUInt()); |
| } |
| void Write(ValueType value) const { |
| const bool result = TryToWrite(value); |
| (void)result; |
| EMBOSS_CHECK(result); |
| } |
| bool TryToWrite(ValueType value) const { |
| if (!CouldWriteValue(value)) return false; |
| if (!IsComplete()) return false; |
| buffer_.WriteUInt(static_cast<typename BitViewType::ValueType>(value)); |
| return true; |
| } |
| static constexpr bool CouldWriteValue(ValueType value) { |
| // The value can be written if: |
| // |
| // a) it can fit in BitViewType::ValueType (verified by casting to |
| // BitViewType::ValueType and back, and making sure that the value is |
| // unchanged) |
| // |
| // and either: |
| // |
| // b1) the field size is large enough to hold all values, or |
| // b2) the value is less than 2**(field size in bits) |
| return value == static_cast<ValueType>( |
| static_cast<typename BitViewType::ValueType>(value)) && |
| ((Parameters::kBits == |
| sizeof(typename BitViewType::ValueType) * 8) || |
| (static_cast<typename BitViewType::ValueType>(value) < |
| ((static_cast<typename BitViewType::ValueType>(1) |
| << (Parameters::kBits - 1)) |
| << 1))) && |
| Parameters::ValueIsOk(value); |
| } |
| void UncheckedWrite(ValueType value) const { |
| buffer_.UncheckedWriteUInt( |
| static_cast<typename BitViewType::ValueType>(value)); |
| } |
| |
| template <typename OtherView> |
| void CopyFrom(const OtherView &other) const { |
| Write(other.Read()); |
| } |
| template <typename OtherView> |
| void UncheckedCopyFrom(const OtherView &other) const { |
| UncheckedWrite(other.UncheckedRead()); |
| } |
| template <typename OtherView> |
| bool TryToCopyFrom(const OtherView &other) const { |
| return other.Ok() && TryToWrite(other.Read()); |
| } |
| |
| // All bit patterns in the underlying buffer are valid, so Ok() is always |
| // true if IsComplete() is true. |
| bool Ok() const { |
| return IsComplete() && Parameters::ValueIsOk(UncheckedRead()); |
| } |
| template <class OtherBitViewType> |
| bool Equals(const EnumView<Enum, Parameters, OtherBitViewType> &other) const { |
| return Read() == other.Read(); |
| } |
| template <class OtherBitViewType> |
| bool UncheckedEquals( |
| const EnumView<Enum, Parameters, OtherBitViewType> &other) const { |
| return UncheckedRead() == other.UncheckedRead(); |
| } |
| bool IsComplete() const { |
| return buffer_.Ok() && buffer_.SizeInBits() >= Parameters::kBits; |
| } |
| |
| template <class Stream> |
| bool UpdateFromTextStream(Stream *stream) const { |
| ::std::string token; |
| if (!ReadToken(stream, &token)) return false; |
| if (token.empty()) return false; |
| if (::std::isdigit(token[0])) { |
| ::std::uint64_t value; |
| if (!DecodeInteger(token, &value)) return false; |
| // TODO(bolms): Fix the static_cast<ValueType> for signed ValueType. |
| // TODO(bolms): Should values between 2**63 and 2**64-1 actually be |
| // allowed in the text format when ValueType is signed? |
| return TryToWrite(static_cast<ValueType>(value)); |
| } else if (token[0] == '-') { |
| ::std::int64_t value; |
| if (!DecodeInteger(token, &value)) return false; |
| return TryToWrite(static_cast<ValueType>(value)); |
| } else { |
| ValueType value; |
| if (!TryToGetEnumFromName(token.c_str(), &value)) return false; |
| return TryToWrite(value); |
| } |
| } |
| |
| template <class Stream> |
| void WriteToTextStream(Stream *stream, |
| const TextOutputOptions &options) const { |
| ::emboss::support::WriteEnumViewToTextStream(this, stream, options); |
| } |
| |
| static constexpr int SizeInBits() { return Parameters::kBits; } |
| |
| private: |
| BitViewType buffer_; |
| }; |
| |
| } // namespace support |
| } // namespace emboss |
| |
| #endif // EMBOSS_RUNTIME_CPP_EMBOSS_ENUM_VIEW_H_ |