You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
4.6 KiB
163 lines
4.6 KiB
// Copyright 2020 The Pigweed Authors
|
|
//
|
|
// 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
|
|
//
|
|
// https://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.
|
|
|
|
#include "pw_varint/varint.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace pw {
|
|
namespace varint {
|
|
namespace {
|
|
|
|
inline bool ZeroTerminated(pw_varint_Format format) {
|
|
return (static_cast<unsigned>(format) & 0b10) == 0;
|
|
}
|
|
|
|
inline bool LeastSignificant(pw_varint_Format format) {
|
|
return (static_cast<unsigned>(format) & 0b01) == 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" size_t pw_varint_EncodeCustom(uint64_t input,
|
|
void* output,
|
|
size_t output_size,
|
|
pw_varint_Format format) {
|
|
size_t written = 0;
|
|
std::byte* buffer = static_cast<std::byte*>(output);
|
|
|
|
int value_shift = LeastSignificant(format) ? 1 : 0;
|
|
int term_shift = value_shift == 1 ? 0 : 7;
|
|
|
|
std::byte cont, term;
|
|
if (ZeroTerminated(format)) {
|
|
cont = std::byte(0x01) << term_shift;
|
|
term = std::byte(0x00) << term_shift;
|
|
} else {
|
|
cont = std::byte(0x00) << term_shift;
|
|
term = std::byte(0x01) << term_shift;
|
|
}
|
|
|
|
do {
|
|
if (written >= output_size) {
|
|
return 0;
|
|
}
|
|
|
|
bool last_byte = (input >> 7) == 0u;
|
|
|
|
// Grab 7 bits and set the eighth according to the continuation bit.
|
|
std::byte value = (static_cast<std::byte>(input) & std::byte(0x7f))
|
|
<< value_shift;
|
|
|
|
if (last_byte) {
|
|
value |= term;
|
|
} else {
|
|
value |= cont;
|
|
}
|
|
|
|
buffer[written++] = value;
|
|
input >>= 7;
|
|
} while (input != 0u);
|
|
|
|
return written;
|
|
}
|
|
|
|
extern "C" size_t pw_varint_DecodeCustom(const void* input,
|
|
size_t input_size,
|
|
uint64_t* output,
|
|
pw_varint_Format format) {
|
|
uint64_t decoded_value = 0;
|
|
uint_fast8_t count = 0;
|
|
const std::byte* buffer = static_cast<const std::byte*>(input);
|
|
|
|
// The largest 64-bit ints require 10 B.
|
|
const size_t max_count = std::min(kMaxVarint64SizeBytes, input_size);
|
|
|
|
std::byte mask;
|
|
uint32_t shift;
|
|
if (LeastSignificant(format)) {
|
|
mask = std::byte(0xfe);
|
|
shift = 1;
|
|
} else {
|
|
mask = std::byte(0x7f);
|
|
shift = 0;
|
|
}
|
|
|
|
// Determines whether a byte is the last byte of a varint.
|
|
auto is_last_byte = [&](std::byte byte) {
|
|
if (ZeroTerminated(format)) {
|
|
return (byte & ~mask) == std::byte(0);
|
|
}
|
|
return (byte & ~mask) != std::byte(0);
|
|
};
|
|
|
|
while (true) {
|
|
if (count >= max_count) {
|
|
return 0;
|
|
}
|
|
|
|
// Add the bottom seven bits of the next byte to the result.
|
|
decoded_value |= static_cast<uint64_t>((buffer[count] & mask) >> shift)
|
|
<< (7 * count);
|
|
|
|
// Stop decoding if the end is reached.
|
|
if (is_last_byte(buffer[count++])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
*output = decoded_value;
|
|
return count;
|
|
}
|
|
|
|
// TODO(frolv): Remove this deprecated alias.
|
|
extern "C" size_t pw_VarintEncode(uint64_t integer,
|
|
void* output,
|
|
size_t output_size) {
|
|
return pw_varint_Encode(integer, output, output_size);
|
|
}
|
|
|
|
extern "C" size_t pw_varint_ZigZagEncode(int64_t integer,
|
|
void* output,
|
|
size_t output_size) {
|
|
return pw_varint_Encode(ZigZagEncode(integer), output, output_size);
|
|
}
|
|
|
|
// TODO(frolv): Remove this deprecated alias.
|
|
extern "C" size_t pw_VarintDecode(const void* input,
|
|
size_t input_size,
|
|
uint64_t* output) {
|
|
return pw_varint_Decode(input, input_size, output);
|
|
}
|
|
|
|
extern "C" size_t pw_varint_ZigZagDecode(const void* input,
|
|
size_t input_size,
|
|
int64_t* output) {
|
|
uint64_t value = 0;
|
|
size_t bytes = pw_varint_Decode(input, input_size, &value);
|
|
*output = ZigZagDecode(value);
|
|
return bytes;
|
|
}
|
|
|
|
extern "C" size_t pw_varint_EncodedSize(uint64_t integer) {
|
|
return EncodedSize(integer);
|
|
}
|
|
|
|
extern "C" size_t pw_varint_ZigZagEncodedSize(int64_t integer) {
|
|
return ZigZagEncodedSize(integer);
|
|
}
|
|
|
|
} // namespace varint
|
|
} // namespace pw
|