Skip to content

Commit

Permalink
Looks like there's something else going on
Browse files Browse the repository at this point in the history
  • Loading branch information
thirtytwobits committed Jun 5, 2024
1 parent bb8ff57 commit 86757b1
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 54 deletions.
28 changes: 18 additions & 10 deletions src/nunavut/lang/c/support/serialization.j2
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ static inline void nunavutCopyBits(void* const dst,
{{ typename_unsigned_bit_length }} dst_off = dst_offset_bits;
const {{ typename_unsigned_bit_length }} last_bit = src_off + length_bits;
{{ assert(
'((psrc < pdst) ? ((uintptr_t)(psrc + ((src_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)pdst) : 1)'
'((psrc < pdst) ? ((uintptr_t)(psrc + ((src_offset_bits + length_bits + 7U) / 8U)) <= (uintptr_t)pdst) : 1)'
) }}
{{ assert(
'((psrc > pdst) ? ((uintptr_t)(pdst + ((dst_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)psrc) : 1)'
'((psrc > pdst) ? ((uintptr_t)(pdst + ((dst_offset_bits + length_bits + 7U) / 8U)) <= (uintptr_t)psrc) : 1)'
) }}
while (last_bit > src_off)
{
Expand Down Expand Up @@ -363,9 +363,9 @@ static inline uint8_t nunavutGetU8(const uint8_t* const buf,
const {{ typename_unsigned_bit_length }} bits = {# -#}
nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 8U));
{{ assert('bits <= (sizeof(uint8_t) * 8U)') }}
uint8_t val = 0;
nunavutCopyBits(&val, 0U, bits, buf, off_bits);
return val;
uint8_t val[2] = {0};
nunavutCopyBits(&val[0], 0U, bits, buf, off_bits);
return val[0];
}

static inline uint16_t nunavutGetU16(const uint8_t* const buf,
Expand All @@ -382,7 +382,7 @@ static inline uint16_t nunavutGetU16(const uint8_t* const buf,
nunavutCopyBits(&val, 0U, bits, buf, off_bits);
return val;
{%- elif options.target_endianness in ('any', 'big') %}
uint8_t tmp[sizeof(uint16_t)] = {0};
uint8_t tmp[sizeof(uint16_t) + 1] = {0};
nunavutCopyBits(&tmp[0], 0U, bits, buf, off_bits);
return (uint16_t)(tmp[0] | (uint16_t)(((uint16_t) tmp[1]) << 8U));
{%- else %}{%- assert False %}
Expand All @@ -403,7 +403,7 @@ static inline uint32_t nunavutGetU32(const uint8_t* const buf,
nunavutCopyBits(&val, 0U, bits, buf, off_bits);
return val;
{%- elif options.target_endianness in ('any', 'big') %}
uint8_t tmp[sizeof(uint32_t)] = {0};
uint8_t tmp[sizeof(uint32_t) + 1] = {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));
{%- else %}{%- assert False %}
Expand All @@ -424,7 +424,7 @@ static inline uint64_t nunavutGetU64(const uint8_t* const buf,
nunavutCopyBits(&val, 0U, bits, buf, off_bits);
return val;
{%- elif options.target_endianness in ('any', 'big') %}
uint8_t tmp[sizeof(uint64_t)] = {0};
uint8_t tmp[sizeof(uint64_t) + 1] = {0};
nunavutCopyBits(&tmp[0], 0U, bits, buf, off_bits);
return (uint64_t)(tmp[0] |
((uint64_t) tmp[1] << 8U) |
Expand All @@ -446,7 +446,11 @@ static inline int8_t nunavutGetI8(const uint8_t* const buf,
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
const uint8_t r = ((sat < 8U) && neg);
if (r)
{
val = (uint8_t)(val | ~((1U << sat) - 1U));
}
return neg ? (int8_t)((-(int8_t)(uint8_t) ~val) - 1) : (int8_t) val;
}

Expand All @@ -458,7 +462,11 @@ static inline int16_t nunavutGetI16(const uint8_t* const buf,
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
const uint16_t r = ((sat < 16U) && neg);
if (r)
{
val = (uint16_t)(val | ~((1U << sat) - 1U));
}
return neg ? (int16_t)((-(int16_t)(uint16_t) ~val) - 1) : (int16_t) val;
}

Expand Down
54 changes: 31 additions & 23 deletions verification/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ else()
set(NUNAVUT_VERIFICATION_LANG "unspecified" CACHE STRING "The Nunavut output language to verify.")
endif()

if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp")
set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/cpp")
message(STATUS "NUNAVUT_VERIFICATION_LANG is C++ (${NUNAVUT_VERIFICATION_LANG})")
message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}")
elseif(NUNAVUT_VERIFICATION_LANG STREQUAL "c")
set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/c")
message(STATUS "NUNAVUT_VERIFICATION_LANG is C (${NUNAVUT_VERIFICATION_LANG})")
message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}")
else()
message(FATAL_ERROR "Unknown or no verification language (${NUNAVUT_VERIFICATION_LANG}). Try cmake -DNUNAVUT_VERIFICATION_LANG:string=[cpp|c]")
endif()

string(TOLOWER ${NUNAVUT_VERIFICATION_LANG} LOCAL_VERIFICATION_LANG)

set(NUNAVUT_SUBMODULES_ROOT "${NUNAVUT_PROJECT_ROOT}/submodules" CACHE STRING "The path to git submodules for the project.")

if(NOT DEFINED NUNAVUT_VERIFICATION_LANG_STANDARD)
Expand All @@ -47,6 +61,12 @@ if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp" AND NOT NUNAVUT_VERIFICATION_LANG_S
set(NUNAVUT_VERIFICATION_LANG_STANDARD "c++20")
endif()

if (NUNAVUT_VERIFICATION_LANG STREQUAL "c" AND NOT NUNAVUT_VERIFICATION_LANG_STANDARD)
set(NUNAVUT_VERIFICATION_LANG_STANDARD "c11")
endif()

message(STATUS "NUNAVUT_VERIFICATION_LANG_STANDARD is ${NUNAVUT_VERIFICATION_LANG_STANDARD}")

if(NOT DEFINED NUNAVUT_VERIFICATION_TARGET_ENDIANNESS)
set(NUNAVUT_VERIFICATION_TARGET_ENDIANNESS "any" CACHE STRING "The endianess for the target verification architecture.")
endif()
Expand All @@ -63,20 +83,6 @@ if(NOT DEFINED NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE)
set(NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE OFF CACHE BOOL "Enable or disable override variable array capacity in generated support code.")
endif()

if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp")
set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/cpp")
message(STATUS "NUNAVUT_VERIFICATION_LANG is C++ (${NUNAVUT_VERIFICATION_LANG})")
message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}")
elseif(NUNAVUT_VERIFICATION_LANG STREQUAL "c")
set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/c")
message(STATUS "NUNAVUT_VERIFICATION_LANG is C (${NUNAVUT_VERIFICATION_LANG})")
message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}")
else()
message(FATAL_ERROR "Unknown or no verification language (${NUNAVUT_VERIFICATION_LANG}). Try cmake -DNUNAVUT_VERIFICATION_LANG:string=[cpp|c]")
endif()

string(TOLOWER ${NUNAVUT_VERIFICATION_LANG} LOCAL_VERIFICATION_LANG)

if(DEFINED ENV{NUNAVUT_FLAGSET})
set(NUNAVUT_FLAGSET "$ENV{NUNAVUT_FLAGSET}")
message(STATUS "Using ${NUNAVUT_FLAGSET} from environment for NUNAVUT_FLAGSET")
Expand Down Expand Up @@ -380,7 +386,7 @@ function(runTestCpp)
target_include_directories(${NATIVE_TEST_NAME} PUBLIC "${NUNAVUT_PROJECT_ROOT}/submodules/CETL/include")
define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*)
define_natve_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}")
list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov")
list(APPEND ALL_TEST_COVERAGE "--add-tracefile")
Expand All @@ -402,21 +408,21 @@ endif()

