357 lines
8.7 KiB
C++
357 lines
8.7 KiB
C++
/*
|
|
* Copyright (c) 2023, Alibaba Group Holding Limited;
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <ostream>
|
|
#include <system_error>
|
|
#include <type_traits>
|
|
|
|
#include "endian_wrapper.hpp"
|
|
#include "reflection.hpp"
|
|
namespace struct_pack {
|
|
|
|
namespace detail {
|
|
|
|
constexpr inline bool is_enable_fast_varint_coding(uint64_t tag) {
|
|
return tag & struct_pack::USE_FAST_VARINT;
|
|
}
|
|
|
|
template <std::size_t bytes_width>
|
|
struct int_t;
|
|
|
|
template <>
|
|
struct int_t<1> {
|
|
using type = int8_t;
|
|
};
|
|
|
|
template <>
|
|
struct int_t<2> {
|
|
using type = int16_t;
|
|
};
|
|
|
|
template <>
|
|
struct int_t<4> {
|
|
using type = int32_t;
|
|
};
|
|
|
|
template <>
|
|
struct int_t<8> {
|
|
using type = int64_t;
|
|
};
|
|
|
|
template <std::size_t bytes_width>
|
|
struct uint_t;
|
|
|
|
template <>
|
|
struct uint_t<1> {
|
|
using type = uint8_t;
|
|
};
|
|
|
|
template <>
|
|
struct uint_t<2> {
|
|
using type = uint16_t;
|
|
};
|
|
|
|
template <>
|
|
struct uint_t<4> {
|
|
using type = uint32_t;
|
|
};
|
|
|
|
template <>
|
|
struct uint_t<8> {
|
|
using type = uint64_t;
|
|
};
|
|
|
|
template <typename T>
|
|
class varint {
|
|
public:
|
|
using value_type = T;
|
|
varint() noexcept = default;
|
|
varint(T t) noexcept : val(t) {}
|
|
[[nodiscard]] operator T() const noexcept { return val; }
|
|
auto& operator=(T t) noexcept {
|
|
val = t;
|
|
return *this;
|
|
}
|
|
[[nodiscard]] auto operator<(const varint& o) const noexcept {
|
|
return val < o.val;
|
|
}
|
|
[[nodiscard]] auto operator<=(const varint& o) const noexcept {
|
|
return val <= o.val;
|
|
}
|
|
[[nodiscard]] auto operator>(const varint& o) const noexcept {
|
|
return val > o.val;
|
|
}
|
|
[[nodiscard]] auto operator>=(const varint& o) const noexcept {
|
|
return val >= o.val;
|
|
}
|
|
[[nodiscard]] auto operator!=(const varint& o) const noexcept {
|
|
return val != o.val;
|
|
}
|
|
[[nodiscard]] bool operator==(const varint<T>& t) const noexcept {
|
|
return val == t.val;
|
|
}
|
|
[[nodiscard]] bool operator==(T t) const noexcept { return val == t; }
|
|
[[nodiscard]] auto operator&(uint8_t mask) const noexcept {
|
|
T new_val = val & mask;
|
|
return varint(new_val);
|
|
}
|
|
template <typename U, typename = std::enable_if_t<std::is_unsigned_v<U>>>
|
|
[[nodiscard]] auto operator<<(U shift) const noexcept {
|
|
T new_val = val << shift;
|
|
return varint(new_val);
|
|
}
|
|
template <typename U>
|
|
[[nodiscard]] auto operator|=(U shift) noexcept {
|
|
if constexpr (std::is_same_v<U, varint<T>>) {
|
|
val |= shift.val;
|
|
}
|
|
else {
|
|
val |= shift;
|
|
}
|
|
return *this;
|
|
}
|
|
friend std::ostream& operator<<(std::ostream& os, const varint& varint) {
|
|
os << varint.val;
|
|
return os;
|
|
}
|
|
|
|
const T& get() const noexcept { return val; }
|
|
T& get() noexcept { return val; }
|
|
|
|
private:
|
|
T val;
|
|
};
|
|
|
|
template <typename T>
|
|
class sint {
|
|
public:
|
|
using value_type = T;
|
|
sint() noexcept = default;
|
|
sint(T t) noexcept : val(t) {}
|
|
[[nodiscard]] operator T() const noexcept { return val; }
|
|
auto& operator=(T t) noexcept {
|
|
val = t;
|
|
return *this;
|
|
}
|
|
[[nodiscard]] auto operator<(const sint& o) const noexcept {
|
|
return val < o.val;
|
|
}
|
|
[[nodiscard]] auto operator<=(const sint& o) const noexcept {
|
|
return val <= o.val;
|
|
}
|
|
[[nodiscard]] auto operator>(const sint& o) const noexcept {
|
|
return val > o.val;
|
|
}
|
|
[[nodiscard]] auto operator>=(const sint& o) const noexcept {
|
|
return val >= o.val;
|
|
}
|
|
[[nodiscard]] auto operator!=(const sint& o) const noexcept {
|
|
return val != o.val;
|
|
}
|
|
[[nodiscard]] bool operator==(T t) const noexcept { return val == t; }
|
|
[[nodiscard]] bool operator==(const sint& t) const noexcept {
|
|
return val == t.val;
|
|
}
|
|
[[nodiscard]] auto operator&(uint8_t mask) const noexcept {
|
|
T new_val = val & mask;
|
|
return sint(new_val);
|
|
}
|
|
template <typename U, typename = std::enable_if_t<std::is_unsigned_v<U>>>
|
|
auto operator<<(U shift) const noexcept {
|
|
T new_val = val << shift;
|
|
return sint(new_val);
|
|
}
|
|
const T& get() const noexcept { return val; }
|
|
T& get() noexcept { return val; }
|
|
friend std::ostream& operator<<(std::ostream& os, const sint& t) {
|
|
os << t.val;
|
|
return os;
|
|
}
|
|
|
|
private:
|
|
T val;
|
|
};
|
|
|
|
template <typename T, typename U>
|
|
[[nodiscard]] STRUCT_PACK_INLINE T decode_zigzag(U u) {
|
|
return static_cast<T>((u >> 1U)) ^ static_cast<U>(-static_cast<T>(u & 1U));
|
|
}
|
|
|
|
template <typename U, typename T, unsigned Shift>
|
|
[[nodiscard]] STRUCT_PACK_INLINE U encode_zigzag(T t) {
|
|
return (static_cast<U>(t) << 1U) ^
|
|
static_cast<U>(-static_cast<T>(static_cast<U>(t) >> Shift));
|
|
}
|
|
template <typename T>
|
|
[[nodiscard]] STRUCT_PACK_INLINE auto encode_zigzag(T t) {
|
|
if constexpr (std::is_same_v<T, int32_t>) {
|
|
return encode_zigzag<uint32_t, int32_t, 31U>(t);
|
|
}
|
|
else if constexpr (std::is_same_v<T, int64_t>) {
|
|
return encode_zigzag<uint64_t, int64_t, 63U>(t);
|
|
}
|
|
else {
|
|
static_assert(!sizeof(T), "error zigzag type");
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
[[nodiscard]] STRUCT_PACK_INLINE constexpr int calculate_varint_size(T t) {
|
|
if constexpr (!varint_t<T>) {
|
|
int ret = 0;
|
|
if constexpr (std::is_unsigned_v<T>) {
|
|
do {
|
|
ret++;
|
|
t >>= 7;
|
|
} while (t != 0);
|
|
}
|
|
else {
|
|
uint64_t temp = t;
|
|
do {
|
|
ret++;
|
|
temp >>= 7;
|
|
} while (temp != 0);
|
|
}
|
|
return ret;
|
|
}
|
|
else if constexpr (varintable_t<T>) {
|
|
return calculate_varint_size(t.get());
|
|
}
|
|
else if constexpr (sintable_t<T>) {
|
|
return calculate_varint_size(encode_zigzag(t.get()));
|
|
}
|
|
else {
|
|
static_assert(!sizeof(T), "error type");
|
|
}
|
|
}
|
|
|
|
template <
|
|
#if __cpp_concepts >= 201907L
|
|
writer_t writer,
|
|
#else
|
|
typename writer,
|
|
#endif
|
|
typename T>
|
|
STRUCT_PACK_INLINE void serialize_varint(writer& writer_, const T& t) {
|
|
#if __cpp_concepts < 201907L
|
|
static_assert(writer_t<writer>, "The writer type must satisfy requirements!");
|
|
#endif
|
|
uint64_t v;
|
|
if constexpr (sintable_t<T>) {
|
|
v = encode_zigzag(get_varint_value(t));
|
|
}
|
|
else {
|
|
v = t;
|
|
}
|
|
while (v >= 0x80) {
|
|
uint8_t temp = v | 0x80u;
|
|
write_wrapper<sizeof(char)>(writer_, (char*)&temp);
|
|
v >>= 7;
|
|
}
|
|
if constexpr (is_system_little_endian) {
|
|
write_wrapper<sizeof(char)>(writer_, (char*)&v);
|
|
}
|
|
else {
|
|
uint8_t tmp = v;
|
|
write_wrapper<sizeof(char)>(writer_, (char*)&tmp);
|
|
}
|
|
}
|
|
#if __cpp_concepts >= 201907L
|
|
template <reader_t Reader>
|
|
#else
|
|
template <typename Reader>
|
|
#endif
|
|
[[nodiscard]] STRUCT_PACK_INLINE struct_pack::err_code deserialize_varint_impl(
|
|
Reader& reader, uint64_t& v) {
|
|
#if __cpp_concepts < 201907L
|
|
static_assert(reader_t<Reader>, "The writer type must satisfy requirements!");
|
|
#endif
|
|
uint8_t now;
|
|
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
|
|
for (int8_t i = 0; i < max_varint_length; ++i) {
|
|
if SP_UNLIKELY (!reader.read((char*)&now, sizeof(char))) {
|
|
return struct_pack::errc::no_buffer_space;
|
|
}
|
|
v |= (1ull * (now & 0x7fu)) << (i * 7);
|
|
if ((now & 0x80U) == 0) {
|
|
return {};
|
|
}
|
|
}
|
|
return struct_pack::errc::invalid_buffer;
|
|
}
|
|
template <bool NotSkip = true,
|
|
#if __cpp_concepts >= 201907L
|
|
reader_t Reader,
|
|
#else
|
|
typename Reader,
|
|
#endif
|
|
typename T>
|
|
[[nodiscard]] STRUCT_PACK_INLINE struct_pack::err_code deserialize_varint(
|
|
Reader& reader, T& t) {
|
|
#if __cpp_concepts < 201907L
|
|
static_assert(reader_t<Reader>, "The writer type must satisfy requirements!");
|
|
#endif
|
|
uint64_t v = 0;
|
|
auto ec = deserialize_varint_impl(reader, v);
|
|
if constexpr (NotSkip) {
|
|
if SP_LIKELY (!ec) {
|
|
if constexpr (sintable_t<T>) {
|
|
t = decode_zigzag<int64_t>(v);
|
|
}
|
|
else if constexpr (std::is_enum_v<T>) {
|
|
t = static_cast<T>(v);
|
|
}
|
|
else {
|
|
t = v;
|
|
}
|
|
}
|
|
}
|
|
return ec;
|
|
// Variable-width integers, or varints,
|
|
// are at the core of the wire format.
|
|
// They allow encoding unsigned 64-bit integers using anywhere
|
|
// between one and ten bytes, with small values using fewer bytes.
|
|
// return decode_varint_v1(f);
|
|
}
|
|
|
|
template <typename T>
|
|
const auto& get_varint_value(const T& v) {
|
|
if constexpr (varint_t<T>) {
|
|
return v.get();
|
|
}
|
|
else {
|
|
return v;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
auto& get_varint_value(T& v) {
|
|
if constexpr (varint_t<T>) {
|
|
return v.get();
|
|
}
|
|
else {
|
|
return v;
|
|
}
|
|
}
|
|
|
|
} // namespace detail
|
|
using var_int32_t = detail::sint<int32_t>;
|
|
using var_int64_t = detail::sint<int64_t>;
|
|
using var_uint32_t = detail::varint<uint32_t>;
|
|
using var_uint64_t = detail::varint<uint64_t>;
|
|
} // namespace struct_pack
|