// 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 #include #include // For isfinite(). #include #include #include // 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)" ( 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