• 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 #pragma once
15 
16 #include <stdarg.h>
17 #include <stddef.h>
18 #include <stdint.h>
19 
20 #include "pw_polyfill/standard.h"
21 #include "pw_preprocessor/util.h"
22 #include "pw_tokenizer/internal/argument_types.h"
23 
24 #if PW_CXX_STANDARD_IS_SUPPORTED(17)
25 
26 #include <cstring>
27 
28 #include "pw_polyfill/standard.h"
29 #include "pw_span/span.h"
30 #include "pw_tokenizer/config.h"
31 #include "pw_tokenizer/tokenize.h"
32 
33 namespace pw::tokenizer {
34 namespace internal {
35 
36 // Returns the maximum encoded size of an argument of the specified type.
37 template <typename T>
ArgEncodedSizeBytes()38 constexpr size_t ArgEncodedSizeBytes() {
39   constexpr pw_tokenizer_ArgTypes kType = VarargsType<T>();
40   if constexpr (kType == PW_TOKENIZER_ARG_TYPE_DOUBLE) {
41     return sizeof(float);
42   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_STRING) {
43     return 1;  // Size of the length byte only
44   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT64) {
45     return 10;  // Max size of a varint-encoded 64-bit integer
46   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT) {
47     return sizeof(T) + 1;  // Max size of zig-zag varint integer <= 32-bits
48   } else {
49     static_assert(sizeof(T) != sizeof(T), "Unsupported argument type");
50   }
51 }
52 
53 }  // namespace internal
54 
55 /// Calculates the minimum buffer size to allocate that is guaranteed to support
56 /// encoding the specified arguments.
57 ///
58 /// The contents of strings are NOT included in this total. The string's
59 /// length/status byte is guaranteed to fit, but the string contents may be
60 /// truncated. Encoding is considered to succeed as long as the string's
61 /// length/status byte is written, even if the actual string is truncated.
62 ///
63 /// Examples:
64 ///
65 /// - Message with no arguments:
66 ///       `MinEncodingBufferSizeBytes() == 4`
67 /// - Message with an int argument
68 ///       `MinEncodingBufferSizeBytes<int>() == 9 (4 + 5)`
69 template <typename... ArgTypes>
MinEncodingBufferSizeBytes()70 constexpr size_t MinEncodingBufferSizeBytes() {
71   return (sizeof(pw_tokenizer_Token) + ... +
72           internal::ArgEncodedSizeBytes<ArgTypes>());
73 }
74 
75 /// Encodes a tokenized string's arguments to a buffer. The
76 /// @cpp_type{pw_tokenizer_ArgTypes} parameter specifies the argument types, in
77 /// place of a format string.
78 ///
79 /// Most tokenization implementations should use the @cpp_class{EncodedMessage}
80 /// class.
81 size_t EncodeArgs(pw_tokenizer_ArgTypes types,
82                   va_list args,
83                   span<std::byte> output);
84 
85 /// Encodes a tokenized message to a fixed size buffer. By default, the buffer
86 /// size is set by the @c_macro{PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES}
87 /// config macro. This class is used to encode tokenized messages passed in from
88 /// tokenization macros.
89 ///
90 /// To use `pw::tokenizer::EncodedMessage`, construct it with the token,
91 /// argument types, and `va_list` from the variadic arguments:
92 ///
93 /// @code{.cpp}
94 ///   void SendLogMessage(span<std::byte> log_data);
95 ///
96 ///   extern "C" void TokenizeToSendLogMessage(pw_tokenizer_Token token,
97 ///                                            pw_tokenizer_ArgTypes types,
98 ///                                            ...) {
99 ///     va_list args;
100 ///     va_start(args, types);
101 ///     EncodedMessage encoded_message(token, types, args);
102 ///     va_end(args);
103 ///
104 ///     SendLogMessage(encoded_message);  // EncodedMessage converts to span
105 ///   }
106 /// @endcode
107 template <size_t kMaxSizeBytes = PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES>
108 class EncodedMessage {
109  public:
110   // Encodes a tokenized message to an internal buffer.
EncodedMessage(pw_tokenizer_Token token,pw_tokenizer_ArgTypes types,va_list args)111   EncodedMessage(pw_tokenizer_Token token,
112                  pw_tokenizer_ArgTypes types,
113                  va_list args) {
114     std::memcpy(data_, &token, sizeof(token));
115     size_ =
116         sizeof(token) +
117         EncodeArgs(types, args, span<std::byte>(data_).subspan(sizeof(token)));
118   }
119 
120   /// The binary-encoded tokenized message.
data()121   const std::byte* data() const { return data_; }
122 
123   /// Returns `data()` as a pointer to `uint8_t` instead of `std::byte`.
data_as_uint8()124   const uint8_t* data_as_uint8() const {
125     return reinterpret_cast<const uint8_t*>(data());
126   }
127 
128   /// The size of the encoded tokenized message in bytes.
size()129   size_t size() const { return size_; }
130 
131  private:
132   static_assert(kMaxSizeBytes >= sizeof(pw_tokenizer_Token),
133                 "The encoding buffer must be at least large enough for a token "
134                 "(4 bytes)");
135 
136   std::byte data_[kMaxSizeBytes];
137   size_t size_;
138 };
139 
140 }  // namespace pw::tokenizer
141 
142 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(17)
143 
144 PW_EXTERN_C_START
145 
146 /// C function that encodes arguments to a tokenized buffer. Use the
147 /// @cpp_func{pw::tokenizer::EncodeArgs} function from C++.
148 size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
149                                va_list args,
150                                void* output_buffer,
151                                size_t output_buffer_size);
152 PW_EXTERN_C_END
153