Skip to content

src/scale/scale_decoder_stream.cpp

Namespaces

Name
sgns
sgns::scale

Source code

#include "scale/scale_decoder_stream.hpp"

#include "base/outcome_throw.hpp"
#include "macro/unreachable.hpp"
#include "scale/scale_error.hpp"
#include "scale/types.hpp"

namespace sgns::scale {
  namespace {
    CompactInteger decodeCompactInteger(ScaleDecoderStream &stream) {
      auto first_byte = stream.nextByte();

      const uint8_t flag = (first_byte)&0b00000011u;

      size_t number = 0u;

      switch (flag) {
        case 0b00u: {
          number = static_cast<size_t>(first_byte >> 2u);
          break;
        }

        case 0b01u: {
          auto second_byte = stream.nextByte();

          number = (static_cast<size_t>((first_byte)&0b11111100u)
                    + static_cast<size_t>(second_byte) * 256u)
                   >> 2u;
          break;
        }

        case 0b10u: {
          number = first_byte;
          size_t multiplier = 256u;
          if (!stream.hasMore(3u)) {
            // not enough data to decode integer
            base::raise(DecodeError::NOT_ENOUGH_DATA);
          }

          for (auto i = 0u; i < 3u; ++i) {
            // we assured that there are 3 more bytes,
            // no need to make checks in a loop
            number += (stream.nextByte()) * multiplier;
            multiplier = multiplier << 8u;
          }
          number = number >> 2u;
          break;
        }

        case 0b11: {
          auto bytes_count = ((first_byte) >> 2u) + 4u;
          if (!stream.hasMore(bytes_count)) {
            // not enough data to decode integer
            base::raise(DecodeError::NOT_ENOUGH_DATA);
          }

          CompactInteger multiplier{1u};
          CompactInteger value = 0;
          // we assured that there are m more bytes,
          // no need to make checks in a loop
          for (auto i = 0u; i < bytes_count; ++i) {
            value += (stream.nextByte()) * multiplier;
            multiplier *= 256u;
          }

          return value;  // special case
        }

        default:
          UNREACHABLE
      }

      return CompactInteger{number};
    }
  }  // namespace

  ScaleDecoderStream::ScaleDecoderStream(gsl::span<const uint8_t> span)
      : span_{span}, current_iterator_{span_.begin()}, current_index_{0} {}

  boost::optional<bool> ScaleDecoderStream::decodeOptionalBool() {
    auto byte = nextByte();
    switch (byte) {
      case static_cast<uint8_t>(OptionalBool::NONE_):
        return boost::none;
        break;
      case static_cast<uint8_t>(OptionalBool::FALSE_):
        return false;
      case static_cast<uint8_t>(OptionalBool::TRUE_):
        return true;
      default:
        base::raise(DecodeError::UNEXPECTED_VALUE);
    }
    UNREACHABLE
  }

  bool ScaleDecoderStream::decodeBool() {
    auto byte = nextByte();
    switch (byte) {
      case 0u:
        return false;
      case 1u:
        return true;
      default:
        base::raise(DecodeError::UNEXPECTED_VALUE);
    }
    UNREACHABLE
  }

  ScaleDecoderStream &ScaleDecoderStream::operator>>(CompactInteger &v) {
    v = decodeCompactInteger(*this);
    return *this;
  }

  ScaleDecoderStream &ScaleDecoderStream::operator>>(std::string &v) {
    std::vector<uint8_t> collection;
    *this >> collection;
    v.clear();
    v.append(collection.begin(), collection.end());
    return *this;
  }

  bool ScaleDecoderStream::hasMore(uint64_t n) const {
    return static_cast<SizeType>(current_index_ + n) <= span_.size();
  }

  uint8_t ScaleDecoderStream::nextByte() {
    // if (! hasMore(1)) {
    if (! hasMore(1)) {
      base::raise(DecodeError::NOT_ENOUGH_DATA);
    }
    ++current_index_;
    return *current_iterator_++;
  }
}  // namespace sgns::scale

Updated on 2026-03-04 at 13:10:45 -0800