template <typename T> staticinlinebool DecodeLeb128Helper(const uint8_t** data, const std::optional<constvoid*>& end,
T* out) {
static_assert(sizeof(T) == 8 || sizeof(T) == 4); const uint8_t* ptr = *data;
T result = 0; const size_t num_bits = (sizeof(T) * 8); // We can encode 7-bits per byte in leb128. So max_bytes is ceil(number_of_bits / 7) const size_t max_bytes = (num_bits + 6u) / 7u; for (size_t index = 0; index < max_bytes; ++index) { if (end.has_value() && ptr >= end.value()) { returnfalse;
}
std::make_unsigned_t<T> curr = *(ptr++);
result |= ((curr & 0x7f) << (index * 7)); if (LIKELY(curr <= 0x7f)) { if (std::is_signed_v<T>) { // For signed values we need to sign extend the result. If we are using all the bits then // the result is already sign extended and we don't need to do anything. if (index < max_bytes - 1) { int shift = num_bits - (index + 1) * 7;
result = (result << shift) >> shift;
}
} // End of encoding. break;
}
}
*out = result;
*data = ptr; returntrue;
}
template <typename T = uint32_t> staticinline T DecodeUnsignedLeb128(const uint8_t** data) {
static_assert(!std::is_signed_v<T>);
T value = 0;
DecodeLeb128Helper(data, std::nullopt, &value); return value;
}
// Reads an unsigned LEB128 + 1 value. updating the given pointer to point // just past the end of the read value. This function tolerates // non-zero high-order bits in the fifth encoded byte. // It is possible for this function to return -1. staticinline int32_t DecodeUnsignedLeb128P1(const uint8_t** data) { return DecodeUnsignedLeb128(data) - 1;
}
template <typename T = int32_t> staticinline T DecodeSignedLeb128(const uint8_t** data) {
static_assert(std::is_signed_v<T>);
T value = 0;
DecodeLeb128Helper(data, std::nullopt, &value); return value;
}
// Returns the first byte of a Leb128 value assuming that: // (1) `end_ptr` points to the first byte after the Leb128 value, and // (2) there is another Leb128 value before this one. template <typename T> staticinline T* ReverseSearchUnsignedLeb128(T* end_ptr) {
static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>, "T must be a uint8_t");
T* ptr = end_ptr;
// Move one byte back, check that this is the terminating byte.
ptr--;
DCHECK(IsLeb128Terminator(ptr));
// Keep moving back while the previous byte is not a terminating byte. // Fail after reading five bytes in case there isn't another Leb128 value // before this one. while (!IsLeb128Terminator(ptr - 1)) {
ptr--;
DCHECK_LE(static_cast<ptrdiff_t>(end_ptr - ptr), 5);
}
return ptr;
}
// Returns the number of bytes needed to encode the value in unsigned LEB128. staticinline uint32_t SignedLeb128Size(int64_t data) { // Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign.
uint64_t bits_to_encode = static_cast<uint64_t>(data ^ (data >> 63));
uint32_t num_bits = 1/* we need to encode the sign bit */ + 6 + 64 - CLZ(bits_to_encode | 1U); // See UnsignedLeb128Size for explanation. This is basically num_bits / 7. return (num_bits * 37) >> 8;
}
staticinline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint64_t value) {
uint8_t out = value & 0x7f;
value >>= 7; while (value != 0) {
*dest++ = out | 0x80;
out = value & 0x7f;
value >>= 7;
}
*dest++ = out; return dest;
}
template <typename Vector> staticinlinevoid EncodeUnsignedLeb128(Vector* dest, uint64_t value) {
static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
uint8_t out = value & 0x7f;
value >>= 7; while (value != 0) {
dest->push_back(out | 0x80);
out = value & 0x7f;
value >>= 7;
}
dest->push_back(out);
}
// Overwrite encoded Leb128 with a new value. The new value must be less than // or equal to the old value to ensure that it fits the allocated space. staticinlinevoid UpdateUnsignedLeb128(uint8_t* dest, uint32_t value) { const uint8_t* old_end = dest;
uint32_t old_value = DecodeUnsignedLeb128(&old_end);
DCHECK_LE(UnsignedLeb128Size(value), UnsignedLeb128Size(old_value)); for (uint8_t* end = EncodeUnsignedLeb128(dest, value); end < old_end; end++) { // Use longer encoding than necessary to fill the allocated space.
end[-1] |= 0x80;
end[0] = 0;
}
}
staticinline uint8_t* EncodeSignedLeb128(uint8_t* dest, int64_t value) {
uint64_t extra_bits = static_cast<uint64_t>(value ^ (value >> 63)) >> 6;
uint8_t out = value & 0x7f; while (extra_bits != 0u) {
*dest++ = out | 0x80;
value >>= 7;
out = value & 0x7f;
extra_bits >>= 7;
}
*dest++ = out; return dest;
}
staticinlinevoid EncodeSignedLeb128(std::vector<uint8_t>* dest, int64_t value) {
uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
uint8_t out = value & 0x7f; while (extra_bits != 0u) {
dest->push_back(out | 0x80);
value >>= 7;
out = value & 0x7f;
extra_bits >>= 7;
}
dest->push_back(out);
}
// An encoder that pushes int32_t/uint32_t data onto the given std::vector. template <typename Vector = std::vector<uint8_t>> class Leb128Encoder {
static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format. template <typename Vector = std::vector<uint8_t>> class Leb128EncodingVector final : private Vector, public Leb128Encoder<Vector> {
static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.