blob: 0fdf96bc5f637a4902e1eb30bf6c57338518f853 [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.
// This header provides internal macros used by the tokenizer module.
#pragma once
#include <stdint.h>
#include "pw_preprocessor/arguments.h"
#include "pw_tokenizer/config.h"
// The size of the argument types variable determines the number of arguments
// supported in tokenized strings.
#if PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 4
#include "pw_tokenizer/internal/argument_types_macro_4_byte.h"
// Encoding types in a uint32_t supports 14 arguments with 2 bits per argument.
#define PW_TOKENIZER_MAX_SUPPORTED_ARGS 14
#define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 4u
#define PW_TOKENIZER_TYPE_COUNT_MASK 0x0Fu
typedef uint32_t pw_tokenizer_ArgTypes;
#elif PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 8
#include "pw_tokenizer/internal/argument_types_macro_8_byte.h"
// Encoding types in a uint64_t supports 29 arguments with 2 bits per argument.
#define PW_TOKENIZER_MAX_SUPPORTED_ARGS 29
#define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 6u
#define PW_TOKENIZER_TYPE_COUNT_MASK 0x1Fu // only 5 bits will be needed
typedef uint64_t pw_tokenizer_ArgTypes;
#else
#error "Unsupported value for PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES"
#endif // PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES
// The tokenized string encoding function is a variadic function that works
// similarly to printf. Instead of a format string, however, the argument types
// are packed into a pw_tokenizer_ArgTypes.
//
// The four supported argument types are represented by two-bit argument codes.
// Just four types are required because only printf-compatible arguments are
// supported, and variadic arguments are further converted to a more limited set
// of types.
//
// char* values cannot be printed as pointers with %p. These arguments are
// always encoded as strings. To format a char* as an address, cast it to void*
// or an integer.
#define PW_TOKENIZER_ARG_TYPE_INT ((pw_tokenizer_ArgTypes)0)
#define PW_TOKENIZER_ARG_TYPE_INT64 ((pw_tokenizer_ArgTypes)1)
#define PW_TOKENIZER_ARG_TYPE_DOUBLE ((pw_tokenizer_ArgTypes)2)
#define PW_TOKENIZER_ARG_TYPE_STRING ((pw_tokenizer_ArgTypes)3)
// Select the int argument type based on the size of the type. Values smaller
// than int are promoted to int.
#define _PW_TOKENIZER_SELECT_INT_TYPE(type) \
(sizeof(type) <= sizeof(int) ? PW_TOKENIZER_ARG_TYPE_INT \
: PW_TOKENIZER_ARG_TYPE_INT64)
// The _PW_VARARGS_TYPE macro selects the varargs-promoted type at compile time.
// The macro has to be different for C and C++ because C doesn't support
// templates and C++ doesn't support _Generic.
#ifdef __cplusplus
#include <type_traits>
#define _PW_VARARGS_TYPE(arg) ::pw::tokenizer::VarargsType<decltype(arg)>()
namespace pw {
namespace tokenizer {
#ifdef __cpp_if_constexpr // C++17 version
// This function selects the matching type enum for supported argument types.
template <typename T>
constexpr pw_tokenizer_ArgTypes VarargsType() {
using ArgType = std::decay_t<T>;
if constexpr (std::is_floating_point<ArgType>()) {
return PW_TOKENIZER_ARG_TYPE_DOUBLE;
} else if constexpr (!std::is_null_pointer<ArgType>() &&
std::is_convertible<ArgType, const char*>()) {
return PW_TOKENIZER_ARG_TYPE_STRING;
} else if constexpr (sizeof(ArgType) == sizeof(int64_t)) {
return PW_TOKENIZER_ARG_TYPE_INT64;
} else {
static_assert(sizeof(ArgType) <= sizeof(int));
return PW_TOKENIZER_ARG_TYPE_INT;
}
}
#else // C++14 version
template <typename T,
bool kIsDouble = std::is_floating_point<T>(),
bool kIsString = !std::is_null_pointer<T>() &&
std::is_convertible<T, const char*>(),
bool kIsInt64 = sizeof(T) == sizeof(int64_t)>
struct SelectVarargsType;
template <typename T, bool kDontCare1, bool kDontCare2>
struct SelectVarargsType<T, true, kDontCare1, kDontCare2> {
static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_DOUBLE;
};
template <typename T, bool kDontCare>
struct SelectVarargsType<T, false, true, kDontCare> {
static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_STRING;
};
template <typename T>
struct SelectVarargsType<T, false, false, true> {
static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT64;
};
template <typename T>
struct SelectVarargsType<T, false, false, false> {
static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT;
};
template <typename T>
constexpr pw_tokenizer_ArgTypes VarargsType() {
return SelectVarargsType<typename std::decay<T>::type>::kValue;
}
#endif // __cpp_if_constexpr
} // namespace tokenizer
} // namespace pw
#else // C version
// This uses a C11 _Generic to select the matching enum value for each supported
// argument type. _Generic evaluates to the expression matching the type of the
// provided expression at compile time.
// clang-format off
#define _PW_VARARGS_TYPE(arg) \
_Generic((arg), \
_Bool: PW_TOKENIZER_ARG_TYPE_INT, \
char: PW_TOKENIZER_ARG_TYPE_INT, \
signed char: PW_TOKENIZER_ARG_TYPE_INT, \
unsigned char: PW_TOKENIZER_ARG_TYPE_INT, \
signed short: PW_TOKENIZER_ARG_TYPE_INT, \
unsigned short: PW_TOKENIZER_ARG_TYPE_INT, \
signed int: PW_TOKENIZER_ARG_TYPE_INT, \
unsigned int: PW_TOKENIZER_ARG_TYPE_INT, \
signed long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long), \
unsigned long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long), \
signed long long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long long), \
unsigned long long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long long), \
float: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
long double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
char*: PW_TOKENIZER_ARG_TYPE_STRING, \
const char*: PW_TOKENIZER_ARG_TYPE_STRING, \
default: _PW_TOKENIZER_SELECT_INT_TYPE(void*))
// clang-format on
#endif // __cplusplus
// Encodes the types of the provided arguments as a pw_tokenizer_ArgTypes
// value. Depending on the size of pw_tokenizer_ArgTypes, the bottom 4 or 6
// bits store the number of arguments and the remaining bits store the types,
// two bits per type.
//
// The arguments are not evaluated; only their types are used to
// select the set their corresponding PW_TOKENIZER_ARG_TYPEs.
#define PW_TOKENIZER_ARG_TYPES(...) \
PW_DELEGATE_BY_ARG_COUNT(_PW_TOKENIZER_TYPES_, __VA_ARGS__)
#define _PW_TOKENIZER_TYPES_0() ((pw_tokenizer_ArgTypes)0)