function(runTestC)
set(options "")
set(oneValueArgs TEST_FILE)
set(oneValueArgs TEST_FILE FRAMEWORK)
set(multiValueArgs LINK LANGUAGE_FLAVORS)
cmake_parse_arguments(runTestC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

# Skip tests not relevant to the specified language standard
list(FIND runTestC_LANGUAGE_FLAVORS "${NUNAVUT_VERIFICATION_LANG_STANDARD}" FIND_INDEX)
if (${FIND_INDEX} GREATER -1)
if (${FIND_INDEX} EQUAL -1)
message(STATUS "Skipping ${runTestC_TEST_FILE}")
return()
endif()

set(NATIVE_TEST "${NUNAVUT_VERIFICATION_ROOT}/suite/${runTestC_TEST_FILE}")
get_filename_component(NATIVE_TEST_NAME ${NATIVE_TEST} NAME_WE)

define_native_unit_test(FRAMEWORK "unity"
define_native_unit_test(FRAMEWORK ${runTestC_FRAMEWORK}
TEST_NAME ${NATIVE_TEST_NAME}
TEST_SOURCE ${NATIVE_TEST}
OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR}
Expand All @@ -425,7 +431,7 @@ function(runTestC)
)
define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*)
define_natve_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR})
list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}")
list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov")
list(APPEND ALL_TEST_COVERAGE "--add-tracefile")
Expand All @@ -437,10 +443,12 @@ endfunction()

