Program Listing for File varint.h
↰ Return to documentation for file (liberate/serialization/varint.h
)
/*
* This file is part of liberate.
*
* Author(s): Jens Finkhaeuser <jens@finkhaeuser.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 <liberate.h>
#include <cstring>
#include <type_traits>
#include <limits>
#include <liberate/types/type_traits.h>
#include <liberate/types/varint.h>
namespace liberate::serialization {
namespace detail {
constexpr std::size_t ceil(double input)
{
return (static_cast<double>(static_cast<std::size_t>(input)) == input)
? static_cast<std::size_t>(input)
: static_cast<std::size_t>(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<varint_base>::type;
constexpr uint8_t mask{0x7f};
constexpr uint8_t sign{0x40};
constexpr varint_base sign_clamp = static_cast<varint_base>(~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<varint_base>(value);
do {
uint8_t bits = static_cast<uint8_t>(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<varint_base>(value);
do {
input >>= 7;
++offset;
} while (input != 0);
return offset;
}
template <
typename outT,
std::enable_if_t<liberate::types::is_8bit_type<outT>::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<varint_base>::type;
using unsigned_output = typename std::make_unsigned<outT>::type;
constexpr unsigned_output more_bit{0x80};
constexpr unsigned_output mask{0x7f};
constexpr unsigned_output sign{0x40};
constexpr varint_base sign_clamp = static_cast<varint_base>(~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<varint_base>(value);
do {
if (offset >= output_length) {
// Ran out of room
return 0;
}
unsigned_output bits = static_cast<unsigned_output>(input & mask);
input >>= 7;
if (negative) {
input |= sign_clamp;
}
if ((input == 0 && (!(bits & sign)))
|| ((input == -1) && (bits & sign)))
{
output[offset] = static_cast<outT>(bits);
break;
}
output[offset] = static_cast<outT>(bits | more_bit);
++offset;
} while (true);
return offset + 1;
}
template <
typename outT,
std::enable_if_t<liberate::types::is_8bit_type<outT>::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<liberate::types::is_8bit_type<outT>::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<outT>::type;
constexpr unsigned_output more_bit{0x80};
constexpr unsigned_output mask{0x7f};
size_t offset = 0;
auto input = static_cast<varint_base>(value);
do {
if (offset >= output_length) {
// Ran out of room
return 0;
}
unsigned_output bits = static_cast<unsigned_output>(input & mask);
input >>= 7;
if (input != 0) {
bits |= more_bit;
}
output[offset] = static_cast<outT>(bits);
++offset;
} while (input != 0);
return offset;
}
template <
typename inT,
std::enable_if_t<liberate::types::is_8bit_type<inT>::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<varint_base>::type;
using unsigned_input = typename std::make_unsigned<inT>::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<unsigned_input>(input[offset++]);
val |= static_cast<varint_base>(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<liberate::types::varint>(val);
return offset;
}
template <
typename inT,
std::enable_if_t<liberate::types::is_8bit_type<inT>::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<liberate::types::is_8bit_type<inT>::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<varint_base>::type;
using unsigned_input = typename std::make_unsigned<inT>::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<unsigned_base>(byte & mask) << shift;
++offset;
if (!(byte & more_bit)) {
break;
}
shift += 7;
}
value = static_cast<varint>(result);
return offset;
}
} // namespace liberate::serialization
#endif // guard