Skip to content

rlp/rlp_encoder.hpp

Namespaces

Name
rlp

Classes

Name
class rlp::RlpEncoder

Source code

#ifndef RLP_ENCODER_HPP
#define RLP_ENCODER_HPP

#include <vector>
#include <boost/core/span.hpp>
#include "common.hpp"
#include "intx.hpp"
#include "endian.hpp"
#include <iostream>
namespace rlp {

class RlpEncoder {
   public:
    RlpEncoder() = default;

    // --- Add basic types ---
    EncodingOperationResult add(ByteView bytes) noexcept; // Add raw bytes (encoded as RLP string)
    EncodingOperationResult AddRaw(ByteView bytes) noexcept; // Add raw bytes without header
    // Add unsigned integrals (using SFINAE for C++17)
    template <typename T>
        auto add(const T& n) noexcept -> std::enable_if_t<is_unsigned_integral_v<T>, EncodingOperationResult>;
    EncodingOperationResult add(const intx::uint256& n) noexcept; // Explicit overload for uint256
    EncodingOperationResult BeginList() noexcept;
    EncodingOperationResult EndList() noexcept; // Calculates and inserts the list header

    // --- Convenience for Vectors ---
    // Note: Implementation needs to be here or in .ipp due to template
    template <typename T>
    EncodingOperationResult add(const std::vector<T>& vec) noexcept {
        BOOST_OUTCOME_TRY(BeginList());
        for (const auto& item : vec) {
            BOOST_OUTCOME_TRY(add(item)); // Recursively call add for each element
        }
        return EndList();
    }
     // Convenience for Spans (similar)
    template <typename T>
    EncodingOperationResult add(boost::span<const T> vec_span) noexcept {
        BOOST_OUTCOME_TRY(BeginList());
        for (const auto& item : vec_span) {
            BOOST_OUTCOME_TRY(add(item)); // Recursively call add for each element
        }
        return EndList();
    }

    // --- Fixed-size array support ---
    template <size_t N>
    EncodingOperationResult add(const std::array<uint8_t, N>& arr) noexcept {
        return add(ByteView(arr.data(), arr.size()));
    }

    // --- Output ---
    [[nodiscard]] EncodingResult<const Bytes*> GetBytes() const noexcept;
    [[nodiscard]] EncodingResult<Bytes*> GetBytes() noexcept; // Mutable access for streaming
    [[nodiscard]] EncodingResult<Bytes> MoveBytes() noexcept;
    void clear() noexcept; // Reset the encoder

    // Get current size without copying
    [[nodiscard]] size_t size() const noexcept { return buffer_.size(); }

    // Reserve capacity for better performance
    void reserve(size_t capacity) noexcept { buffer_.reserve(capacity); }

   private:
    Bytes buffer_{};
    std::vector<size_t> list_start_positions_{}; // Stack to track list starts

    // --- Internal Template Implementation for Integrals ---
    // Needs to be in header if add<T> is public template method
    template <typename T>
    auto add_integral(const T& n) noexcept -> std::enable_if_t<is_unsigned_integral_v<T>, EncodingOperationResult>;
};

template <typename T>
inline auto RlpEncoder::add(const T& n) noexcept -> std::enable_if_t<is_unsigned_integral_v<T>, EncodingOperationResult> {
    if constexpr (std::is_same_v<T, bool>) {
        // Handle boolean values
        buffer_.push_back(n ? uint8_t{1} : kEmptyStringCode);
        return outcome::success();
    } else {
        // Handle other unsigned integral types
        return add_integral(n);
    }
}

template <typename T>
inline auto RlpEncoder::add_integral(const T& n) noexcept -> std::enable_if_t<is_unsigned_integral_v<T>, EncodingOperationResult> {
    if ( n == 0 ) {
        buffer_.push_back(kEmptyStringCode);
        return outcome::success();
    }
    if constexpr (sizeof(T) == 1) {
        uint8_t val = static_cast<uint8_t>(n);
        if ( val < kRlpSingleByteThreshold ) {
            buffer_.push_back(val);
        } else {
            // Single byte that is >= 0x80: encode as 1-byte string (prefix = kShortStringOffset + kLongPrefixByteSize)
            buffer_.push_back(static_cast<uint8_t>(kShortStringOffset + kLongPrefixByteSize));
            buffer_.push_back(val);
        }
    } else if constexpr (sizeof(T) == 2) {
        uint16_t val = n;
        if ( val < kRlpSingleByteThreshold ) {
            buffer_.push_back(static_cast<uint8_t>(val));
        } else {
            uint8_t buf[2];
            size_t len = 0;
            if ( val >> 8 ) buf[len++] = static_cast<uint8_t>(val >> 8);
            buf[len++] = static_cast<uint8_t>(val & 0xFF);
            buffer_.push_back(static_cast<uint8_t>(kShortStringOffset + len));
            buffer_.append(buf, len);
        }
    } else if constexpr (sizeof(T) == 4) {
        uint32_t val = n;
        if ( val < kRlpSingleByteThreshold ) {
            buffer_.push_back(static_cast<uint8_t>(val));
        } else {
            uint8_t buf[4];
            size_t len = 0;
            if ( val >> 24 ) buf[len++] = static_cast<uint8_t>(val >> 24);
            if ( val >> 16 ) buf[len++] = static_cast<uint8_t>(val >> 16);
            if ( val >> 8 ) buf[len++] = static_cast<uint8_t>(val >> 8);
            buf[len++] = static_cast<uint8_t>(val & 0xFF);
            buffer_.push_back(static_cast<uint8_t>(kShortStringOffset + len));
            buffer_.append(buf, len);
        }
    } else if constexpr (sizeof(T) == 8) {
        uint64_t val = n;
        if ( val < kRlpSingleByteThreshold ) {
            buffer_.push_back(static_cast<uint8_t>(val));
        } else {
            uint8_t buf[8];
            size_t len = 0;
            if ( val >> 56 ) buf[len++] = static_cast<uint8_t>(val >> 56);
            if ( val >> 48 ) buf[len++] = static_cast<uint8_t>(val >> 48);
            if ( val >> 40 ) buf[len++] = static_cast<uint8_t>(val >> 40);
            if ( val >> 32 ) buf[len++] = static_cast<uint8_t>(val >> 32);
            if ( val >> 24 ) buf[len++] = static_cast<uint8_t>(val >> 24);
            if ( val >> 16 ) buf[len++] = static_cast<uint8_t>(val >> 16);
            if ( val >> 8 ) buf[len++] = static_cast<uint8_t>(val >> 8);
            buf[len++] = static_cast<uint8_t>(val & 0xFF);
            buffer_.push_back(static_cast<uint8_t>(kShortStringOffset + len));
            buffer_.append(buf, len);
        }
    }
    return outcome::success();
}

} // namespace rlp

#endif // RLP_ENCODER_HPP

Updated on 2026-04-13 at 23:22:46 -0700