blob: ddfc0ed40062cf035847049db13c14337e5f8544 [file] [log] [blame]
Alexei Frolov8ecefe92020-01-13 10:40:08 -08001// Copyright 2020 The Pigweed Authors
Alexei Frolov82d3cb32019-11-27 14:38:39 -08002//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
Wyatt Hepler8c493bb2019-12-02 14:49:21 -08004// use this file except in compliance with the License. You may obtain a copy of
5// the License at
Alexei Frolov82d3cb32019-11-27 14:38:39 -08006//
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
Wyatt Hepler8c493bb2019-12-02 14:49:21 -080012// License for the specific language governing permissions and limitations under
13// the License.
Alexei Frolov82d3cb32019-11-27 14:38:39 -080014
15#include "pw_varint/varint.h"
16
17#include <algorithm>
18
Wyatt Hepler588907a2020-01-16 16:34:58 -080019namespace pw {
20namespace varint {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080021
Alexei Frolov8ecefe92020-01-13 10:40:08 -080022extern "C" size_t pw_VarintEncode(uint64_t integer,
23 void* output,
24 size_t output_size) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080025 size_t written = 0;
Alexei Frolov8ecefe92020-01-13 10:40:08 -080026 std::byte* buffer = static_cast<std::byte*>(output);
27
Alexei Frolov82d3cb32019-11-27 14:38:39 -080028 do {
Alexei Frolov8ecefe92020-01-13 10:40:08 -080029 if (written >= output_size) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080030 return 0;
31 }
32
33 // Grab 7 bits; the eighth bit is set to 1 to indicate more data coming.
Wyatt Hepler588907a2020-01-16 16:34:58 -080034 buffer[written++] = static_cast<std::byte>(integer) | std::byte(0x80);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080035 integer >>= 7;
36 } while (integer != 0u);
37
Wyatt Hepler588907a2020-01-16 16:34:58 -080038 buffer[written - 1] &= std::byte(0x7f); // clear the top bit of the last byte
Alexei Frolov82d3cb32019-11-27 14:38:39 -080039 return written;
40}
41
Alexei Frolov8ecefe92020-01-13 10:40:08 -080042extern "C" size_t pw_VarintZigZagEncode(int64_t integer,
43 void* output,
44 size_t output_size) {
45 return pw_VarintEncode(ZigZagEncode(integer), output, output_size);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080046}
47
Alexei Frolov8ecefe92020-01-13 10:40:08 -080048extern "C" size_t pw_VarintDecode(const void* input,
49 size_t input_size,
50 uint64_t* output) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080051 uint64_t decoded_value = 0;
52 uint_fast8_t count = 0;
Alexei Frolov8ecefe92020-01-13 10:40:08 -080053 const std::byte* buffer = static_cast<const std::byte*>(input);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080054
55 // The largest 64-bit ints require 10 B.
Alexei Frolov8ecefe92020-01-13 10:40:08 -080056 const size_t max_count = std::min(kMaxVarintSizeBytes, input_size);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080057
58 while (true) {
59 if (count >= max_count) {
60 return 0;
61 }
62
63 // Add the bottom seven bits of the next byte to the result.
Wyatt Hepler588907a2020-01-16 16:34:58 -080064 decoded_value |= static_cast<uint64_t>(buffer[count] & std::byte(0x7f))
Alexei Frolov82d3cb32019-11-27 14:38:39 -080065 << (7 * count);
66
67 // Stop decoding if the top bit is not set.
Wyatt Hepler588907a2020-01-16 16:34:58 -080068 if ((buffer[count++] & std::byte(0x80)) == std::byte(0)) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080069 break;
70 }
71 }
72
Alexei Frolov8ecefe92020-01-13 10:40:08 -080073 *output = decoded_value;
Alexei Frolov82d3cb32019-11-27 14:38:39 -080074 return count;
75}
76
Alexei Frolov8ecefe92020-01-13 10:40:08 -080077extern "C" size_t pw_VarintZigZagDecode(const void* input,
78 size_t input_size,
79 int64_t* output) {
80 uint64_t value = 0;
81 size_t bytes = pw_VarintDecode(input, input_size, &value);
82 *output = ZigZagDecode(value);
83 return bytes;
84}
85
Wyatt Hepler588907a2020-01-16 16:34:58 -080086} // namespace varint
87} // namespace pw