594 lines
28 KiB
C
594 lines
28 KiB
C
// OpenCyphal common serialization support routines. -
|
|
// This file is based on canard_dsdl.h, which is part of Libcanard. | C/-
|
|
// -
|
|
// AUTOGENERATED, DO NOT EDIT.
|
|
//
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Language Options
|
|
// target_endianness: any
|
|
// omit_float_serialization_support: False
|
|
// enable_serialization_asserts: True
|
|
// enable_override_variable_array_capacity: False
|
|
// cast_format: (({type}) {value})
|
|
|
|
#ifndef NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED
|
|
#define NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED
|
|
|
|
#ifdef __cplusplus
|
|
# if (__cplusplus < 201402L)
|
|
# error "Unsupported language: ISO C11, C++14, or a newer version of either is required."
|
|
# endif
|
|
extern "C"
|
|
{
|
|
#else
|
|
# if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L)
|
|
# error "Unsupported language: ISO C11 or a newer version is required."
|
|
# endif
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <float.h>
|
|
#include <math.h> // For isfinite().
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <assert.h> // For static_assert (C11) and assert() if NUNAVUT_ASSERT is used.
|
|
|
|
static_assert(sizeof(size_t) >= sizeof(size_t),
|
|
"The bit-length type used by Nunavut, size_t, "
|
|
"is smaller than this platform's size_t type. "
|
|
"Nunavut serialization relies on size_t to size_t conversions "
|
|
"that do not lose data. You will need to regenerate Nunavut serialization support with a larger "
|
|
"unsigned_bit_length type specified.");
|
|
|
|
#define NUNAVUT_SUPPORT_LANGUAGE_OPTION_TARGET_ENDIANNESS 1693710260
|
|
#define NUNAVUT_SUPPORT_LANGUAGE_OPTION_OMIT_FLOAT_SERIALIZATION_SUPPORT 0
|
|
#define NUNAVUT_SUPPORT_LANGUAGE_OPTION_ENABLE_SERIALIZATION_ASSERTS 1
|
|
#define NUNAVUT_SUPPORT_LANGUAGE_OPTION_ENABLE_OVERRIDE_VARIABLE_ARRAY_CAPACITY 0
|
|
#define NUNAVUT_SUPPORT_LANGUAGE_OPTION_CAST_FORMAT 2368206204
|
|
|
|
/// Nunavut returns 0 for success and < 0 for any failure. It is always adequate to check that error_value < 0
|
|
/// to detect errors or error_value == 0 for success.
|
|
///
|
|
/// Nunavut serialization will never define more than 127 errors and the reserved error numbers are [-1,-127]
|
|
/// (-128 is not used). Error code 1 is currently also not used to avoid conflicts with 3rd-party software.
|
|
///
|
|
/// Return values > 0 for Nunavut serialization are undefined.
|
|
#define NUNAVUT_SUCCESS 0
|
|
// API usage errors:
|
|
#define NUNAVUT_ERROR_INVALID_ARGUMENT 2
|
|
#define NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL 3
|
|
// Invalid representation (caused by bad input data, not API misuse):
|
|
#define NUNAVUT_ERROR_REPRESENTATION_BAD_ARRAY_LENGTH 10
|
|
#define NUNAVUT_ERROR_REPRESENTATION_BAD_UNION_TAG 11
|
|
#define NUNAVUT_ERROR_REPRESENTATION_BAD_DELIMITER_HEADER 12
|
|
|
|
/// Detect whether the target platform is compatible with IEEE 754.
|
|
#define NUNAVUT_PLATFORM_IEEE754_FLOAT \
|
|
((FLT_RADIX == 2) && (FLT_MANT_DIG == 24) && (FLT_MIN_EXP == -125) && (FLT_MAX_EXP == 128))
|
|
#define NUNAVUT_PLATFORM_IEEE754_DOUBLE \
|
|
((FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MIN_EXP == -1021) && (DBL_MAX_EXP == 1024))
|
|
|
|
#ifndef NUNAVUT_ASSERT
|
|
// By default Nunavut does not generate assert statements since the logic to halt a program is platform
|
|
// dependent and because this header requires an absolute minimum from a platform and from the C standard library.
|
|
// Most platforms can simply define "NUNAVUT_ASSERT(x)=assert(x)" (<assert.h> is always included by Nunavut).
|
|
# error "You must either define NUNAVUT_ASSERT or you need to disable assertions" \
|
|
" when generating serialization support code using Nunavut language options"
|
|
#endif
|
|
|
|
// This code is endianness-invariant. Use target_endianness='little' to generate little-endian-optimized code.
|
|
|
|
// ---------------------------------------------------- HELPERS ----------------------------------------------------
|
|
|
|
/// Returns the smallest value.
|
|
static inline size_t nunavutChooseMin(const size_t a, const size_t b)
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
/// Calculate the number of bits to safely copy from/to a serialized buffer.
|
|
/// Mind the units! By convention, buffer size is specified in bytes, but fragment length and offset are in bits.
|
|
///
|
|
/// buffer buffer
|
|
/// origin end
|
|
/// [------------------------- buffer_size_bytes ------------------------]
|
|
/// [--------------- fragment_offset_bits ---------------][--- fragment_length_bits ---]
|
|
/// [-- out bits --]
|
|
///
|
|
static inline size_t nunavutSaturateBufferFragmentBitLength(
|
|
const size_t buffer_size_bytes, const size_t fragment_offset_bits, const size_t fragment_length_bits)
|
|
{
|
|
const size_t size_bits = (size_t)buffer_size_bytes * 8U;
|
|
const size_t tail_bits = size_bits - nunavutChooseMin(size_bits, fragment_offset_bits);
|
|
return nunavutChooseMin(fragment_length_bits, tail_bits);
|
|
}
|
|
|
|
// ---------------------------------------------------- BIT ARRAY ----------------------------------------------------
|
|
|
|
/// Copy the specified number of bits from the source buffer into the destination buffer in accordance with the
|
|
/// DSDL bit-level serialization specification. The offsets may be arbitrary (may exceed 8 bits).
|
|
/// If both offsets are byte-aligned, the function invokes memmove() and possibly adjusts the last byte separately.
|
|
/// If the source and the destination overlap AND the offsets are not byte-aligned, the behavior is undefined.
|
|
/// If either source or destination pointers are NULL, the behavior is undefined.
|
|
/// Arguments:
|
|
/// dst Destination buffer. Shall be at least ceil(length_bits/8) bytes large.
|
|
/// dst_offset_bits Offset in bits from the destination pointer. May exceed 8.
|
|
/// length_bits The number of bits to copy. Both source and destination shall be large enough.
|
|
/// src Source buffer. Shall be at least ceil(length_bits/8) bytes large.
|
|
/// src_offset_bits Offset in bits from the source pointer. May exceed 8.
|
|
static inline void nunavutCopyBits(void* const dst,
|
|
const size_t dst_offset_bits,
|
|
const size_t length_bits,
|
|
const void* const src,
|
|
const size_t src_offset_bits)
|
|
{
|
|
NUNAVUT_ASSERT(src != NULL);
|
|
NUNAVUT_ASSERT(dst != NULL);
|
|
NUNAVUT_ASSERT(src != dst);
|
|
if ((0U == (src_offset_bits % 8U)) && (0U == (dst_offset_bits % 8U))) // Aligned copy, optimized, most common case.
|
|
{
|
|
const size_t length_bytes = (size_t)(length_bits / 8U);
|
|
// Intentional violation of MISRA: Pointer arithmetics. This is done to remove the API constraint that
|
|
// offsets be under 8 bits. Fewer constraints reduce the chance of API misuse.
|
|
const uint8_t* const psrc = (src_offset_bits / 8U) + (const uint8_t*) src; // NOSONAR NOLINT
|
|
uint8_t* const pdst = (dst_offset_bits / 8U) + (uint8_t*) dst; // NOSONAR NOLINT
|
|
(void) memmove(pdst, psrc, length_bytes);
|
|
const uint8_t length_mod = (uint8_t)(length_bits % 8U);
|
|
if (0U != length_mod) // If the length is unaligned, the last byte requires special treatment.
|
|
{
|
|
// Intentional violation of MISRA: Pointer arithmetics. It is unavoidable in this context.
|
|
const uint8_t* const last_src = psrc + length_bytes; // NOLINT NOSONAR
|
|
uint8_t* const last_dst = pdst + length_bytes; // NOLINT NOSONAR
|
|
NUNAVUT_ASSERT(length_mod < 8U);
|
|
const uint8_t mask = (uint8_t)((1U << length_mod) - 1U);
|
|
*last_dst = (*last_dst & (uint8_t)~mask) | (*last_src & mask);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The algorithm was originally designed by Ben Dyer for Libuavcan v0:
|
|
// https://github.com/OpenCyphal/libuavcan/blob/legacy-v0/libuavcan/src/marshal/uc_bit_array_copy.cpp
|
|
// This version is modified for v1 where the bit order is the opposite.
|
|
const uint8_t* const psrc = (const uint8_t*) src;
|
|
uint8_t* const pdst = (uint8_t*) dst;
|
|
size_t src_off = src_offset_bits;
|
|
size_t dst_off = dst_offset_bits;
|
|
const size_t last_bit = src_off + length_bits;
|
|
NUNAVUT_ASSERT(((psrc < pdst) ? ((uintptr_t)(psrc + ((src_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)pdst) : 1));
|
|
NUNAVUT_ASSERT(((psrc > pdst) ? ((uintptr_t)(pdst + ((dst_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)psrc) : 1));
|
|
while (last_bit > src_off)
|
|
{
|
|
const uint8_t src_mod = (uint8_t)(src_off % 8U);
|
|
const uint8_t dst_mod = (uint8_t)(dst_off % 8U);
|
|
const uint8_t max_mod = (src_mod > dst_mod) ? src_mod : dst_mod;
|
|
const uint8_t size = (uint8_t) nunavutChooseMin(8U - max_mod, last_bit - src_off);
|
|
NUNAVUT_ASSERT(size > 0U);
|
|
NUNAVUT_ASSERT(size <= 8U);
|
|
// Suppress a false warning from Clang-Tidy & Sonar that size is being over-shifted. It's not.
|
|
const uint8_t mask = (uint8_t)((((1U << size) - 1U) << dst_mod) & 0xFFU); // NOLINT NOSONAR
|
|
NUNAVUT_ASSERT(mask > 0U);
|
|
// Intentional violation of MISRA: indexing on a pointer.
|
|
// This simplifies the implementation greatly and avoids pointer arithmetics.
|
|
const uint8_t in = (uint8_t)((uint8_t)(psrc[src_off / 8U] >> src_mod) << dst_mod) & 0xFFU; // NOSONAR
|
|
// Intentional violation of MISRA: indexing on a pointer.
|
|
// This simplifies the implementation greatly and avoids pointer arithmetics.
|
|
const uint8_t a = pdst[dst_off / 8U] & ((uint8_t) ~mask); // NOSONAR
|
|
const uint8_t b = in & mask;
|
|
// Intentional violation of MISRA: indexing on a pointer.
|
|
// This simplifies the implementation greatly and avoids pointer arithmetics.
|
|
pdst[dst_off / 8U] = a | b; // NOSONAR
|
|
src_off += size;
|
|
dst_off += size;
|
|
}
|
|
NUNAVUT_ASSERT(last_bit == src_off);
|
|
}
|
|
}
|
|
|
|
/// This function is intended for deserialization of contiguous sequences of zero-cost primitives.
|
|
/// It extracts (len_bits) bits that are offset by (off_bits) from the origin of (buf) whose size is (buf_size_bytes).
|
|
/// If the requested (len_bits+off_bits) overruns the buffer, the missing bits are implicitly zero-extended.
|
|
/// If (len_bits % 8 != 0), the output buffer is right-zero-padded up to the next byte boundary.
|
|
/// If (off_bits % 8 == 0), the operation is delegated to memmove(); otherwise, a much slower unaligned bit copy
|
|
/// algorithm is employed. See @ref nunavutCopyBits() for further details.
|
|
static inline void nunavutGetBits(void* const output,
|
|
const void* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const size_t len_bits)
|
|
{
|
|
NUNAVUT_ASSERT(output != NULL);
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
const size_t sat_bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, len_bits);
|
|
// Apply implicit zero extension. Normally, this is a no-op unless (len_bits > sat_bits) or (len_bits % 8 != 0).
|
|
// The former case ensures that if we're copying <8 bits, the MSB in the destination will be zeroed out.
|
|
(void) memset(((uint8_t*)output) + (sat_bits / 8U), 0, ((len_bits + 7U) / 8U) - (sat_bits / 8U));
|
|
nunavutCopyBits(output, 0U, sat_bits, buf, off_bits);
|
|
}
|
|
|
|
// ---------------------------------------------------- INTEGER ----------------------------------------------------
|
|
|
|
/// Serialize a DSDL field value at the specified bit offset from the beginning of the destination buffer.
|
|
/// The behavior is undefined if the input pointer is NULL. The time complexity is linear of the bit length.
|
|
/// One-bit-wide signed integers are processed without raising an error but the result is unspecified.
|
|
///
|
|
/// Arguments:
|
|
/// buf Destination buffer where the result will be stored.
|
|
/// buf_size_bytes Size of the above, in bytes.
|
|
/// off_bits Offset, in bits, from the beginning of the buffer. May exceed one byte.
|
|
/// value The value itself (in case of integers it is promoted to 64-bit for unification).
|
|
/// len_bits Length of the serialized representation, in bits. Zero has no effect. Values >64 bit saturated.
|
|
|
|
static inline int8_t nunavutSetBit(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const bool value)
|
|
{
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
if ((buf_size_bytes * 8) <= off_bits)
|
|
{
|
|
return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL;
|
|
}
|
|
const uint8_t val = value ? 1U : 0U;
|
|
nunavutCopyBits(buf, off_bits, 1U, &val, 0U);
|
|
return NUNAVUT_SUCCESS;
|
|
}
|
|
|
|
static inline int8_t nunavutSetUxx(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint64_t value,
|
|
const uint8_t len_bits)
|
|
{
|
|
static_assert(64U == (sizeof(uint64_t) * 8U), "Unexpected size of uint64_t");
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
if ((buf_size_bytes * 8) < (off_bits + len_bits))
|
|
{
|
|
return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL;
|
|
}
|
|
const size_t saturated_len_bits = nunavutChooseMin(len_bits, 64U);
|
|
const uint8_t tmp[sizeof(uint64_t)] = {
|
|
(uint8_t)((value >> 0U) & 0xFFU),
|
|
(uint8_t)((value >> 8U) & 0xFFU),
|
|
(uint8_t)((value >> 16U) & 0xFFU),
|
|
(uint8_t)((value >> 24U) & 0xFFU),
|
|
(uint8_t)((value >> 32U) & 0xFFU),
|
|
(uint8_t)((value >> 40U) & 0xFFU),
|
|
(uint8_t)((value >> 48U) & 0xFFU),
|
|
(uint8_t)((value >> 56U) & 0xFFU),
|
|
};
|
|
nunavutCopyBits(buf, off_bits, saturated_len_bits, &tmp[0], 0U);
|
|
return NUNAVUT_SUCCESS;
|
|
}
|
|
|
|
static inline int8_t nunavutSetIxx(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const int64_t value,
|
|
const uint8_t len_bits)
|
|
{
|
|
// The naive sign conversion is safe and portable according to the C standard:
|
|
// 6.3.1.3.3: if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more
|
|
// than the maximum value that can be represented in the new type until the value is in the range of the new type.
|
|
return nunavutSetUxx(buf, buf_size_bytes, off_bits, (uint64_t) value, len_bits);
|
|
}
|
|
|
|
/// Deserialize a DSDL field value located at the specified bit offset from the beginning of the source buffer.
|
|
/// If the deserialized value extends beyond the end of the buffer, the missing bits are taken as zero, as required
|
|
/// by the DSDL specification (see Implicit Zero Extension Rule, IZER).
|
|
///
|
|
/// If len_bits is greater than the return type, extra bits will be truncated per standard narrowing conversion rules.
|
|
/// If len_bits is shorter than the return type, missing bits will be zero per standard integer promotion rules.
|
|
/// Essentially, for integers, it would be enough to have 64-bit versions only; narrower variants exist only to avoid
|
|
/// narrowing type conversions of the result and for some performance gains.
|
|
///
|
|
/// The behavior is undefined if the input pointer is NULL. The time complexity is linear of the bit length.
|
|
/// One-bit-wide signed integers are processed without raising an error but the result is unspecified.
|
|
///
|
|
/// Arguments:
|
|
/// buf Source buffer where the serialized representation will be read from.
|
|
/// buf_size_bytes The size of the source buffer, in bytes. Reads past this limit will return zero bits.
|
|
/// off_bits Offset, in bits, from the beginning of the buffer. May exceed one byte.
|
|
/// len_bits Length of the serialized representation, in bits. Zero returns 0. Out-of-range values saturated.
|
|
|
|
static inline uint8_t nunavutGetU8(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits);
|
|
|
|
static inline bool nunavutGetBit(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits)
|
|
{
|
|
return 1U == nunavutGetU8(buf, buf_size_bytes, off_bits, 1U);
|
|
}
|
|
|
|
static inline uint8_t nunavutGetU8(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 8U));
|
|
NUNAVUT_ASSERT(bits <= (sizeof(uint8_t) * 8U));
|
|
uint8_t val = 0;
|
|
nunavutCopyBits(&val, 0U, bits, buf, off_bits);
|
|
return val;
|
|
}
|
|
|
|
static inline uint16_t nunavutGetU16(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 16U));
|
|
NUNAVUT_ASSERT(bits <= (sizeof(uint16_t) * 8U));
|
|
uint8_t tmp[sizeof(uint16_t)] = {0};
|
|
nunavutCopyBits(&tmp[0], 0U, bits, buf, off_bits);
|
|
return (uint16_t)(tmp[0] | (uint16_t)(((uint16_t) tmp[1]) << 8U));
|
|
}
|
|
|
|
static inline uint32_t nunavutGetU32(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 32U));
|
|
NUNAVUT_ASSERT(bits <= (sizeof(uint32_t) * 8U));
|
|
uint8_t tmp[sizeof(uint32_t)] = {0};
|
|
nunavutCopyBits(&tmp[0], 0U, bits, buf, off_bits);
|
|
return (uint32_t)(tmp[0] | ((uint32_t) tmp[1] << 8U) | ((uint32_t) tmp[2] << 16U) | ((uint32_t) tmp[3] << 24U));
|
|
}
|
|
|
|
static inline uint64_t nunavutGetU64(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
NUNAVUT_ASSERT(buf != NULL);
|
|
const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 64U));
|
|
NUNAVUT_ASSERT(bits <= (sizeof(uint64_t) * 8U));
|
|
uint8_t tmp[sizeof(uint64_t)] = {0};
|
|
nunavutCopyBits(&tmp[0], 0U, bits, buf, off_bits);
|
|
return (uint64_t)(tmp[0] |
|
|
((uint64_t) tmp[1] << 8U) |
|
|
((uint64_t) tmp[2] << 16U) |
|
|
((uint64_t) tmp[3] << 24U) |
|
|
((uint64_t) tmp[4] << 32U) |
|
|
((uint64_t) tmp[5] << 40U) |
|
|
((uint64_t) tmp[6] << 48U) |
|
|
((uint64_t) tmp[7] << 56U));
|
|
}
|
|
|
|
static inline int8_t nunavutGetI8(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 8U);
|
|
uint8_t val = nunavutGetU8(buf, buf_size_bytes, off_bits, sat);
|
|
const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U);
|
|
val = ((sat < 8U) && neg) ? (uint8_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension
|
|
return neg ? (int8_t)((-(int8_t)(uint8_t) ~val) - 1) : (int8_t) val;
|
|
}
|
|
|
|
static inline int16_t nunavutGetI16(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 16U);
|
|
uint16_t val = nunavutGetU16(buf, buf_size_bytes, off_bits, sat);
|
|
const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U);
|
|
val = ((sat < 16U) && neg) ? (uint16_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension
|
|
return neg ? (int16_t)((-(int16_t)(uint16_t) ~val) - 1) : (int16_t) val;
|
|
}
|
|
|
|
static inline int32_t nunavutGetI32(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 32U);
|
|
uint32_t val = nunavutGetU32(buf, buf_size_bytes, off_bits, sat);
|
|
const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U);
|
|
val = ((sat < 32U) && neg) ? (uint32_t)(val | ~((1UL << sat) - 1U)) : val; // Sign extension
|
|
return neg ? (int32_t)((-(int32_t) ~val) - 1) : (int32_t) val;
|
|
}
|
|
|
|
static inline int64_t nunavutGetI64(const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const uint8_t len_bits)
|
|
{
|
|
const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 64U);
|
|
uint64_t val = nunavutGetU64(buf, buf_size_bytes, off_bits, sat);
|
|
const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U);
|
|
val = ((sat < 64U) && neg) ? (uint64_t)(val | ~((1ULL << sat) - 1U)) : val; // Sign extension
|
|
return neg ? (int64_t)((-(int64_t) ~val) - 1) : (int64_t) val;
|
|
}
|
|
|
|
// ---------------------------------------------------- FLOAT16 ----------------------------------------------------
|
|
|
|
static_assert(NUNAVUT_PLATFORM_IEEE754_FLOAT,
|
|
"The target platform does not support IEEE754 floating point operations.");
|
|
static_assert(32U == (sizeof(float) * 8U), "Unsupported floating point model");
|
|
|
|
/// Converts a single-precision float into the binary representation of the value as a half-precision IEEE754 value.
|
|
static inline uint16_t nunavutFloat16Pack(const float value)
|
|
{
|
|
typedef union // NOSONAR
|
|
{
|
|
uint32_t bits;
|
|
float real;
|
|
} Float32Bits;
|
|
|
|
// The no-lint statements suppress the warning about the use of union. This is required for low-level bit access.
|
|
const uint32_t round_mask = ~(uint32_t) 0x0FFFU;
|
|
Float32Bits f32inf; // NOSONAR
|
|
Float32Bits f16inf; // NOSONAR
|
|
Float32Bits magic; // NOSONAR
|
|
Float32Bits in; // NOSONAR
|
|
f32inf.bits = ((uint32_t) 255U) << 23U;
|
|
f16inf.bits = ((uint32_t) 31U) << 23U;
|
|
magic.bits = ((uint32_t) 15U) << 23U;
|
|
in.real = value;
|
|
const uint32_t sign = in.bits & (((uint32_t) 1U) << 31U);
|
|
in.bits ^= sign;
|
|
uint16_t out = 0;
|
|
if (in.bits >= f32inf.bits)
|
|
{
|
|
if ((in.bits & 0x7FFFFFUL) != 0)
|
|
{
|
|
out = 0x7E00U;
|
|
}
|
|
else
|
|
{
|
|
out = (in.bits > f32inf.bits) ? (uint16_t) 0x7FFFU : (uint16_t) 0x7C00U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
in.bits &= round_mask;
|
|
in.real *= magic.real;
|
|
in.bits -= round_mask;
|
|
if (in.bits > f16inf.bits)
|
|
{
|
|
in.bits = f16inf.bits;
|
|
}
|
|
out = (uint16_t)(in.bits >> 13U);
|
|
}
|
|
out |= (uint16_t)(sign >> 16U);
|
|
return out;
|
|
}
|
|
|
|
static inline float nunavutFloat16Unpack(const uint16_t value)
|
|
{
|
|
typedef union // NOSONAR
|
|
{
|
|
uint32_t bits;
|
|
float real;
|
|
} Float32Bits;
|
|
|
|
// The no-lint statements suppress the warning about the use of union. This is required for low-level bit access.
|
|
Float32Bits magic; // NOSONAR
|
|
Float32Bits inf_nan; // NOSONAR
|
|
Float32Bits out; // NOSONAR
|
|
magic.bits = ((uint32_t) 0xEFU) << 23U;
|
|
inf_nan.bits = ((uint32_t) 0x8FU) << 23U;
|
|
out.bits = ((uint32_t)(value & 0x7FFFU)) << 13U;
|
|
out.real *= magic.real;
|
|
if (out.real >= inf_nan.real)
|
|
{
|
|
out.bits |= ((uint32_t) 0xFFU) << 23U;
|
|
}
|
|
out.bits |= ((uint32_t)(value & 0x8000U)) << 16U;
|
|
return out.real;
|
|
}
|
|
|
|
static inline int8_t nunavutSetF16(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const float value)
|
|
{
|
|
return nunavutSetUxx(buf, buf_size_bytes, off_bits, nunavutFloat16Pack(value), 16U);
|
|
}
|
|
|
|
static inline float nunavutGetF16(
|
|
const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits)
|
|
{
|
|
return nunavutFloat16Unpack(nunavutGetU16(buf, buf_size_bytes, off_bits, 16U));
|
|
}
|
|
|
|
// ---------------------------------------------------- FLOAT32 ----------------------------------------------------
|
|
|
|
static_assert(NUNAVUT_PLATFORM_IEEE754_FLOAT,
|
|
"The target platform does not support IEEE754 floating point operations.");
|
|
static_assert(32U == (sizeof(float) * 8U), "Unsupported floating point model");
|
|
|
|
static inline int8_t nunavutSetF32(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const float value)
|
|
{
|
|
// Intentional violation of MISRA: use union to perform fast conversion from an IEEE 754-compatible native
|
|
// representation into a serializable integer. The assumptions about the target platform properties are made
|
|
// clear. In the future we may add a more generic conversion that is platform-invariant.
|
|
union // NOSONAR
|
|
{
|
|
float fl;
|
|
uint32_t in;
|
|
} const tmp = {value}; // NOSONAR
|
|
return nunavutSetUxx(buf, buf_size_bytes, off_bits, tmp.in, sizeof(tmp) * 8U);
|
|
}
|
|
|
|
static inline float nunavutGetF32(
|
|
const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits)
|
|
{
|
|
// Intentional violation of MISRA: use union to perform fast conversion to an IEEE 754-compatible native
|
|
// representation into a serializable integer. The assumptions about the target platform properties are made
|
|
// clear. In the future we may add a more generic conversion that is platform-invariant.
|
|
union // NOSONAR
|
|
{
|
|
uint32_t in;
|
|
float fl;
|
|
} const tmp = {nunavutGetU32(buf, buf_size_bytes, off_bits, 32U)};
|
|
return tmp.fl;
|
|
}
|
|
|
|
// ---------------------------------------------------- FLOAT64 ----------------------------------------------------
|
|
|
|
static_assert(NUNAVUT_PLATFORM_IEEE754_DOUBLE,
|
|
"The target platform does not support IEEE754 double-precision floating point operations.");
|
|
static_assert(64U == (sizeof(double) * 8U), "Unsupported floating point model");
|
|
|
|
static inline int8_t nunavutSetF64(
|
|
uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits,
|
|
const double value)
|
|
{
|
|
// Intentional violation of MISRA: use union to perform fast conversion from an IEEE 754-compatible native
|
|
// representation into a serializable integer. The assumptions about the target platform properties are made
|
|
// clear. In the future we may add a more generic conversion that is platform-invariant.
|
|
union // NOSONAR
|
|
{
|
|
double fl;
|
|
uint64_t in;
|
|
} const tmp = {value}; // NOSONAR
|
|
return nunavutSetUxx(buf, buf_size_bytes, off_bits, tmp.in, sizeof(tmp) * 8U);
|
|
}
|
|
|
|
static inline double nunavutGetF64(
|
|
const uint8_t* const buf,
|
|
const size_t buf_size_bytes,
|
|
const size_t off_bits)
|
|
{
|
|
// Intentional violation of MISRA: use union to perform fast conversion to an IEEE 754-compatible native
|
|
// representation into a serializable integer. The assumptions about the target platform properties are made
|
|
// clear. In the future we may add a more generic conversion that is platform-invariant.
|
|
union // NOSONAR
|
|
{
|
|
uint64_t in;
|
|
double fl;
|
|
} const tmp = {nunavutGetU64(buf, buf_size_bytes, off_bits, 64U)};
|
|
return tmp.fl;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED
|