if (NUNAVUT_VERIFICATION_LANG STREQUAL "c")
runTestCpp(TEST_FILE test_canard.cpp LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestC( TEST_FILE test_constant.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestC( TEST_FILE test_override_variable_array_capacity.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestC( TEST_FILE test_serialization.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestC( TEST_FILE test_support.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestCpp(TEST_FILE test_support_assert.cpp LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11)
runTestC( TEST_FILE test_constant.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11 FRAMEWORK "unity")
runTestC( TEST_FILE test_override_variable_array_capacity.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11 FRAMEWORK "unity")
runTestC( TEST_FILE test_serialization.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11 FRAMEWORK "unity")
runTestC( TEST_FILE test_support.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11 FRAMEWORK "unity")
runTestC( TEST_FILE test_simple.c LINK dsdl-regulated dsdl-test LANGUAGE_FLAVORS c11 FRAMEWORK "none")
endif()

# +---------------------------------------------------------------------------+
Expand Down
134 changes: 134 additions & 0 deletions verification/c/suite/test_simple.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) 2020 OpenCyphal Development Team.
// This software is distributed under the terms of the MIT License.

#include <regulated/basics/Struct__0_1.h>
#include <regulated/basics/Union_0_1.h>
#include <regulated/basics/Primitive_0_1.h>
#include <regulated/basics/PrimitiveArrayFixed_0_1.h>
#include <regulated/basics/PrimitiveArrayVariable_0_1.h>
#include <regulated/delimited/A_1_0.h>
#include <regulated/delimited/A_1_1.h>
#include <uavcan/pnp/NodeIDAllocationData_2_0.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

#define TEST_ASSERT_EQUAL(A, B) do {\
if ((A) != (B)) { \
abort(); \
} \
} while(0)

#define TEST_ASSERT_TRUE(A) TEST_ASSERT_EQUAL(A, true)

/// A test to run with no test framework linked in. This allows some sanity checking but mostly is useful to support
/// analysis or instrumentation of the code while debugging.
static void testStructDelimited(void)
{
regulated_delimited_A_1_0 obj;
regulated_delimited_A_1_0_initialize_(&obj);
regulated_delimited_A_1_0_select_del_(&obj);
regulated_delimited_A_1_0_select_del_(NULL); // No action.
obj.del.var.count = 2;
obj.del.var.elements[0].a.count = 2;
obj.del.var.elements[0].a.elements[0] = 1;
obj.del.var.elements[0].a.elements[1] = 2;
obj.del.var.elements[0].b = 0;
obj.del.var.elements[1].a.count = 1;
obj.del.var.elements[1].a.elements[0] = 3;
obj.del.var.elements[1].a.elements[1] = 123; // ignored
obj.del.var.elements[1].b = 4;
obj.del.fix.count = 1;
obj.del.fix.elements[0].a[0] = 5;
obj.del.fix.elements[0].a[1] = 6;

const uint8_t reference[] = {
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0x01U, 0x17U, 0x00U, 0x00U, 0x00U, 0x02U, 0x04U, 0x00U, 0x00U, 0x00U, 0x02U, 0x01U, 0x02U, 0x00U, 0x03U, 0x00U,
0x00U, 0x00U, 0x01U, 0x03U, 0x04U, 0x01U, 0x02U, 0x00U, 0x00U, 0x00U, 0x05U, 0x06U,
// END OF SERIALIZED REPRESENTATION
0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU, 0xAAU,
0xAAU,
};
static_assert(sizeof(reference) == regulated_delimited_A_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, "");

uint8_t buf[1024] = {0};
(void) memset(&buf[0], 0xAAU, sizeof(buf)); // Fill out the canaries
size_t size = sizeof(buf);
TEST_ASSERT_EQUAL(0, regulated_delimited_A_1_0_serialize_(&obj, &buf[0], &size));
TEST_ASSERT_EQUAL(28U, size);

// Deserialize back from the reference using the same type and compare the field values.
regulated_delimited_A_1_0_initialize_(&obj); // Erase prior state.
size = sizeof(reference);
TEST_ASSERT_EQUAL(0, regulated_delimited_A_1_0_deserialize_(&obj, &reference[0], &size));
TEST_ASSERT_EQUAL(28U, size);
TEST_ASSERT_TRUE(regulated_delimited_A_1_0_is_del_(&obj));
TEST_ASSERT_EQUAL(2, obj.del.var.count);
TEST_ASSERT_EQUAL(2, obj.del.var.elements[0].a.count);
TEST_ASSERT_EQUAL(1, obj.del.var.elements[0].a.elements[0]);
TEST_ASSERT_EQUAL(2, obj.del.var.elements[0].a.elements[1]);
TEST_ASSERT_EQUAL(0, obj.del.var.elements[0].b);
TEST_ASSERT_EQUAL(1, obj.del.var.elements[1].a.count);
TEST_ASSERT_EQUAL(3, obj.del.var.elements[1].a.elements[0]);
TEST_ASSERT_EQUAL(4, obj.del.var.elements[1].b);
TEST_ASSERT_EQUAL(1, obj.del.fix.count);
TEST_ASSERT_EQUAL(5, obj.del.fix.elements[0].a[0]);
TEST_ASSERT_EQUAL(6, obj.del.fix.elements[0].a[1]);

// Deserialize using a different type to test extensibility enabled by delimited serialization.
regulated_delimited_A_1_1 dif;
size = sizeof(reference);
TEST_ASSERT_EQUAL(0, regulated_delimited_A_1_1_deserialize_(&dif, &reference[0], &size));
TEST_ASSERT_EQUAL(28U, size);
TEST_ASSERT_TRUE(regulated_delimited_A_1_1_is_del_(&dif));
TEST_ASSERT_EQUAL(2, dif.del.var.count);
TEST_ASSERT_EQUAL(2, dif.del.var.elements[0].a.count);
TEST_ASSERT_EQUAL(1, dif.del.var.elements[0].a.elements[0]);
TEST_ASSERT_EQUAL(2, dif.del.var.elements[0].a.elements[1]);
// b implicitly truncated away
TEST_ASSERT_EQUAL(1, dif.del.var.elements[1].a.count);
TEST_ASSERT_EQUAL(3, dif.del.var.elements[1].a.elements[0]);
// b implicitly truncated away
TEST_ASSERT_EQUAL(1, dif.del.fix.count);
TEST_ASSERT_EQUAL(5, dif.del.fix.elements[0].a[0]);
TEST_ASSERT_EQUAL(6, dif.del.fix.elements[0].a[1]);
TEST_ASSERT_EQUAL(0, dif.del.fix.elements[0].a[2]); // 3rd element is implicitly zero-extended
TEST_ASSERT_EQUAL(0, dif.del.fix.elements[0].b); // b is implicitly zero-extended

// Reverse version switch -- serialize v1.1 and then deserialize back using v1.0.
dif.del.var.count = 1;
dif.del.var.elements[0].a.count = 2;
dif.del.var.elements[0].a.elements[0] = 11;
dif.del.var.elements[0].a.elements[1] = 22;
dif.del.fix.count = 2;
dif.del.fix.elements[0].a[0] = 5;
dif.del.fix.elements[0].a[1] = 6;
dif.del.fix.elements[0].a[2] = 7;
dif.del.fix.elements[0].b = 8;
dif.del.fix.elements[1].a[0] = 100;
dif.del.fix.elements[1].a[1] = 200;
dif.del.fix.elements[1].a[2] = 123;
dif.del.fix.elements[1].b = 99;
size = sizeof(buf);
TEST_ASSERT_EQUAL(0, regulated_delimited_A_1_1_serialize_(&dif, &buf[0], &size));
TEST_ASSERT_EQUAL(30U, size); // the reference size was computed by hand
TEST_ASSERT_EQUAL(0, regulated_delimited_A_1_0_deserialize_(&obj, &buf[0], &size));
TEST_ASSERT_TRUE(regulated_delimited_A_1_0_is_del_(&obj));
TEST_ASSERT_EQUAL(1, obj.del.var.count);
TEST_ASSERT_EQUAL(2, obj.del.var.elements[0].a.count);
TEST_ASSERT_EQUAL(11, obj.del.var.elements[0].a.elements[0]);
TEST_ASSERT_EQUAL(22, obj.del.var.elements[0].a.elements[1]);
TEST_ASSERT_EQUAL(0, obj.del.var.elements[0].b); // b is implicitly zero-extended
TEST_ASSERT_EQUAL(2, obj.del.fix.count);
TEST_ASSERT_EQUAL(5, obj.del.fix.elements[0].a[0]); // 3rd is implicitly truncated, b is implicitly truncated
TEST_ASSERT_EQUAL(6, obj.del.fix.elements[0].a[1]);
TEST_ASSERT_EQUAL(100, obj.del.fix.elements[1].a[0]); // 3rd is implicitly truncated, b is implicitly truncated
TEST_ASSERT_EQUAL(200, obj.del.fix.elements[1].a[1]);
}

int main(void)
{
testStructDelimited();
return 0;
}
Loading

0 comments on commit 86757b1

Please sign in to comment.