blob: 0eadab3c24b8afa61f78aa8c009385dd3427b935 [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 Frolov247efd92021-03-10 20:16:16 -080021namespace {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080022
Alexei Frolov247efd92021-03-10 20:16:16 -080023inline bool ZeroTerminated(pw_varint_Format format) {
24 return (static_cast<unsigned>(format) & 0b10) == 0;
25}
26
27inline bool LeastSignificant(pw_varint_Format format) {
28 return (static_cast<unsigned>(format) & 0b01) == 0;
29}
30
31} // namespace
32
Ted Pudlik4180d4d2021-10-28 00:07:29 +000033extern "C" size_t pw_varint_EncodeCustom(uint64_t integer,
Alexei Frolov247efd92021-03-10 20:16:16 -080034 void* output,
35 size_t output_size,
36 pw_varint_Format format) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080037 size_t written = 0;
Alexei Frolov8ecefe92020-01-13 10:40:08 -080038 std::byte* buffer = static_cast<std::byte*>(output);
39
Alexei Frolov247efd92021-03-10 20:16:16 -080040 int value_shift = LeastSignificant(format) ? 1 : 0;
41 int term_shift = value_shift == 1 ? 0 : 7;
42
43 std::byte cont, term;
44 if (ZeroTerminated(format)) {
45 cont = std::byte(0x01) << term_shift;
46 term = std::byte(0x00) << term_shift;
47 } else {
48 cont = std::byte(0x00) << term_shift;
49 term = std::byte(0x01) << term_shift;
50 }
51
Alexei Frolov82d3cb32019-11-27 14:38:39 -080052 do {
Alexei Frolov8ecefe92020-01-13 10:40:08 -080053 if (written >= output_size) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080054 return 0;
55 }
56
Ted Pudlik4180d4d2021-10-28 00:07:29 +000057 bool last_byte = (integer >> 7) == 0u;
Alexei Frolov82d3cb32019-11-27 14:38:39 -080058
Alexei Frolov247efd92021-03-10 20:16:16 -080059 // Grab 7 bits and set the eighth according to the continuation bit.
Ted Pudlik4180d4d2021-10-28 00:07:29 +000060 std::byte value = (static_cast<std::byte>(integer) & std::byte(0x7f))
Alexei Frolov247efd92021-03-10 20:16:16 -080061 << value_shift;
62
63 if (last_byte) {
64 value |= term;
65 } else {
66 value |= cont;
67 }
68
69 buffer[written++] = value;
Ted Pudlik4180d4d2021-10-28 00:07:29 +000070 integer >>= 7;
71 } while (integer != 0u);
Alexei Frolov247efd92021-03-10 20:16:16 -080072
Alexei Frolov82d3cb32019-11-27 14:38:39 -080073 return written;
74}
75
Alexei Frolov247efd92021-03-10 20:16:16 -080076extern "C" size_t pw_varint_DecodeCustom(const void* input,
77 size_t input_size,
78 uint64_t* output,
79 pw_varint_Format format) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -080080 uint64_t decoded_value = 0;
81 uint_fast8_t count = 0;
Alexei Frolov8ecefe92020-01-13 10:40:08 -080082 const std::byte* buffer = static_cast<const std::byte*>(input);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080083
84 // The largest 64-bit ints require 10 B.
Ewout van Bekkumbd338122020-09-22 16:32:45 -070085 const size_t max_count = std::min(kMaxVarint64SizeBytes, input_size);
Alexei Frolov82d3cb32019-11-27 14:38:39 -080086
Alexei Frolov247efd92021-03-10 20:16:16 -080087 std::byte mask;
88 uint32_t shift;
89 if (LeastSignificant(format)) {
90 mask = std::byte(0xfe);
91 shift = 1;
92 } else {
93 mask = std::byte(0x7f);
94 shift = 0;
95 }
96
97 // Determines whether a byte is the last byte of a varint.
98 auto is_last_byte = [&](std::byte byte) {
99 if (ZeroTerminated(format)) {
100 return (byte & ~mask) == std::byte(0);
101 }
102 return (byte & ~mask) != std::byte(0);
103 };
104
Alexei Frolov82d3cb32019-11-27 14:38:39 -0800105 while (true) {
106 if (count >= max_count) {
107 return 0;
108 }
109
110 // Add the bottom seven bits of the next byte to the result.
Alexei Frolov247efd92021-03-10 20:16:16 -0800111 decoded_value |= static_cast<uint64_t>((buffer[count] & mask) >> shift)
Alexei Frolov82d3cb32019-11-27 14:38:39 -0800112 << (7 * count);
113
Alexei Frolov247efd92021-03-10 20:16:16 -0800114 // Stop decoding if the end is reached.
115 if (is_last_byte(buffer[count++])) {
Alexei Frolov82d3cb32019-11-27 14:38:39 -0800116 break;
117 }
118 }
119
Alexei Frolov8ecefe92020-01-13 10:40:08 -0800120 *output = decoded_value;
Alexei Frolov82d3cb32019-11-27 14:38:39 -0800121 return count;
122}
123
Alexei Frolovb9fda582021-03-13 18:02:52 -0800124// TODO(frolv): Remove this deprecated alias.
Alexei Frolov247efd92021-03-10 20:16:16 -0800125extern "C" size_t pw_VarintEncode(uint64_t integer,
126 void* output,
127 size_t output_size) {
128 return pw_varint_Encode(integer, output, output_size);
129}
130
131extern "C" size_t pw_varint_ZigZagEncode(int64_t integer,
132 void* output,
133 size_t output_size) {
134 return pw_varint_Encode(ZigZagEncode(integer), output, output_size);
135}
136
Alexei Frolovb9fda582021-03-13 18:02:52 -0800137// TODO(frolv): Remove this deprecated alias.
Alexei Frolov247efd92021-03-10 20:16:16 -0800138extern "C" size_t pw_VarintDecode(const void* input,
139 size_t input_size,
140 uint64_t* output) {
141 return pw_varint_Decode(input, input_size, output);
142}
143
144extern "C" size_t pw_varint_ZigZagDecode(const void* input,
145 size_t input_size,
146 int64_t* output) {
Alexei Frolov8ecefe92020-01-13 10:40:08 -0800147 uint64_t value = 0;
Alexei Frolov247efd92021-03-10 20:16:16 -0800148 size_t bytes = pw_varint_Decode(input, input_size, &value);
Alexei Frolov8ecefe92020-01-13 10:40:08 -0800149 *output = ZigZagDecode(value);
150 return bytes;
151}
152
Alexei Frolov247efd92021-03-10 20:16:16 -0800153extern "C" size_t pw_varint_EncodedSize(uint64_t integer) {
Ewout van Bekkumdc6d9842020-09-23 18:05:19 -0700154 return EncodedSize(integer);
Alexei Frolov388d4b92020-05-20 13:30:07 -0700155}
156
Alexei Frolov247efd92021-03-10 20:16:16 -0800157extern "C" size_t pw_varint_ZigZagEncodedSize(int64_t integer) {
Ewout van Bekkumdc6d9842020-09-23 18:05:19 -0700158 return ZigZagEncodedSize(integer);
Alexei Frolov388d4b92020-05-20 13:30:07 -0700159}
160
Wyatt Hepler588907a2020-01-16 16:34:58 -0800161} // namespace varint
162} // namespace pw