xref: /aosp_15_r20/external/pigweed/pw_tokenizer/encode_args.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_tokenizer/encode_args.h"
16 
17 #include <algorithm>
18 #include <cstring>
19 
20 #include "pw_preprocessor/compiler.h"
21 #include "pw_varint/varint.h"
22 
23 static_assert((PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 4) ||
24                   (PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 8),
25               "PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES must be 4 or 8");
26 
27 namespace pw::tokenizer {
28 namespace {
29 
30 // Declare the types as an enum for convenience.
31 enum class ArgType : uint8_t {
32   kInt = PW_TOKENIZER_ARG_TYPE_INT,
33   kInt64 = PW_TOKENIZER_ARG_TYPE_INT64,
34   kDouble = PW_TOKENIZER_ARG_TYPE_DOUBLE,
35   kString = PW_TOKENIZER_ARG_TYPE_STRING,
36 };
37 
EncodeInt(int value,const span<std::byte> & output)38 size_t EncodeInt(int value, const span<std::byte>& output) {
39   // Use the 64-bit function to avoid instantiating both 32-bit and 64-bit.
40   return pw_tokenizer_EncodeInt64(value, output.data(), output.size());
41 }
42 
EncodeInt64(int64_t value,const span<std::byte> & output)43 size_t EncodeInt64(int64_t value, const span<std::byte>& output) {
44   return pw_tokenizer_EncodeInt64(value, output.data(), output.size());
45 }
46 
EncodeFloat(float value,const span<std::byte> & output)47 size_t EncodeFloat(float value, const span<std::byte>& output) {
48   if (output.size() < sizeof(value)) {
49     return 0;
50   }
51   std::memcpy(output.data(), &value, sizeof(value));
52   return sizeof(value);
53 }
54 
EncodeString(const char * string,const span<std::byte> & output)55 size_t EncodeString(const char* string, const span<std::byte>& output) {
56   // The top bit of the status byte indicates if the string was truncated.
57   static constexpr size_t kMaxStringLength = 0x7Fu;
58 
59   if (output.empty()) {  // At least one byte is needed for the status/size.
60     return 0;
61   }
62 
63   if (string == nullptr) {
64     string = "NULL";
65   }
66 
67   // Subtract 1 to save room for the status byte.
68   const size_t max_bytes =
69       std::min(static_cast<size_t>(output.size()), kMaxStringLength) - 1;
70 
71   // Scan the string to find out how many bytes to copy.
72   size_t bytes_to_copy = 0;
73   std::byte overflow_bit = std::byte(0);
74 
75   while (string[bytes_to_copy] != '\0') {
76     if (bytes_to_copy == max_bytes) {
77       overflow_bit = std::byte('\x80');
78       break;
79     }
80     bytes_to_copy += 1;
81   }
82 
83   output[0] = static_cast<std::byte>(bytes_to_copy) | overflow_bit;
84   std::memcpy(output.data() + 1, string, bytes_to_copy);
85 
86   return bytes_to_copy + 1;  // include the status byte in the total
87 }
88 
89 }  // namespace
90 
EncodeArgs(pw_tokenizer_ArgTypes types,va_list args,span<std::byte> output)91 size_t EncodeArgs(pw_tokenizer_ArgTypes types,
92                   va_list args,
93                   span<std::byte> output) {
94   size_t arg_count = types & PW_TOKENIZER_TYPE_COUNT_MASK;
95   types >>= PW_TOKENIZER_TYPE_COUNT_SIZE_BITS;
96 
97   size_t encoded_bytes = 0;
98   while (arg_count != 0u) {
99     // How many bytes were encoded; 0 indicates that there wasn't enough space.
100     size_t argument_bytes = 0;
101 
102     switch (static_cast<ArgType>(types & 0b11u)) {
103       case ArgType::kInt:
104         argument_bytes = EncodeInt(va_arg(args, int), output);
105         break;
106       case ArgType::kInt64:
107         argument_bytes = EncodeInt64(va_arg(args, int64_t), output);
108         break;
109       case ArgType::kDouble:
110         argument_bytes =
111             EncodeFloat(static_cast<float>(va_arg(args, double)), output);
112         break;
113       case ArgType::kString:
114         argument_bytes = EncodeString(va_arg(args, const char*), output);
115         break;
116     }
117 
118     // If zero bytes were encoded, the encoding buffer is full.
119     if (argument_bytes == 0u) {
120       break;
121     }
122 
123     output = output.subspan(argument_bytes);
124     encoded_bytes += argument_bytes;
125 
126     arg_count -= 1;
127     types >>= 2;  // each argument type is encoded in two bits
128   }
129 
130   return encoded_bytes;
131 }
132 
pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,va_list args,void * output_buffer,size_t output_buffer_size)133 extern "C" size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
134                                           va_list args,
135                                           void* output_buffer,
136                                           size_t output_buffer_size) {
137   return EncodeArgs(types,
138                     args,
139                     span<std::byte>(static_cast<std::byte*>(output_buffer),
140                                     output_buffer_size));
141 }
142 
143 }  // namespace pw::tokenizer
144