mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
163 lines
8.4 KiB
C++
163 lines
8.4 KiB
C++
// Copyright (c) 2018-2020, The Loki Project
|
|
// Copyright (c) 2014-2019, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
|
|
|
#pragma once
|
|
|
|
#include <type_traits>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <cassert>
|
|
|
|
/*! \file varint.h
|
|
* \brief provides the implementation of varint's
|
|
*
|
|
* Variable length integers ("varints") are serialized in little-endian order using 7 bits per byte
|
|
* where the most significant bit of each byte is a continuation bit (1 = more bytes coming, 0 =
|
|
* varint done) and the lower 7 bits are the next 7 significant bits. The varint sequence ends when
|
|
* there are no more significant bits to encode.
|
|
*
|
|
* Examples (with bit position drawings):
|
|
*
|
|
* value binary [' @ 7-bits] encoded bytes encoded binary
|
|
* ===== =================== ============= ==============
|
|
* 0x64 0'1100100 \x64 0110'0100
|
|
* └┴┴┼┴┴┘ done┛└┴┴┬┴┴┴┘
|
|
* ╰──────────────────────────────╯
|
|
*
|
|
* 0x80 0000001'0000000 \x80\x01 1000'0000 0000'0001
|
|
* └┴┴┼┴┴┘ └┴┴┼┴┴┘ continue┛└┴┴┬┴┴┴┘ ┃└┴┴┬┴┴┴┘
|
|
* │ ╰──────────────────────────────╯ done │
|
|
* ╰─────────────────────────────────────────────────╯
|
|
*
|
|
* 0xcc 0000001'1001100 \xcc\x01 1100'1100 0000'0001
|
|
* └┴┴┼┴┴┘ └┴┴┼┴┴┘ continue┛└┴┴┬┴┴┴┘ ┃└┴┴┬┴┴┴┘
|
|
* │ ╰──────────────────────────────╯ done │
|
|
* ╰─────────────────────────────────────────────────╯
|
|
*
|
|
* 0xbf04 0000010'1111110'0000100 \x84\xfe\x02 1000'0100 1111'1110 0000'0010)
|
|
* └┴┴┼┴┴┘ └┴┴┼┴┴┘ └┴┴┼┴┴┘ continue┛└┴┴┬┴┴┴┘ ┃└┴┴┬┴┴┴┘ ┃└┴┴┬┴┴┴┘
|
|
* │ │ ╰──────────────────────────────╯ continue │ done │
|
|
* │ ╰─────────────────────────────────────────────────╯ │
|
|
* ╰────────────────────────────────────────────────────────────────────╯
|
|
*/
|
|
|
|
namespace tools {
|
|
|
|
/// Returned by read_varint in case of overflow
|
|
constexpr int EVARINT_OVERFLOW = -1;
|
|
/// Returned by read_varint in case of malformed varint representation
|
|
constexpr int EVARINT_REPRESENT = -2;
|
|
/// Returns by read_varint if the input range ends before we finish reading a value
|
|
constexpr int EVARINT_TRUNCATED = -3;
|
|
|
|
// Maximum number of bytes of a varint-encoded integer of the given type.
|
|
template <typename T>
|
|
constexpr size_t VARINT_MAX_LENGTH = (sizeof(T) * 8 + 6) / 7; // i.e. integer ceil(bits / 7)
|
|
|
|
/*! \brief writes a varint to an output iterator. Only supports unsigned integer types. You
|
|
* could static cast a signed type to an unsigned type, but note that any actual negative values
|
|
* (which have a high bit set) will always end up full size since the high bit is set.
|
|
*/
|
|
template <typename OutputIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
|
void write_varint(OutputIt&& it, T i) {
|
|
#ifndef NDEBUG
|
|
size_t count = 0;
|
|
#endif
|
|
while (i > 0b0111'1111) {
|
|
// Write the lower 7 bits with the 8th continuation bit set:
|
|
assert(++count < VARINT_MAX_LENGTH<T>);
|
|
*it++ = static_cast<char>((i & 0b0111'1111) | 0b1000'0000);
|
|
i >>= 7;
|
|
}
|
|
// Last byte (7 bits or less, no continuation bit)
|
|
assert(++count <= VARINT_MAX_LENGTH<T>);
|
|
*it++ = static_cast<char>(i);
|
|
}
|
|
|
|
/*! \brief Returns the string that represents the varint
|
|
*/
|
|
template<typename T>
|
|
std::string get_varint_data(const T& v)
|
|
{
|
|
std::string result;
|
|
write_varint(std::back_insert_iterator{result}, v);
|
|
return result;
|
|
}
|
|
|
|
/*! \brief reads in the varint that is pointed to by InputIt into write. `it` will modified to
|
|
* the position after the varint bytes that we read. Returns the number of bytes read on success,
|
|
* or one of the negative EVARIANT_* constants on failure.
|
|
*/
|
|
template <typename It, typename EndIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
|
int read_varint(It&& it, const EndIt& end, T& write) {
|
|
constexpr size_t bits = sizeof(T) * 8;
|
|
|
|
bool more = true;
|
|
int read = 0;
|
|
write = 0;
|
|
for (size_t shift = 0; more && it != end; shift += 7)
|
|
{
|
|
auto byte = static_cast<unsigned char>(*it++);
|
|
++read;
|
|
|
|
// If byte is all 0s and this isn't the first byte then something is wrong: we have a final
|
|
// byte containing nothing significant, but we should never have produced that.
|
|
if (byte == 0 && shift)
|
|
return EVARINT_REPRESENT;
|
|
|
|
// If we have <= 7 bits of space remaining then the value must fit and must not have a continuation bit
|
|
if (size_t bits_avail = bits - shift; bits_avail <= 7 && byte >= 1 << bits_avail)
|
|
return EVARINT_OVERFLOW;
|
|
|
|
more = byte & 0b1000'0000; // continuation bit
|
|
|
|
write |= static_cast<T>(byte & 0b0111'1111) << shift; // 7-bit value
|
|
}
|
|
|
|
return more ? EVARINT_TRUNCATED : read;
|
|
}
|
|
|
|
// Overloads to allow {read,write}_varint to be called with const iterators
|
|
template <typename OutputIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
|
auto write_varint(const OutputIt& it_, T i) { return write_varint(OutputIt{it_}, i); }
|
|
|
|
template <typename It, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
|
auto read_varint(const It& it_, const It& end, T& i) { return read_varint(It{it_}, end, i); }
|
|
|
|
|
|
/*! \brief reads the varint from an encoded string into `write`. Returns the number of bytes
|
|
* consumed, or an error value (as above).
|
|
*/
|
|
template <typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
|
int read_varint(std::string_view s, T& write) {
|
|
return read_varint(s.begin(), s.end(), write);
|
|
}
|
|
}
|