• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace pw {
24 namespace tokenizer {
25 namespace {
26 
27 // Declare the types as an enum for convenience.
28 enum class ArgType : uint8_t {
29   kInt = PW_TOKENIZER_ARG_TYPE_INT,
30   kInt64 = PW_TOKENIZER_ARG_TYPE_INT64,
31   kDouble = PW_TOKENIZER_ARG_TYPE_DOUBLE,
32   kString = PW_TOKENIZER_ARG_TYPE_STRING,
33 };
34 
35 // Just to be safe, make sure these values are what we expect them to be.
36 static_assert(0b00u == static_cast<uint8_t>(ArgType::kInt));
37 static_assert(0b01u == static_cast<uint8_t>(ArgType::kInt64));
38 static_assert(0b10u == static_cast<uint8_t>(ArgType::kDouble));
39 static_assert(0b11u == static_cast<uint8_t>(ArgType::kString));
40 
EncodeInt(int value,const std::span<std::byte> & output)41 size_t EncodeInt(int value, const std::span<std::byte>& output) {
42   return varint::Encode(value, std::as_writable_bytes(output));
43 }
44 
EncodeInt64(int64_t value,const std::span<std::byte> & output)45 size_t EncodeInt64(int64_t value, const std::span<std::byte>& output) {
46   return varint::Encode(value, std::as_writable_bytes(output));
47 }
48 
EncodeFloat(float value,const std::span<std::byte> & output)49 size_t EncodeFloat(float value, const std::span<std::byte>& output) {
50   if (output.size() < sizeof(value)) {
51     return 0;
52   }
53   std::memcpy(output.data(), &value, sizeof(value));
54   return sizeof(value);
55 }
56 
EncodeString(const char * string,const std::span<std::byte> & output)57 size_t EncodeString(const char* string, const std::span<std::byte>& output) {
58   // The top bit of the status byte indicates if the string was truncated.
59   static constexpr size_t kMaxStringLength = 0x7Fu;
60 
61   if (output.empty()) {  // At least one byte is needed for the status/size.
62     return 0;
63   }
64 
65   if (string == nullptr) {
66     string = "NULL";
67   }
68 
69   // Subtract 1 to save room for the status byte.
70   const size_t max_bytes = std::min(output.size(), kMaxStringLength) - 1;
71 
72   // Scan the string to find out how many bytes to copy.
73   size_t bytes_to_copy = 0;
74   std::byte overflow_bit = std::byte(0);
75 
76   while (string[bytes_to_copy] != '\0') {
77     if (bytes_to_copy == max_bytes) {
78       overflow_bit = std::byte('\x80');
79       break;
80     }
81     bytes_to_copy += 1;
82   }
83 
84   output[0] = static_cast<std::byte>(bytes_to_copy) | overflow_bit;
85   std::memcpy(output.data() + 1, string, bytes_to_copy);
86 
87   return bytes_to_copy + 1;  // include the status byte in the total
88 }
89 
90 }  // namespace
91 
EncodeArgs(pw_tokenizer_ArgTypes types,va_list args,std::span<std::byte> output)92 size_t EncodeArgs(pw_tokenizer_ArgTypes types,
93                   va_list args,
94                   std::span<std::byte> output) {
95   size_t arg_count = types & PW_TOKENIZER_TYPE_COUNT_MASK;
96   types >>= PW_TOKENIZER_TYPE_COUNT_SIZE_BITS;
97 
98   size_t encoded_bytes = 0;
99   while (arg_count != 0u) {
100     // How many bytes were encoded; 0 indicates that there wasn't enough space.
101     size_t argument_bytes = 0;
102 
103     switch (static_cast<ArgType>(types & 0b11u)) {
104       case ArgType::kInt:
105         argument_bytes = EncodeInt(va_arg(args, int), output);
106         break;
107       case ArgType::kInt64:
108         argument_bytes = EncodeInt64(va_arg(args, int64_t), output);
109         break;
110       case ArgType::kDouble:
111         argument_bytes =
112             EncodeFloat(static_cast<float>(va_arg(args, double)), output);
113         break;
114       case ArgType::kString:
115         argument_bytes = EncodeString(va_arg(args, const char*), output);
116         break;
117     }
118 
119     // If zero bytes were encoded, the encoding buffer is full.
120     if (argument_bytes == 0u) {
121       break;
122     }
123 
124     output = output.subspan(argument_bytes);
125     encoded_bytes += argument_bytes;
126 
127     arg_count -= 1;
128     types >>= 2;  // each argument type is encoded in two bits
129   }
130 
131   return encoded_bytes;
132 }
133 
134 }  // namespace tokenizer
135 }  // namespace pw
136