.. _program_listing_file_liberate_serialization_varint.h: Program Listing for File varint.h ================================= |exhale_lsh| :ref:`Return to documentation for file ` (``liberate/serialization/varint.h``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* * This file is part of liberate. * * Author(s): Jens Finkhaeuser * * Copyright (c) 2020-2021 Jens Finkhaeuser. * Copyright (c) 2022 Interpeer gUG (haftungsbeschränkt) * * SPDX-License-Identifier: GPL-3.0-only * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LIBERATE_SERIALIZATION_VARINT_H #define LIBERATE_SERIALIZATION_VARINT_H #ifndef __cplusplus #error You are trying to include a C++ only header file #endif #include #include #include #include #include #include namespace liberate::serialization { namespace detail { constexpr std::size_t ceil(double input) { return (static_cast(static_cast(input)) == input) ? static_cast(input) : static_cast(input) + ((input > 0) ? 1 : 0); } } // namespace detail constexpr std::size_t const VARINT_MAX_BUFSIZE = detail::ceil( double{sizeof(liberate::types::varint) * 8} / 7 ); inline std::size_t sleb128_serialized_size(::liberate::types::varint const & value) { using namespace liberate::types; using unsigned_base = std::make_unsigned::type; constexpr uint8_t mask{0x7f}; constexpr uint8_t sign{0x40}; constexpr varint_base sign_clamp = static_cast(~unsigned_base{0} << 57); bool negative = (value < 0); // We start with the least significant 7 bits, and consume buffer until it // runs out. size_t offset = 0; auto input = static_cast(value); do { uint8_t bits = static_cast(input & mask); input >>= 7; if (negative) { input |= sign_clamp; } if ((input == 0 && (!(bits & sign))) || ((input == -1) && (bits & sign))) { break; } ++offset; } while (true); return offset + 1; } inline std::size_t serialized_size(::liberate::types::varint const & value) { return sleb128_serialized_size(value); } inline std::size_t uleb128_serialized_size(::liberate::types::varint const & value) { using namespace liberate::types; size_t offset = 0; auto input = static_cast(value); do { input >>= 7; ++offset; } while (input != 0); return offset; } template < typename outT, std::enable_if_t::value, int> = 0 > std::size_t sleb128_serialize_varint(outT * output, std::size_t output_length, ::liberate::types::varint const & value) { if (!output || !output_length) { return 0; } using namespace liberate::types; using unsigned_base = std::make_unsigned::type; using unsigned_output = typename std::make_unsigned::type; constexpr unsigned_output more_bit{0x80}; constexpr unsigned_output mask{0x7f}; constexpr unsigned_output sign{0x40}; constexpr varint_base sign_clamp = static_cast(~unsigned_base{0} << 57); bool negative = (value < 0); // We start with the least significant 7 bits, and consume buffer until it // runs out. size_t offset = 0; auto input = static_cast(value); do { if (offset >= output_length) { // Ran out of room return 0; } unsigned_output bits = static_cast(input & mask); input >>= 7; if (negative) { input |= sign_clamp; } if ((input == 0 && (!(bits & sign))) || ((input == -1) && (bits & sign))) { output[offset] = static_cast(bits); break; } output[offset] = static_cast(bits | more_bit); ++offset; } while (true); return offset + 1; } template < typename outT, std::enable_if_t::value, int> = 0 > std::size_t serialize_varint(outT * output, std::size_t output_length, ::liberate::types::varint const & value) { return sleb128_serialize_varint(output, output_length, value); } template < typename outT, std::enable_if_t::value, int> = 0 > std::size_t uleb128_serialize_varint(outT * output, std::size_t output_length, ::liberate::types::varint const & value) { if (!output || !output_length) { return 0; } using namespace liberate::types; using unsigned_output = typename std::make_unsigned::type; constexpr unsigned_output more_bit{0x80}; constexpr unsigned_output mask{0x7f}; size_t offset = 0; auto input = static_cast(value); do { if (offset >= output_length) { // Ran out of room return 0; } unsigned_output bits = static_cast(input & mask); input >>= 7; if (input != 0) { bits |= more_bit; } output[offset] = static_cast(bits); ++offset; } while (input != 0); return offset; } template < typename inT, std::enable_if_t::value, int> = 0 > std::size_t sleb128_deserialize_varint(::liberate::types::varint & value, inT const * input, std::size_t input_length) { using varint_base = liberate::types::varint_base; if (!input || !input_length) { return 0; } using namespace liberate::types; using unsigned_base = std::make_unsigned::type; using unsigned_input = typename std::make_unsigned::type; constexpr unsigned_input more_bit{0x80}; constexpr unsigned_input mask{0x7f}; constexpr unsigned_input sign{0x40}; varint_base val = 0; size_t shift = 0; size_t offset = 0; do { if (offset >= input_length) { // Ran out of data return 0; } unsigned_input tmp = static_cast(input[offset++]); val |= static_cast(tmp & mask) << shift; shift += 7; if (!(tmp & more_bit)) { if ((shift < 64) && (tmp & sign)) { val |= ~unsigned_base{0} << shift; } break; } } while (true); value = static_cast(val); return offset; } template < typename inT, std::enable_if_t::value, int> = 0 > std::size_t deserialize_varint(::liberate::types::varint & value, inT const * input, std::size_t input_length) { return sleb128_deserialize_varint(value, input, input_length); } template < typename inT, std::enable_if_t::value, int> = 0 > std::size_t uleb128_deserialize_varint(::liberate::types::varint & value, inT const * input, std::size_t input_length) { using varint_base = liberate::types::varint_base; if (!input || !input_length) { return 0; } using namespace liberate::types; using unsigned_base = std::make_unsigned::type; using unsigned_input = typename std::make_unsigned::type; constexpr unsigned_input more_bit{0x80}; constexpr unsigned_input mask{0x7f}; unsigned_base result = 0; size_t shift = 0; size_t offset = 0; while (true) { if (offset >= input_length) { return 0; } auto byte = input[offset]; result |= static_cast(byte & mask) << shift; ++offset; if (!(byte & more_bit)) { break; } shift += 7; } value = static_cast(result); return offset; } } // namespace liberate::serialization #endif // guard