blob: e5b585cc3e709acf69db2241cd86081124d29cb2 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include "pw_polyfill/standard.h"
#include "pw_preprocessor/util.h"
#include "pw_tokenizer/internal/argument_types.h"
#include "pw_varint/varint.h"
#if PW_CXX_STANDARD_IS_SUPPORTED(17)
#include <cstring>
#include "pw_polyfill/standard.h"
#include "pw_span/span.h"
#include "pw_tokenizer/config.h"
#include "pw_tokenizer/tokenize.h"
namespace pw::tokenizer {
namespace internal {
// Returns the maximum encoded size of an argument of the specified type.
template <typename T>
constexpr size_t ArgEncodedSizeBytes() {
constexpr pw_tokenizer_ArgTypes kType = VarargsType<T>();
if constexpr (kType == PW_TOKENIZER_ARG_TYPE_DOUBLE) {
return sizeof(float);
} else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_STRING) {
return 1; // Size of the length byte only
} else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT64) {
return 10; // Max size of a varint-encoded 64-bit integer
} else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT) {
return sizeof(T) + 1; // Max size of zig-zag varint integer <= 32-bits
} else {
static_assert(sizeof(T) != sizeof(T), "Unsupported argument type");
}
}
} // namespace internal
/// Calculates the minimum buffer size to allocate that is guaranteed to support
/// encoding the specified arguments.
///
/// The contents of strings are NOT included in this total. The string's
/// length/status byte is guaranteed to fit, but the string contents may be
/// truncated. Encoding is considered to succeed as long as the string's
/// length/status byte is written, even if the actual string is truncated.
///
/// Examples:
///
/// - Message with no arguments:
/// `MinEncodingBufferSizeBytes() == 4`
/// - Message with an int argument
/// `MinEncodingBufferSizeBytes<int>() == 9 (4 + 5)`
template <typename... ArgTypes>
constexpr size_t MinEncodingBufferSizeBytes() {
return (sizeof(pw_tokenizer_Token) + ... +
internal::ArgEncodedSizeBytes<ArgTypes>());
}
/// Encodes a tokenized string's arguments to a buffer. The
/// @cpp_type{pw_tokenizer_ArgTypes} parameter specifies the argument types, in
/// place of a format string.
///
/// Most tokenization implementations should use the @cpp_class{EncodedMessage}
/// class.
size_t EncodeArgs(pw_tokenizer_ArgTypes types,
va_list args,
span<std::byte> output);
/// Encodes a tokenized message to a fixed size buffer. This class is used to
/// encode tokenized messages passed in from tokenization macros.
///
/// To use `pw::tokenizer::EncodedMessage`, construct it with the token,
/// argument types, and `va_list` from the variadic arguments:
///
/// @code{.cpp}
/// void SendLogMessage(span<std::byte> log_data);
///
/// extern "C" void TokenizeToSendLogMessage(pw_tokenizer_Token token,
/// pw_tokenizer_ArgTypes types,
/// ...) {
/// va_list args;
/// va_start(args, types);
/// EncodedMessage encoded_message(token, types, args);
/// va_end(args);
///
/// SendLogMessage(encoded_message); // EncodedMessage converts to span
/// }
/// @endcode
template <size_t kMaxSizeBytes>
class EncodedMessage {
public:
// Encodes a tokenized message to an internal buffer.
EncodedMessage(pw_tokenizer_Token token,
pw_tokenizer_ArgTypes types,
va_list args) {
std::memcpy(data_, &token, sizeof(token));
size_ =
sizeof(token) +
EncodeArgs(types, args, span<std::byte>(data_).subspan(sizeof(token)));
}
/// The binary-encoded tokenized message.
const std::byte* data() const { return data_; }
/// Returns `data()` as a pointer to `uint8_t` instead of `std::byte`.
const uint8_t* data_as_uint8() const {
return reinterpret_cast<const uint8_t*>(data());
}
/// The size of the encoded tokenized message in bytes.
size_t size() const { return size_; }
private:
static_assert(kMaxSizeBytes >= sizeof(pw_tokenizer_Token),
"The encoding buffer must be at least large enough for a token "
"(4 bytes)");
std::byte data_[kMaxSizeBytes];
size_t size_;
};
} // namespace pw::tokenizer
#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
PW_EXTERN_C_START
/// C function that encodes arguments to a tokenized buffer. Use the
/// @cpp_func{pw::tokenizer::EncodeArgs} function from C++.
size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
va_list args,
void* output_buffer,
size_t output_buffer_size);
/// Encodes an `int` with the standard integer encoding: zig-zag + LEB128.
/// This function is only necessary when manually encoding tokenized messages.
static inline size_t pw_tokenizer_EncodeInt(int value,
void* output,
size_t output_size_bytes) {
return pw_varint_Encode32(
pw_varint_ZigZagEncode32(value), output, output_size_bytes);
}
/// Encodes an `int64_t` with the standard integer encoding: zig-zag + LEB128.
/// This function is only necessary when manually encoding tokenized messages.
static inline size_t pw_tokenizer_EncodeInt64(int64_t value,
void* output,
size_t output_size_bytes) {
return pw_varint_Encode64(
pw_varint_ZigZagEncode64(value), output, output_size_bytes);
}
PW_EXTERN_C_END