xref: /aosp_15_r20/external/pigweed/pw_varint/varint.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 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 
15 #include "pw_varint/varint.h"
16 
17 #include <algorithm>
18 #include <cstddef>
19 
20 namespace pw {
21 namespace varint {
22 namespace {
23 
ZeroTerminated(pw_varint_Format format)24 inline bool ZeroTerminated(pw_varint_Format format) {
25   return (static_cast<unsigned>(format) & 0b10) == 0;
26 }
27 
LeastSignificant(pw_varint_Format format)28 inline bool LeastSignificant(pw_varint_Format format) {
29   return (static_cast<unsigned>(format) & 0b01) == 0;
30 }
31 
32 }  // namespace
33 
pw_varint_EncodeCustom(uint64_t integer,void * output,size_t output_size,pw_varint_Format format)34 extern "C" size_t pw_varint_EncodeCustom(uint64_t integer,
35                                          void* output,
36                                          size_t output_size,
37                                          pw_varint_Format format) {
38   size_t written = 0;
39   std::byte* buffer = static_cast<std::byte*>(output);
40 
41   int value_shift = LeastSignificant(format) ? 1 : 0;
42   int term_shift = value_shift == 1 ? 0 : 7;
43 
44   std::byte cont, term;
45   if (ZeroTerminated(format)) {
46     cont = std::byte(0x01) << term_shift;
47     term = std::byte(0x00) << term_shift;
48   } else {
49     cont = std::byte(0x00) << term_shift;
50     term = std::byte(0x01) << term_shift;
51   }
52 
53   do {
54     if (written >= output_size) {
55       return 0;
56     }
57 
58     bool last_byte = (integer >> 7) == 0u;
59 
60     // Grab 7 bits and set the eighth according to the continuation bit.
61     std::byte value = (static_cast<std::byte>(integer) & std::byte(0x7f))
62                       << value_shift;
63 
64     if (last_byte) {
65       value |= term;
66     } else {
67       value |= cont;
68     }
69 
70     buffer[written++] = value;
71     integer >>= 7;
72   } while (integer != 0u);
73 
74   return written;
75 }
76 
pw_varint_DecodeCustom(const void * input,size_t input_size,uint64_t * output,pw_varint_Format format)77 extern "C" size_t pw_varint_DecodeCustom(const void* input,
78                                          size_t input_size,
79                                          uint64_t* output,
80                                          pw_varint_Format format) {
81   uint64_t decoded_value = 0;
82   uint_fast8_t count = 0;
83   const std::byte* buffer = static_cast<const std::byte*>(input);
84 
85   // The largest 64-bit ints require 10 B.
86   const size_t max_count = std::min(kMaxVarint64SizeBytes, input_size);
87 
88   std::byte mask;
89   uint32_t shift;
90   if (LeastSignificant(format)) {
91     mask = std::byte(0xfe);
92     shift = 1;
93   } else {
94     mask = std::byte(0x7f);
95     shift = 0;
96   }
97 
98   // Determines whether a byte is the last byte of a varint.
99   auto is_last_byte = [&](std::byte byte) {
100     if (ZeroTerminated(format)) {
101       return (byte & ~mask) == std::byte(0);
102     }
103     return (byte & ~mask) != std::byte(0);
104   };
105 
106   while (true) {
107     if (count >= max_count) {
108       return 0;
109     }
110 
111     // Add the bottom seven bits of the next byte to the result.
112     decoded_value |= static_cast<uint64_t>((buffer[count] & mask) >> shift)
113                      << (7 * count);
114 
115     // Stop decoding if the end is reached.
116     if (is_last_byte(buffer[count++])) {
117       break;
118     }
119   }
120 
121   *output = decoded_value;
122   return count;
123 }
124 
pw_varint_EncodedSizeBytes(uint64_t integer)125 extern "C" size_t pw_varint_EncodedSizeBytes(uint64_t integer) {
126   return EncodedSize(integer);
127 }
128 
129 }  // namespace varint
130 }  // namespace pw
131