blob: 93cd6c4cc002b8925e71a1e410011293715a2c46 [file] [log] [blame]
Wyatt Hepler6639c452020-05-06 11:43:07 -07001// 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
Wyatt Hepler7c8b3392021-04-05 18:00:24 -070015#include "pw_tokenizer/encode_args.h"
Wyatt Hepler6639c452020-05-06 11:43:07 -070016
17#include <algorithm>
18#include <cstring>
19
20#include "pw_preprocessor/compiler.h"
21#include "pw_varint/varint.h"
22
23namespace pw {
24namespace tokenizer {
25namespace {
26
Wyatt Hepler6639c452020-05-06 11:43:07 -070027// Declare the types as an enum for convenience.
28enum 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.
36static_assert(0b00u == static_cast<uint8_t>(ArgType::kInt));
37static_assert(0b01u == static_cast<uint8_t>(ArgType::kInt64));
38static_assert(0b10u == static_cast<uint8_t>(ArgType::kDouble));
39static_assert(0b11u == static_cast<uint8_t>(ArgType::kString));
40
Wyatt Heplerbcf07352021-04-05 14:44:30 -070041size_t EncodeInt(int value, const std::span<std::byte>& output) {
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070042 return varint::Encode(value, std::as_writable_bytes(output));
Wyatt Hepler6639c452020-05-06 11:43:07 -070043}
44
Wyatt Heplerbcf07352021-04-05 14:44:30 -070045size_t EncodeInt64(int64_t value, const std::span<std::byte>& output) {
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070046 return varint::Encode(value, std::as_writable_bytes(output));
Wyatt Hepler6639c452020-05-06 11:43:07 -070047}
48
Wyatt Heplerbcf07352021-04-05 14:44:30 -070049size_t EncodeFloat(float value, const std::span<std::byte>& output) {
Wyatt Hepler6639c452020-05-06 11:43:07 -070050 if (output.size() < sizeof(value)) {
51 return 0;
52 }
53 std::memcpy(output.data(), &value, sizeof(value));
54 return sizeof(value);
55}
56
Wyatt Heplerbcf07352021-04-05 14:44:30 -070057size_t EncodeString(const char* string, const std::span<std::byte>& output) {
Wyatt Hepler6639c452020-05-06 11:43:07 -070058 // 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;
Wyatt Heplerbcf07352021-04-05 14:44:30 -070074 std::byte overflow_bit = std::byte(0);
Wyatt Hepler6639c452020-05-06 11:43:07 -070075
76 while (string[bytes_to_copy] != '\0') {
77 if (bytes_to_copy == max_bytes) {
Wyatt Heplerbcf07352021-04-05 14:44:30 -070078 overflow_bit = std::byte('\x80');
Wyatt Hepler6639c452020-05-06 11:43:07 -070079 break;
80 }
81 bytes_to_copy += 1;
82 }
83
Wyatt Heplerbcf07352021-04-05 14:44:30 -070084 output[0] = static_cast<std::byte>(bytes_to_copy) | overflow_bit;
Wyatt Hepler6639c452020-05-06 11:43:07 -070085 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
Wyatt Heplerbcf07352021-04-05 14:44:30 -070092size_t EncodeArgs(pw_tokenizer_ArgTypes types,
Wyatt Hepler6639c452020-05-06 11:43:07 -070093 va_list args,
Wyatt Heplerbcf07352021-04-05 14:44:30 -070094 std::span<std::byte> output) {
Wyatt Hepler6639c452020-05-06 11:43:07 -070095 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