C/C++ API Reference
Loading...
Searching...
No Matches
varint.h
Go to the documentation of this file.
1// Copyright 2023 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
33
34#include <stdbool.h>
35#include <stddef.h>
36#include <stdint.h>
37
38#ifdef __cplusplus
39
40#include <limits>
41#include <type_traits>
42
43#include "lib/stdcompat/bit.h"
44#include "pw_bytes/span.h"
45#include "pw_preprocessor/compiler.h"
46
47extern "C" {
48#endif // __cplusplus
49
50// C++ API.
51
53#define PW_VARINT_MAX_INT32_SIZE_BYTES 5
54
56#define PW_VARINT_MAX_INT64_SIZE_BYTES 10
57
59typedef enum {
60 PW_VARINT_ZERO_TERMINATED_LEAST_SIGNIFICANT = 0,
61 PW_VARINT_ZERO_TERMINATED_MOST_SIGNIFICANT = 1,
62 PW_VARINT_ONE_TERMINATED_LEAST_SIGNIFICANT = 2,
63 PW_VARINT_ONE_TERMINATED_MOST_SIGNIFICANT = 3,
65
66#ifdef __cplusplus
67} // extern "C"
68namespace pw::varint {
69
72
75
78
80enum class Format {
81 kZeroTerminatedLeastSignificant = PW_VARINT_ZERO_TERMINATED_LEAST_SIGNIFICANT,
82 kZeroTerminatedMostSignificant = PW_VARINT_ZERO_TERMINATED_MOST_SIGNIFICANT,
83 kOneTerminatedLeastSignificant = PW_VARINT_ONE_TERMINATED_LEAST_SIGNIFICANT,
84 kOneTerminatedMostSignificant = PW_VARINT_ONE_TERMINATED_MOST_SIGNIFICANT,
85};
86
109constexpr uint64_t MaxValueInBytes(size_t bytes) {
110 return bytes >= kMaxVarint64SizeBytes ? std::numeric_limits<uint64_t>::max()
111 : (uint64_t(1) << (7 * bytes)) - 1;
112}
113
120template <typename T,
121 typename = std::enable_if_t<std::is_integral<T>::value ||
122 std::is_convertible<T, uint64_t>::value>>
123constexpr size_t EncodedSize(T value) {
124 return value == 0
125 ? 1u
126 : static_cast<size_t>(
127 (64 - cpp20::countl_zero(static_cast<uint64_t>(value)) + 6) /
128 7);
129}
130
143template <typename T>
144constexpr size_t Encode(T value, ByteSpan out_encoded);
145size_t Encode(uint64_t value, ByteSpan out_encoded, Format format);
147
150constexpr size_t EncodeLittleEndianBase128(uint64_t value,
151 ByteSpan out_encoded);
152
155template <typename T>
156constexpr std::byte EncodeOneByte(T* value);
157
171template <typename T>
172constexpr std::make_unsigned_t<T> ZigZagEncode(T value);
173
199template <typename T>
200constexpr size_t Decode(ConstByteSpan encoded, T* out_value);
201size_t Decode(ConstByteSpan encoded, uint64_t* out_value, Format format);
203
206template <typename T>
207[[nodiscard]] constexpr bool DecodeOneByte(std::byte encoded,
208 size_t count,
209 T* out_value);
210
215template <typename T>
216constexpr std::make_signed_t<T> ZigZagDecode(T encoded);
217
218} // namespace pw::varint
219extern "C" {
220#endif // __cplusplus
221
222// C API.
223
225size_t pw_varint_EncodedSizeBytes(uint64_t value);
226
228size_t pw_varint_Encode32(uint32_t value,
229 void* out_encoded,
230 size_t out_encoded_size);
231
233size_t pw_varint_Encode64(uint64_t value,
234 void* out_encoded,
235 size_t out_encoded_size);
236
238size_t pw_varint_EncodeCustom(uint64_t value,
239 void* out_encoded,
240 size_t out_encoded_size,
241 pw_varint_Format format);
242
244uint8_t pw_varint_EncodeOneByte32(uint32_t* value);
245
247uint8_t pw_varint_EncodeOneByte64(uint64_t* value);
248
250uint32_t pw_varint_ZigZagEncode32(int32_t value);
251
253uint64_t pw_varint_ZigZagEncode64(int64_t value);
254
256size_t pw_varint_Decode32(const void* encoded,
257 size_t encoded_size,
258 uint32_t* out_value);
259
261size_t pw_varint_Decode64(const void* encoded,
262 size_t encoded_size,
263 uint64_t* out_value);
264
266size_t pw_varint_DecodeCustom(const void* encoded,
267 size_t encoded_size,
268 uint64_t* out_value,
269 pw_varint_Format format);
270
272bool pw_varint_DecodeOneByte32(uint8_t encoded,
273 size_t count,
274 uint32_t* out_value);
275
277bool pw_varint_DecodeOneByte64(uint8_t encoded,
278 size_t count,
279 uint64_t* out_value);
280
282int32_t pw_varint_ZigZagDecode32(uint32_t encoded);
283
285int64_t pw_varint_ZigZagDecode64(uint64_t encoded);
286
290#define PW_VARINT_ENCODED_SIZE_BYTES(value) \
291 ((unsigned long long)value < (1u << 7) ? 1u \
292 : (unsigned long long)value < (1u << 14) ? 2u \
293 : (unsigned long long)value < (1u << 21) ? 3u \
294 : (unsigned long long)value < (1u << 28) ? 4u \
295 : (unsigned long long)value < (1llu << 35) ? 5u \
296 : (unsigned long long)value < (1llu << 42) ? 6u \
297 : (unsigned long long)value < (1llu << 49) ? 7u \
298 : (unsigned long long)value < (1llu << 56) ? 8u \
299 : (unsigned long long)value < (1llu << 63) ? 9u \
300 : 10u)
301
302#ifdef __cplusplus
303} // extern "C"
304namespace pw::varint {
305
307// Template function implementations.
308
309namespace internal {
310
311template <typename U>
312constexpr size_t DecodeUnsigned(ConstByteSpan encoded,
313 U* out_uvalue,
314 size_t max_count) {
315 max_count = std::min(encoded.size(), max_count);
316 size_t count = 0;
317 bool keep_going = true;
318 while (keep_going) {
319 if (count >= max_count) {
320 *out_uvalue = U(0);
321 return 0;
322 }
323 keep_going = DecodeOneByte(encoded[count], count, out_uvalue);
324 ++count;
325 }
326 return count;
327}
328
329template <typename U>
330constexpr size_t EncodeUnsigned(U uvalue, ByteSpan out_encoded) {
331 size_t written = 0;
332 do {
333 if (written >= out_encoded.size()) {
334 return 0u;
335 }
336 out_encoded[written++] = EncodeOneByte(&uvalue);
337 } while (uvalue != 0u);
338
339 out_encoded[written - 1] &= static_cast<std::byte>(0x7f);
340 return written;
341}
342
343} // namespace internal
344
345template <typename T>
346constexpr size_t Encode(T value, ByteSpan out_encoded) {
347 using U = std::conditional_t<sizeof(T) <= sizeof(uint32_t),
348 uint_fast32_t,
349 uint_fast64_t>;
350 if constexpr (std::is_signed<T>()) {
351 return internal::EncodeUnsigned<U>(ZigZagEncode(value), out_encoded);
352 } else {
353 return internal::EncodeUnsigned<U>(value, out_encoded);
354 }
355}
356
357template <typename T>
358constexpr std::byte EncodeOneByte(T* value) {
359 const auto bits = static_cast<std::byte>((*value & 0x7Fu) | 0x80u);
360 *value >>= 7;
361 return bits;
362}
363
364template <typename T>
365constexpr std::make_unsigned_t<T> ZigZagEncode(T value) {
366 static_assert(std::is_signed<T>(), "Zig-zag encoding is for signed integers");
367 using U = std::make_unsigned_t<T>;
368 return static_cast<U>(static_cast<U>(value) << 1) ^
369 static_cast<U>(value >> (sizeof(T) * 8 - 1));
370}
371
372constexpr size_t EncodeLittleEndianBase128(uint64_t value,
373 ByteSpan out_encoded) {
374 return Encode(value, out_encoded);
375}
376
377template <typename T>
378constexpr size_t Decode(ConstByteSpan encoded, T* out_value) {
379 static_assert(sizeof(T) >= sizeof(uint32_t));
380 using U = std::conditional_t<sizeof(T) == sizeof(uint32_t),
381 uint_fast32_t,
382 uint_fast64_t>;
383 U uvalue = 0u;
384 size_t max_count = sizeof(T) <= sizeof(uint32_t) ? kMaxVarint32SizeBytes
386 size_t count = internal::DecodeUnsigned(encoded, &uvalue, max_count);
387 if constexpr (std::is_signed_v<T>) {
388 *out_value = static_cast<T>(ZigZagDecode(uvalue));
389 } else {
390 *out_value = static_cast<T>(uvalue);
391 }
392 return count;
393}
394
395template <typename T>
396[[nodiscard]] constexpr bool DecodeOneByte(std::byte encoded,
397 size_t count,
398 T* out_value) {
399 auto u8 = static_cast<uint_fast8_t>(encoded);
400 *out_value |= static_cast<T>(u8 & 0x7fu) << (count * 7);
401 return (u8 & 0x80u) != 0u;
402}
403
404template <typename T>
405constexpr std::make_signed_t<T> ZigZagDecode(T encoded)
406 PW_NO_SANITIZE("unsigned-integer-overflow") {
407 static_assert(std::is_unsigned<T>(),
408 "Zig-zag decoding is for unsigned integers");
409 return static_cast<std::make_signed_t<T>>((encoded >> 1) ^
410 (~(encoded & 1) + 1));
411}
412
413} // namespace pw::varint
414#endif // __cplusplus
void Encode(span< const std::byte > binary, char *output)
Definition: base64.h:109
#define PW_NO_SANITIZE(check)
Definition: compiler.h:158
Format
Describes a custom varint format.
Definition: varint.h:80
constexpr size_t kMaxVarint32SizeBytes
Maximum size of a varint (LEB128) encoded uint32_t.
Definition: varint.h:74
constexpr std::make_unsigned_t< T > ZigZagEncode(T value)
Definition: varint.h:365
constexpr size_t kMaxVarint64SizeBytes
Maximum size of a varint (LEB128) encoded uint64_t.
Definition: varint.h:77
constexpr std::byte EncodeOneByte(T *value)
Definition: varint.h:358
constexpr std::make_signed_t< T > ZigZagDecode(T encoded)
Definition: varint.h:405
constexpr bool DecodeOneByte(std::byte encoded, size_t count, T *out_value)
Definition: varint.h:396
constexpr size_t EncodeLittleEndianBase128(uint64_t value, ByteSpan out_encoded)
Definition: varint.h:372
constexpr size_t EncodedSize(T value)
Computes the size of an integer when encoded as a varint.
Definition: varint.h:123
constexpr uint64_t MaxValueInBytes(size_t bytes)
Returns the maximum (max) integer value that can be encoded as a varint into the specified number of ...
Definition: varint.h:109
pw_varint_Format
Describes a custom varint format.
Definition: varint.h:59
size_t pw_varint_Decode64(const void *encoded, size_t encoded_size, uint64_t *out_value)
size_t pw_varint_Decode32(const void *encoded, size_t encoded_size, uint32_t *out_value)
bool pw_varint_DecodeOneByte64(uint8_t encoded, size_t count, uint64_t *out_value)
uint64_t pw_varint_ZigZagEncode64(int64_t value)
size_t pw_varint_EncodedSizeBytes(uint64_t value)
Computes the size of an integer when encoded as a varint.
#define PW_VARINT_MAX_INT64_SIZE_BYTES
Maximum size of an LEB128-encoded uint64_t.
Definition: varint.h:56
size_t pw_varint_Encode32(uint32_t value, void *out_encoded, size_t out_encoded_size)
size_t pw_varint_EncodeCustom(uint64_t value, void *out_encoded, size_t out_encoded_size, pw_varint_Format format)
#define PW_VARINT_MAX_INT32_SIZE_BYTES
Maximum size of an LEB128-encoded uint32_t.
Definition: varint.h:53
int64_t pw_varint_ZigZagDecode64(uint64_t encoded)
uint8_t pw_varint_EncodeOneByte64(uint64_t *value)
bool pw_varint_DecodeOneByte32(uint8_t encoded, size_t count, uint32_t *out_value)
int32_t pw_varint_ZigZagDecode32(uint32_t encoded)
uint8_t pw_varint_EncodeOneByte32(uint32_t *value)
size_t pw_varint_DecodeCustom(const void *encoded, size_t encoded_size, uint64_t *out_value, pw_varint_Format format)
uint32_t pw_varint_ZigZagEncode32(int32_t value)
size_t pw_varint_Encode64(uint64_t value, void *out_encoded, size_t out_encoded_size)