1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_tokenizer/encode_args.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
18*61c4878aSAndroid Build Coastguard Worker #include <cstring>
19*61c4878aSAndroid Build Coastguard Worker
20*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/compiler.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_varint/varint.h"
22*61c4878aSAndroid Build Coastguard Worker
23*61c4878aSAndroid Build Coastguard Worker static_assert((PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 4) ||
24*61c4878aSAndroid Build Coastguard Worker (PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 8),
25*61c4878aSAndroid Build Coastguard Worker "PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES must be 4 or 8");
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker namespace pw::tokenizer {
28*61c4878aSAndroid Build Coastguard Worker namespace {
29*61c4878aSAndroid Build Coastguard Worker
30*61c4878aSAndroid Build Coastguard Worker // Declare the types as an enum for convenience.
31*61c4878aSAndroid Build Coastguard Worker enum class ArgType : uint8_t {
32*61c4878aSAndroid Build Coastguard Worker kInt = PW_TOKENIZER_ARG_TYPE_INT,
33*61c4878aSAndroid Build Coastguard Worker kInt64 = PW_TOKENIZER_ARG_TYPE_INT64,
34*61c4878aSAndroid Build Coastguard Worker kDouble = PW_TOKENIZER_ARG_TYPE_DOUBLE,
35*61c4878aSAndroid Build Coastguard Worker kString = PW_TOKENIZER_ARG_TYPE_STRING,
36*61c4878aSAndroid Build Coastguard Worker };
37*61c4878aSAndroid Build Coastguard Worker
EncodeInt(int value,const span<std::byte> & output)38*61c4878aSAndroid Build Coastguard Worker size_t EncodeInt(int value, const span<std::byte>& output) {
39*61c4878aSAndroid Build Coastguard Worker // Use the 64-bit function to avoid instantiating both 32-bit and 64-bit.
40*61c4878aSAndroid Build Coastguard Worker return pw_tokenizer_EncodeInt64(value, output.data(), output.size());
41*61c4878aSAndroid Build Coastguard Worker }
42*61c4878aSAndroid Build Coastguard Worker
EncodeInt64(int64_t value,const span<std::byte> & output)43*61c4878aSAndroid Build Coastguard Worker size_t EncodeInt64(int64_t value, const span<std::byte>& output) {
44*61c4878aSAndroid Build Coastguard Worker return pw_tokenizer_EncodeInt64(value, output.data(), output.size());
45*61c4878aSAndroid Build Coastguard Worker }
46*61c4878aSAndroid Build Coastguard Worker
EncodeFloat(float value,const span<std::byte> & output)47*61c4878aSAndroid Build Coastguard Worker size_t EncodeFloat(float value, const span<std::byte>& output) {
48*61c4878aSAndroid Build Coastguard Worker if (output.size() < sizeof(value)) {
49*61c4878aSAndroid Build Coastguard Worker return 0;
50*61c4878aSAndroid Build Coastguard Worker }
51*61c4878aSAndroid Build Coastguard Worker std::memcpy(output.data(), &value, sizeof(value));
52*61c4878aSAndroid Build Coastguard Worker return sizeof(value);
53*61c4878aSAndroid Build Coastguard Worker }
54*61c4878aSAndroid Build Coastguard Worker
EncodeString(const char * string,const span<std::byte> & output)55*61c4878aSAndroid Build Coastguard Worker size_t EncodeString(const char* string, const span<std::byte>& output) {
56*61c4878aSAndroid Build Coastguard Worker // The top bit of the status byte indicates if the string was truncated.
57*61c4878aSAndroid Build Coastguard Worker static constexpr size_t kMaxStringLength = 0x7Fu;
58*61c4878aSAndroid Build Coastguard Worker
59*61c4878aSAndroid Build Coastguard Worker if (output.empty()) { // At least one byte is needed for the status/size.
60*61c4878aSAndroid Build Coastguard Worker return 0;
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker
63*61c4878aSAndroid Build Coastguard Worker if (string == nullptr) {
64*61c4878aSAndroid Build Coastguard Worker string = "NULL";
65*61c4878aSAndroid Build Coastguard Worker }
66*61c4878aSAndroid Build Coastguard Worker
67*61c4878aSAndroid Build Coastguard Worker // Subtract 1 to save room for the status byte.
68*61c4878aSAndroid Build Coastguard Worker const size_t max_bytes =
69*61c4878aSAndroid Build Coastguard Worker std::min(static_cast<size_t>(output.size()), kMaxStringLength) - 1;
70*61c4878aSAndroid Build Coastguard Worker
71*61c4878aSAndroid Build Coastguard Worker // Scan the string to find out how many bytes to copy.
72*61c4878aSAndroid Build Coastguard Worker size_t bytes_to_copy = 0;
73*61c4878aSAndroid Build Coastguard Worker std::byte overflow_bit = std::byte(0);
74*61c4878aSAndroid Build Coastguard Worker
75*61c4878aSAndroid Build Coastguard Worker while (string[bytes_to_copy] != '\0') {
76*61c4878aSAndroid Build Coastguard Worker if (bytes_to_copy == max_bytes) {
77*61c4878aSAndroid Build Coastguard Worker overflow_bit = std::byte('\x80');
78*61c4878aSAndroid Build Coastguard Worker break;
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker bytes_to_copy += 1;
81*61c4878aSAndroid Build Coastguard Worker }
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard Worker output[0] = static_cast<std::byte>(bytes_to_copy) | overflow_bit;
84*61c4878aSAndroid Build Coastguard Worker std::memcpy(output.data() + 1, string, bytes_to_copy);
85*61c4878aSAndroid Build Coastguard Worker
86*61c4878aSAndroid Build Coastguard Worker return bytes_to_copy + 1; // include the status byte in the total
87*61c4878aSAndroid Build Coastguard Worker }
88*61c4878aSAndroid Build Coastguard Worker
89*61c4878aSAndroid Build Coastguard Worker } // namespace
90*61c4878aSAndroid Build Coastguard Worker
EncodeArgs(pw_tokenizer_ArgTypes types,va_list args,span<std::byte> output)91*61c4878aSAndroid Build Coastguard Worker size_t EncodeArgs(pw_tokenizer_ArgTypes types,
92*61c4878aSAndroid Build Coastguard Worker va_list args,
93*61c4878aSAndroid Build Coastguard Worker span<std::byte> output) {
94*61c4878aSAndroid Build Coastguard Worker size_t arg_count = types & PW_TOKENIZER_TYPE_COUNT_MASK;
95*61c4878aSAndroid Build Coastguard Worker types >>= PW_TOKENIZER_TYPE_COUNT_SIZE_BITS;
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard Worker size_t encoded_bytes = 0;
98*61c4878aSAndroid Build Coastguard Worker while (arg_count != 0u) {
99*61c4878aSAndroid Build Coastguard Worker // How many bytes were encoded; 0 indicates that there wasn't enough space.
100*61c4878aSAndroid Build Coastguard Worker size_t argument_bytes = 0;
101*61c4878aSAndroid Build Coastguard Worker
102*61c4878aSAndroid Build Coastguard Worker switch (static_cast<ArgType>(types & 0b11u)) {
103*61c4878aSAndroid Build Coastguard Worker case ArgType::kInt:
104*61c4878aSAndroid Build Coastguard Worker argument_bytes = EncodeInt(va_arg(args, int), output);
105*61c4878aSAndroid Build Coastguard Worker break;
106*61c4878aSAndroid Build Coastguard Worker case ArgType::kInt64:
107*61c4878aSAndroid Build Coastguard Worker argument_bytes = EncodeInt64(va_arg(args, int64_t), output);
108*61c4878aSAndroid Build Coastguard Worker break;
109*61c4878aSAndroid Build Coastguard Worker case ArgType::kDouble:
110*61c4878aSAndroid Build Coastguard Worker argument_bytes =
111*61c4878aSAndroid Build Coastguard Worker EncodeFloat(static_cast<float>(va_arg(args, double)), output);
112*61c4878aSAndroid Build Coastguard Worker break;
113*61c4878aSAndroid Build Coastguard Worker case ArgType::kString:
114*61c4878aSAndroid Build Coastguard Worker argument_bytes = EncodeString(va_arg(args, const char*), output);
115*61c4878aSAndroid Build Coastguard Worker break;
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker
118*61c4878aSAndroid Build Coastguard Worker // If zero bytes were encoded, the encoding buffer is full.
119*61c4878aSAndroid Build Coastguard Worker if (argument_bytes == 0u) {
120*61c4878aSAndroid Build Coastguard Worker break;
121*61c4878aSAndroid Build Coastguard Worker }
122*61c4878aSAndroid Build Coastguard Worker
123*61c4878aSAndroid Build Coastguard Worker output = output.subspan(argument_bytes);
124*61c4878aSAndroid Build Coastguard Worker encoded_bytes += argument_bytes;
125*61c4878aSAndroid Build Coastguard Worker
126*61c4878aSAndroid Build Coastguard Worker arg_count -= 1;
127*61c4878aSAndroid Build Coastguard Worker types >>= 2; // each argument type is encoded in two bits
128*61c4878aSAndroid Build Coastguard Worker }
129*61c4878aSAndroid Build Coastguard Worker
130*61c4878aSAndroid Build Coastguard Worker return encoded_bytes;
131*61c4878aSAndroid Build Coastguard Worker }
132*61c4878aSAndroid Build Coastguard Worker
pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,va_list args,void * output_buffer,size_t output_buffer_size)133*61c4878aSAndroid Build Coastguard Worker extern "C" size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
134*61c4878aSAndroid Build Coastguard Worker va_list args,
135*61c4878aSAndroid Build Coastguard Worker void* output_buffer,
136*61c4878aSAndroid Build Coastguard Worker size_t output_buffer_size) {
137*61c4878aSAndroid Build Coastguard Worker return EncodeArgs(types,
138*61c4878aSAndroid Build Coastguard Worker args,
139*61c4878aSAndroid Build Coastguard Worker span<std::byte>(static_cast<std::byte*>(output_buffer),
140*61c4878aSAndroid Build Coastguard Worker output_buffer_size));
141*61c4878aSAndroid Build Coastguard Worker }
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard Worker } // namespace pw::tokenizer
144