diff --git a/.github/instructions/license_header.instructions.md b/.github/instructions/license_header.instructions.md index e352548..5deae6d 100644 --- a/.github/instructions/license_header.instructions.md +++ b/.github/instructions/license_header.instructions.md @@ -31,6 +31,7 @@ Use Doxygen comments. * SPDX-License-Identifier: Apache-2.0 * SPDX-FileCopyrightText: 2026 Yogev Neumann */ + ``` ### Python / YAML / Makefile / Shell / Gitignore @@ -54,12 +55,34 @@ Use hash comments. # # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: 2026 Yogev Neumann -> + ``` -### Jinja2 Templates +### Jinja2 `.j2` Templates -Templates should also have the license as a comment at the top of each file wrapped with `{#-` and `-#}`. +Use a comment at the top of each file wrapped with `{#-` and `-#}`. + +```jinja2 +{#- + Copyright 2026 Yogev Neumann + + 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 + + http://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. + + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann +-#} + +``` ## Exceptions diff --git a/src/J2735_internal_DE_AllowedManeuvers.h b/src/J2735_internal_DE_AllowedManeuvers.h index cdbf156..ad6ff12 100644 --- a/src/J2735_internal_DE_AllowedManeuvers.h +++ b/src/J2735_internal_DE_AllowedManeuvers.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 AllowedManeuvers Definition and Access Macros. * + * @verbatim * AllowedManeuvers ::= BIT STRING { * maneuverStraightAllowed (0), * maneuverLeftAllowed (1), @@ -35,46 +36,42 @@ * caution (10), * reserved1 (11) * } (SIZE (12)) + * @endverbatim * * Fixed BIT STRING with size 12. * - * Wire Format (12 bits total): + * @par Wire Format (12 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-11 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..11] (12 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 12 bits ≤ 56-bit READ_BITS limit. * We read all 12 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-11 * * 12-bit read layout (left-justified from bit 0): * [F0..F11] (12 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_ALLOWEDMANEUVERS_H #define J2735_INTERNAL_DE_ALLOWEDMANEUVERS_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of AllowedManeuvers in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_ALLOWED_MANEUVERS 12U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_ALLOWED_MANEUVERS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_ALLOWED_MANEUVERS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_ALLOWED_MANEUVERS_MANEUVER_STRAIGHT_ALLOWED 0U #define J2735_INTERNAL_BIT_ALLOWED_MANEUVERS_MANEUVER_LEFT_ALLOWED 1U @@ -107,7 +104,7 @@ * @note Internal use only. Not part of the public API. */ #define J2735_INTERNAL_RAW_READ_ALLOWED_MANEUVERS(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_ALLOWED_MANEUVERS) + J2735_READ_BITS((buf), 0U, J2735_BW_ALLOWED_MANEUVERS) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -174,7 +171,7 @@ * @param[in] buf Pointer to the start of the AllowedManeuvers UPER encoding (const uint8_t*). * @return Always 12U. */ -#define J2735_ALLOWED_MANEUVERS_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_ALLOWED_MANEUVERS) +#define J2735_ALLOWED_MANEUVERS_SIZE(buf) ((void)(buf), J2735_BW_ALLOWED_MANEUVERS) /** * @brief Get all AllowedManeuvers as a single uint16_t value. diff --git a/src/J2735_internal_DE_BrakeAppliedStatus.h b/src/J2735_internal_DE_BrakeAppliedStatus.h index a33fe62..ba76d99 100644 --- a/src/J2735_internal_DE_BrakeAppliedStatus.h +++ b/src/J2735_internal_DE_BrakeAppliedStatus.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 BrakeAppliedStatus Definition and Access Macros. * + * @verbatim * BrakeAppliedStatus ::= BIT STRING { * unavailable (0), * leftFront (1), @@ -28,46 +29,42 @@ * rightFront (3), * rightRear (4) * } (SIZE (5)) + * @endverbatim * * Fixed BIT STRING with size 5. * - * Wire Format (5 bits total): + * @par Wire Format (5 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-4 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..4] (5 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 5 bits ≤ 56-bit READ_BITS limit. * We read all 5 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-4 * * 5-bit read layout (left-justified from bit 0): * [F0..F4] (5 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_BRAKEAPPLIEDSTATUS_H #define J2735_INTERNAL_DE_BRAKEAPPLIEDSTATUS_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of BrakeAppliedStatus in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_BRAKE_APPLIED_STATUS 5U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_BRAKE_APPLIED_STATUS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_BRAKE_APPLIED_STATUS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_BRAKE_APPLIED_STATUS_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_BRAKE_APPLIED_STATUS_LEFT_FRONT 1U @@ -93,7 +90,7 @@ * @note Internal use only. Not part of the public API. */ #define J2735_INTERNAL_RAW_READ_BRAKE_APPLIED_STATUS(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_BRAKE_APPLIED_STATUS) + J2735_READ_BITS((buf), 0U, J2735_BW_BRAKE_APPLIED_STATUS) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -119,7 +116,7 @@ * * @param[in] raw5 Value previously returned by J2735_INTERNAL_RAW_READ_BRAKE_APPLIED_STATUS(). * @return Right-aligned flag bits as uint8_t: - * - 5 significant bits (0x0000-0x001F) + * - 5 significant bits (0x00-0x1F) * @note Internal use only. Use J2735_BRAKE_APPLIED_STATUS_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_BRAKE_APPLIED_STATUS(raw5) ((uint8_t)((raw5) & 0x1FU)) @@ -160,8 +157,7 @@ * @param[in] buf Pointer to the start of the BrakeAppliedStatus UPER encoding (const uint8_t*). * @return Always 5U. */ -#define J2735_BRAKE_APPLIED_STATUS_SIZE(buf) \ - ((void)(buf), J2735_INTERNAL_ROOT_SIZE_BRAKE_APPLIED_STATUS) +#define J2735_BRAKE_APPLIED_STATUS_SIZE(buf) ((void)(buf), J2735_BW_BRAKE_APPLIED_STATUS) /** * @brief Get all BrakeAppliedStatus as a single uint8_t value. diff --git a/src/J2735_internal_DE_ExteriorLights.h b/src/J2735_internal_DE_ExteriorLights.h index 1e00862..18084ed 100644 --- a/src/J2735_internal_DE_ExteriorLights.h +++ b/src/J2735_internal_DE_ExteriorLights.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 ExteriorLights Definition and Access Macros. * + * @verbatim * ExteriorLights ::= BIT STRING { * lowBeamHeadlightsOn (0), * highBeamHeadlightsOn (1), @@ -32,25 +33,30 @@ * fogLightOn (7), * parkingLightsOn (8) * } (SIZE (9, ...)) + * @endverbatim * * Extensible BIT STRING with root size 9 and known extension size 9. * - * Wire Format (non-extended, 10 bits total): + * @par Wire Format (non-extended, 10 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-9 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..8] (9 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 17 bits total): + * @par Wire Format (extended, 17 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-16 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=9 (7 bits) │ flags[0..8] (9 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 17 bits ≤ 56-bit READ_BITS limit. * We read all 17 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 16 (MSB of 17-bit value) @@ -61,6 +67,7 @@ * bit16 15..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F8] * bit16 15..9 8..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_EXTERIORLIGHTS_H #define J2735_INTERNAL_DE_EXTERIORLIGHTS_H @@ -102,7 +109,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_EXTERIOR_LIGHTS == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_EXTERIOR_LIGHTS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_EXTERIOR_LIGHTS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_EXTERIOR_LIGHTS_LOW_BEAM_HEADLIGHTS_ON 0U #define J2735_INTERNAL_BIT_EXTERIOR_LIGHTS_HIGH_BEAM_HEADLIGHTS_ON 1U diff --git a/src/J2735_internal_DE_GNSSstatus.h b/src/J2735_internal_DE_GNSSstatus.h index c1e120e..b8d4e34 100644 --- a/src/J2735_internal_DE_GNSSstatus.h +++ b/src/J2735_internal_DE_GNSSstatus.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 GNSSstatus Definition and Access Macros. * + * @verbatim * GNSSstatus ::= BIT STRING { * unavailable (0), * isHealthy (1), @@ -31,46 +32,42 @@ * localCorrectionsPresent (6), * networkCorrectionsPresent (7) * } (SIZE (8)) + * @endverbatim * * Fixed BIT STRING with size 8. * - * Wire Format (8 bits total): + * @par Wire Format (8 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-7 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..7] (8 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 8 bits ≤ 56-bit READ_BITS limit. * We read all 8 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-7 * * 8-bit read layout (left-justified from bit 0): * [F0..F7] (8 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_GNSSSTATUS_H #define J2735_INTERNAL_DE_GNSSSTATUS_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of GNSSstatus in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_GNSS_STATUS 8U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_GNSS_STATUS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_GNSS_STATUS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_GNSS_STATUS_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_GNSS_STATUS_IS_HEALTHY 1U @@ -98,8 +95,7 @@ * @return 8-bit value as uint64_t with wire bits left-justified. * @note Internal use only. Not part of the public API. */ -#define J2735_INTERNAL_RAW_READ_GNSS_STATUS(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_GNSS_STATUS) +#define J2735_INTERNAL_RAW_READ_GNSS_STATUS(buf) J2735_READ_BITS((buf), 0U, J2735_BW_GNSS_STATUS) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -125,7 +121,7 @@ * * @param[in] raw8 Value previously returned by J2735_INTERNAL_RAW_READ_GNSS_STATUS(). * @return Right-aligned flag bits as uint8_t: - * - 8 significant bits (0x0000-0x00FF) + * - 8 significant bits (0x00-0xFF) * @note Internal use only. Use J2735_GNSS_STATUS_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_GNSS_STATUS(raw8) ((uint8_t)((raw8) & 0xFFU)) @@ -166,7 +162,7 @@ * @param[in] buf Pointer to the start of the GNSSstatus UPER encoding (const uint8_t*). * @return Always 8U. */ -#define J2735_GNSS_STATUS_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_GNSS_STATUS) +#define J2735_GNSS_STATUS_SIZE(buf) ((void)(buf), J2735_BW_GNSS_STATUS) /** * @brief Get all GNSSstatus as a single uint8_t value. diff --git a/src/J2735_internal_DE_LaneDirection.h b/src/J2735_internal_DE_LaneDirection.h index 48639e4..c9a9117 100644 --- a/src/J2735_internal_DE_LaneDirection.h +++ b/src/J2735_internal_DE_LaneDirection.h @@ -21,50 +21,47 @@ * @author Yogev Neumann * @brief J2735 LaneDirection Definition and Access Macros. * + * @verbatim * LaneDirection ::= BIT STRING { * ingressPath (0), * egressPath (1) * } (SIZE (2)) + * @endverbatim * * Fixed BIT STRING with size 2. * - * Wire Format (2 bits total): + * @par Wire Format (2 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-1 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..1] (2 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 2 bits ≤ 56-bit READ_BITS limit. * We read all 2 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-1 * * 2-bit read layout (left-justified from bit 0): * [F0..F1] (2 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_LANEDIRECTION_H #define J2735_INTERNAL_DE_LANEDIRECTION_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of LaneDirection in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_LANE_DIRECTION 2U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_LANE_DIRECTION_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_LANE_DIRECTION_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_LANE_DIRECTION_INGRESS_PATH 0U #define J2735_INTERNAL_BIT_LANE_DIRECTION_EGRESS_PATH 1U @@ -87,7 +84,7 @@ * @note Internal use only. Not part of the public API. */ #define J2735_INTERNAL_RAW_READ_LANE_DIRECTION(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_LANE_DIRECTION) + J2735_READ_BITS((buf), 0U, J2735_BW_LANE_DIRECTION) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -113,7 +110,7 @@ * * @param[in] raw2 Value previously returned by J2735_INTERNAL_RAW_READ_LANE_DIRECTION(). * @return Right-aligned flag bits as uint8_t: - * - 2 significant bits (0x0000-0x0003) + * - 2 significant bits (0x00-0x03) * @note Internal use only. Use J2735_LANE_DIRECTION_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_LANE_DIRECTION(raw2) ((uint8_t)((raw2) & 0x3U)) @@ -154,7 +151,7 @@ * @param[in] buf Pointer to the start of the LaneDirection UPER encoding (const uint8_t*). * @return Always 2U. */ -#define J2735_LANE_DIRECTION_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_LANE_DIRECTION) +#define J2735_LANE_DIRECTION_SIZE(buf) ((void)(buf), J2735_BW_LANE_DIRECTION) /** * @brief Get all LaneDirection as a single uint8_t value. diff --git a/src/J2735_internal_DE_LaneSharing.h b/src/J2735_internal_DE_LaneSharing.h index 86bb6be..3ce8511 100644 --- a/src/J2735_internal_DE_LaneSharing.h +++ b/src/J2735_internal_DE_LaneSharing.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 LaneSharing Definition and Access Macros. * + * @verbatim * LaneSharing ::= BIT STRING { * overlappingLaneDescriptionProvided (0), * multipleLanesTreatedAsOneLane (1), @@ -33,46 +34,42 @@ * trackedVehicleTraffic (8), * reserved (9) * } (SIZE (10)) + * @endverbatim * * Fixed BIT STRING with size 10. * - * Wire Format (10 bits total): + * @par Wire Format (10 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-9 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..9] (10 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 10 bits ≤ 56-bit READ_BITS limit. * We read all 10 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-9 * * 10-bit read layout (left-justified from bit 0): * [F0..F9] (10 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_LANESHARING_H #define J2735_INTERNAL_DE_LANESHARING_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of LaneSharing in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_LANE_SHARING 10U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_LANE_SHARING_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_LANE_SHARING_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_LANE_SHARING_OVERLAPPING_LANE_DESCRIPTION_PROVIDED 0U #define J2735_INTERNAL_BIT_LANE_SHARING_MULTIPLE_LANES_TREATED_AS_ONE_LANE 1U @@ -102,8 +99,7 @@ * @return 10-bit value as uint64_t with wire bits left-justified. * @note Internal use only. Not part of the public API. */ -#define J2735_INTERNAL_RAW_READ_LANE_SHARING(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_LANE_SHARING) +#define J2735_INTERNAL_RAW_READ_LANE_SHARING(buf) J2735_READ_BITS((buf), 0U, J2735_BW_LANE_SHARING) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -170,7 +166,7 @@ * @param[in] buf Pointer to the start of the LaneSharing UPER encoding (const uint8_t*). * @return Always 10U. */ -#define J2735_LANE_SHARING_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_LANE_SHARING) +#define J2735_LANE_SHARING_SIZE(buf) ((void)(buf), J2735_BW_LANE_SHARING) /** * @brief Get all LaneSharing as a single uint16_t value. diff --git a/src/J2735_internal_DE_PersonalAssistive.h b/src/J2735_internal_DE_PersonalAssistive.h index fc7c6ae..9284d96 100644 --- a/src/J2735_internal_DE_PersonalAssistive.h +++ b/src/J2735_internal_DE_PersonalAssistive.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 PersonalAssistive Definition and Access Macros. * + * @verbatim * PersonalAssistive ::= BIT STRING { * unavailable (0), * otherType (1), @@ -29,25 +30,30 @@ * movement (4), * cognition (5) * } (SIZE (6, ...)) + * @endverbatim * * Extensible BIT STRING with root size 6 and known extension size 6. * - * Wire Format (non-extended, 7 bits total): + * @par Wire Format (non-extended, 7 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-6 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..5] (6 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 14 bits total): + * @par Wire Format (extended, 14 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-13 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=6 (7 bits) │ flags[0..5] (6 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 14 bits ≤ 56-bit READ_BITS limit. * We read all 14 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 13 (MSB of 14-bit value) @@ -58,6 +64,7 @@ * bit13 12..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F5] * bit13 12..6 5..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_PERSONALASSISTIVE_H #define J2735_INTERNAL_DE_PERSONALASSISTIVE_H @@ -99,7 +106,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PERSONAL_ASSISTIVE == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_PERSONAL_ASSISTIVE_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_PERSONAL_ASSISTIVE_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_PERSONAL_ASSISTIVE_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_PERSONAL_ASSISTIVE_OTHER_TYPE 1U @@ -169,8 +177,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PERSONAL_ASSISTIVE == * * @param[in] raw14 Value previously returned by J2735_INTERNAL_RAW_READ_PERSONAL_ASSISTIVE(). * @return Right-aligned flag bits as uint8_t: - * - 6 significant bits (0x0000-0x003F) if non-extended - * - 6 significant bits (0x0000-0x003F) if extended + * - 6 significant bits (0x00-0x3F) if non-extended + * - 6 significant bits (0x00-0x3F) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to uint8_t. * @note Internal use only. Use J2735_PERSONAL_ASSISTIVE_GET() for public API. diff --git a/src/J2735_internal_DE_PersonalDeviceUsageState.h b/src/J2735_internal_DE_PersonalDeviceUsageState.h index 1d9b118..77ea283 100644 --- a/src/J2735_internal_DE_PersonalDeviceUsageState.h +++ b/src/J2735_internal_DE_PersonalDeviceUsageState.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 PersonalDeviceUsageState Definition and Access Macros. * + * @verbatim * PersonalDeviceUsageState ::= BIT STRING { * unavailable (0), * other (1), @@ -32,25 +33,30 @@ * reading (7), * viewing (8) * } (SIZE (9, ...)) + * @endverbatim * * Extensible BIT STRING with root size 9 and known extension size 9. * - * Wire Format (non-extended, 10 bits total): + * @par Wire Format (non-extended, 10 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-9 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..8] (9 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 17 bits total): + * @par Wire Format (extended, 17 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-16 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=9 (7 bits) │ flags[0..8] (9 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 17 bits ≤ 56-bit READ_BITS limit. * We read all 17 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 16 (MSB of 17-bit value) @@ -61,6 +67,7 @@ * bit16 15..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F8] * bit16 15..9 8..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_PERSONALDEVICEUSAGESTATE_H #define J2735_INTERNAL_DE_PERSONALDEVICEUSAGESTATE_H @@ -102,8 +109,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PERSONAL_DEVICE_USAGE_STATE == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_PERSONAL_DEVICE_USAGE_STATE_GET_*() accessors - * instead. */ +/* @note Internal use only. */ +/* Use the public J2735_PERSONAL_DEVICE_USAGE_STATE_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_PERSONAL_DEVICE_USAGE_STATE_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_PERSONAL_DEVICE_USAGE_STATE_OTHER 1U diff --git a/src/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity.h b/src/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity.h index 3b77e14..16f7fa5 100644 --- a/src/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity.h +++ b/src/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 PublicSafetyAndRoadWorkerActivity Definition and Access Macros. * + * @verbatim * PublicSafetyAndRoadWorkerActivity ::= BIT STRING { * unavailable (0), * workingOnRoad (1), @@ -29,25 +30,30 @@ * directingTraffic (4), * otherActivities (5) * } (SIZE (6, ...)) + * @endverbatim * * Extensible BIT STRING with root size 6 and known extension size 6. * - * Wire Format (non-extended, 7 bits total): + * @par Wire Format (non-extended, 7 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-6 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..5] (6 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 14 bits total): + * @par Wire Format (extended, 14 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-13 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=6 (7 bits) │ flags[0..5] (6 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 14 bits ≤ 56-bit READ_BITS limit. * We read all 14 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 13 (MSB of 14-bit value) @@ -58,6 +64,7 @@ * bit13 12..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F5] * bit13 12..6 5..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_PUBLICSAFETYANDROADWORKERACTIVITY_H #define J2735_INTERNAL_DE_PUBLICSAFETYANDROADWORKERACTIVITY_H @@ -99,8 +106,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVI /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY_GET_*() - * accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY_WORKING_ON_ROAD 1U @@ -174,8 +181,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVI * @param[in] raw14 Value previously returned by * J2735_INTERNAL_RAW_READ_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY(). * @return Right-aligned flag bits as uint8_t: - * - 6 significant bits (0x0000-0x003F) if non-extended - * - 6 significant bits (0x0000-0x003F) if extended + * - 6 significant bits (0x00-0x3F) if non-extended + * - 6 significant bits (0x00-0x3F) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to uint8_t. * @note Internal use only. Use J2735_PUBLIC_SAFETY_AND_ROAD_WORKER_ACTIVITY_GET() for public API. diff --git a/src/J2735_internal_DE_PublicSafetyDirectingTrafficSubType.h b/src/J2735_internal_DE_PublicSafetyDirectingTrafficSubType.h index d3eb847..a042cd3 100644 --- a/src/J2735_internal_DE_PublicSafetyDirectingTrafficSubType.h +++ b/src/J2735_internal_DE_PublicSafetyDirectingTrafficSubType.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 PublicSafetyDirectingTrafficSubType Definition and Access Macros. * + * @verbatim * PublicSafetyDirectingTrafficSubType ::= BIT STRING { * unavailable (0), * policeAndTrafficOfficers (1), @@ -30,25 +31,30 @@ * emergencyOrganizationPersonnel (5), * highwayServiceVehiclePersonnel (6) * } (SIZE (7, ...)) + * @endverbatim * * Extensible BIT STRING with root size 7 and known extension size 7. * - * Wire Format (non-extended, 8 bits total): + * @par Wire Format (non-extended, 8 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..6] (7 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 15 bits total): + * @par Wire Format (extended, 15 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-14 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=7 (7 bits) │ flags[0..6] (7 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 15 bits ≤ 56-bit READ_BITS limit. * We read all 15 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 14 (MSB of 15-bit value) @@ -59,6 +65,7 @@ * bit14 13..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F6] * bit14 13..7 6..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_H #define J2735_INTERNAL_DE_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_H @@ -100,8 +107,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE_GET_*() - * accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE_POLICE_AND_TRAFFIC_OFFICERS 1U @@ -179,8 +186,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_ * @param[in] raw15 Value previously returned by * J2735_INTERNAL_RAW_READ_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE(). * @return Right-aligned flag bits as uint8_t: - * - 7 significant bits (0x0000-0x007F) if non-extended - * - 7 significant bits (0x0000-0x007F) if extended + * - 7 significant bits (0x00-0x7F) if non-extended + * - 7 significant bits (0x00-0x7F) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to uint8_t. * @note Internal use only. Use J2735_PUBLIC_SAFETY_DIRECTING_TRAFFIC_SUB_TYPE_GET() for public API. diff --git a/src/J2735_internal_DE_TrafficLightOperationStatus.h b/src/J2735_internal_DE_TrafficLightOperationStatus.h index 097a4db..ffe0455 100644 --- a/src/J2735_internal_DE_TrafficLightOperationStatus.h +++ b/src/J2735_internal_DE_TrafficLightOperationStatus.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 TrafficLightOperationStatus Definition and Access Macros. * + * @verbatim * TrafficLightOperationStatus ::= BIT STRING { * manual (0), * flashing (1), @@ -31,25 +32,30 @@ * phase (6), * reserved (7) * } (SIZE (8, ...)) + * @endverbatim * * Extensible BIT STRING with root size 8 and known extension size 8. * - * Wire Format (non-extended, 9 bits total): + * @par Wire Format (non-extended, 9 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-8 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..7] (8 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 16 bits total): + * @par Wire Format (extended, 16 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-15 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=8 (7 bits) │ flags[0..7] (8 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 16 bits ≤ 56-bit READ_BITS limit. * We read all 16 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 15 (MSB of 16-bit value) @@ -60,6 +66,7 @@ * bit15 14..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F7] * bit15 14..8 7..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_TRAFFICLIGHTOPERATIONSTATUS_H #define J2735_INTERNAL_DE_TRAFFICLIGHTOPERATIONSTATUS_H @@ -101,8 +108,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_TRAFFIC_LIGHT_OPERATION_STATUS == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_TRAFFIC_LIGHT_OPERATION_STATUS_GET_*() accessors - * instead. */ +/* @note Internal use only. */ +/* Use the public J2735_TRAFFIC_LIGHT_OPERATION_STATUS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_TRAFFIC_LIGHT_OPERATION_STATUS_MANUAL 0U #define J2735_INTERNAL_BIT_TRAFFIC_LIGHT_OPERATION_STATUS_FLASHING 1U @@ -176,8 +183,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_TRAFFIC_LIGHT_OPERATION_STATUS == * @param[in] raw16 Value previously returned by * J2735_INTERNAL_RAW_READ_TRAFFIC_LIGHT_OPERATION_STATUS(). * @return Right-aligned flag bits as uint8_t: - * - 8 significant bits (0x0000-0x00FF) if non-extended - * - 8 significant bits (0x0000-0x00FF) if extended + * - 8 significant bits (0x00-0xFF) if non-extended + * - 8 significant bits (0x00-0xFF) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to uint8_t. * @note Internal use only. Use J2735_TRAFFIC_LIGHT_OPERATION_STATUS_GET() for public API. diff --git a/src/J2735_internal_DE_TransitStatus.h b/src/J2735_internal_DE_TransitStatus.h index 09f647f..681532b 100644 --- a/src/J2735_internal_DE_TransitStatus.h +++ b/src/J2735_internal_DE_TransitStatus.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 TransitStatus Definition and Access Macros. * + * @verbatim * TransitStatus ::= BIT STRING { * none (0), * anADAuse (1), @@ -29,46 +30,42 @@ * occM (4), * occL (5) * } (SIZE (6)) + * @endverbatim * * Fixed BIT STRING with size 6. * - * Wire Format (6 bits total): + * @par Wire Format (6 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-5 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..5] (6 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 6 bits ≤ 56-bit READ_BITS limit. * We read all 6 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-5 * * 6-bit read layout (left-justified from bit 0): * [F0..F5] (6 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_TRANSITSTATUS_H #define J2735_INTERNAL_DE_TRANSITSTATUS_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of TransitStatus in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_TRANSIT_STATUS 6U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_TRANSIT_STATUS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_TRANSIT_STATUS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_TRANSIT_STATUS_NONE 0U #define J2735_INTERNAL_BIT_TRANSIT_STATUS_AN_ADA_USE 1U @@ -95,7 +92,7 @@ * @note Internal use only. Not part of the public API. */ #define J2735_INTERNAL_RAW_READ_TRANSIT_STATUS(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_TRANSIT_STATUS) + J2735_READ_BITS((buf), 0U, J2735_BW_TRANSIT_STATUS) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -121,7 +118,7 @@ * * @param[in] raw6 Value previously returned by J2735_INTERNAL_RAW_READ_TRANSIT_STATUS(). * @return Right-aligned flag bits as uint8_t: - * - 6 significant bits (0x0000-0x003F) + * - 6 significant bits (0x00-0x3F) * @note Internal use only. Use J2735_TRANSIT_STATUS_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_TRANSIT_STATUS(raw6) ((uint8_t)((raw6) & 0x3FU)) @@ -162,7 +159,7 @@ * @param[in] buf Pointer to the start of the TransitStatus UPER encoding (const uint8_t*). * @return Always 6U. */ -#define J2735_TRANSIT_STATUS_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_TRANSIT_STATUS) +#define J2735_TRANSIT_STATUS_SIZE(buf) ((void)(buf), J2735_BW_TRANSIT_STATUS) /** * @brief Get all TransitStatus as a single uint8_t value. diff --git a/src/J2735_internal_DE_UserSizeAndBehaviour.h b/src/J2735_internal_DE_UserSizeAndBehaviour.h index 9961163..eb2bd2e 100644 --- a/src/J2735_internal_DE_UserSizeAndBehaviour.h +++ b/src/J2735_internal_DE_UserSizeAndBehaviour.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 UserSizeAndBehaviour Definition and Access Macros. * + * @verbatim * UserSizeAndBehaviour ::= BIT STRING { * unavailable (0), * smallStature (1), @@ -28,25 +29,30 @@ * erraticMoving (3), * slowMoving (4) * } (SIZE (5, ...)) + * @endverbatim * * Extensible BIT STRING with root size 5 and known extension size 5. * - * Wire Format (non-extended, 6 bits total): + * @par Wire Format (non-extended, 6 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-5 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..4] (5 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 13 bits total): + * @par Wire Format (extended, 13 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-12 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=5 (7 bits) │ flags[0..4] (5 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 13 bits ≤ 56-bit READ_BITS limit. * We read all 13 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 12 (MSB of 13-bit value) @@ -57,6 +63,7 @@ * bit12 11..7 6..0 * Extended: [Ext=1][nsnnwn:7][F0..F4] * bit12 11..5 4..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_USERSIZEANDBEHAVIOUR_H #define J2735_INTERNAL_DE_USERSIZEANDBEHAVIOUR_H @@ -98,8 +105,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_USER_SIZE_AND_BEHAVIOUR == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_USER_SIZE_AND_BEHAVIOUR_GET_*() accessors instead. - */ +/* @note Internal use only. */ +/* Use the public J2735_USER_SIZE_AND_BEHAVIOUR_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_USER_SIZE_AND_BEHAVIOUR_UNAVAILABLE 0U #define J2735_INTERNAL_BIT_USER_SIZE_AND_BEHAVIOUR_SMALL_STATURE 1U @@ -168,8 +175,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_USER_SIZE_AND_BEHAVIOUR == * * @param[in] raw13 Value previously returned by J2735_INTERNAL_RAW_READ_USER_SIZE_AND_BEHAVIOUR(). * @return Right-aligned flag bits as uint8_t: - * - 5 significant bits (0x0000-0x001F) if non-extended - * - 5 significant bits (0x0000-0x001F) if extended + * - 5 significant bits (0x00-0x1F) if non-extended + * - 5 significant bits (0x00-0x1F) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to uint8_t. * @note Internal use only. Use J2735_USER_SIZE_AND_BEHAVIOUR_GET() for public API. diff --git a/src/J2735_internal_DE_VehicleEventFlags.h b/src/J2735_internal_DE_VehicleEventFlags.h index 39097ba..68717b4 100644 --- a/src/J2735_internal_DE_VehicleEventFlags.h +++ b/src/J2735_internal_DE_VehicleEventFlags.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 VehicleEventFlags Definition and Access Macros. * + * @verbatim * VehicleEventFlags ::= BIT STRING { * eventHazardLights (0), * eventStopLineViolation (1), @@ -37,25 +38,30 @@ * eventAirBagDeployment (12), * eventJackKnife (13) * } (SIZE (13, ..., 14)) + * @endverbatim * * Extensible BIT STRING with root size 13 and known extension size 14. * - * Wire Format (non-extended, 14 bits total): + * @par Wire Format (non-extended, 14 bits total): + * @verbatim * ┌───────┬──────────────────────────────────────────────────────┐ * │ Bit 0 │ Bits 1-13 │ * ├───────┼──────────────────────────────────────────────────────┤ * │ Ext=0 │ flags[0..12] (13 bits) │ * └───────┴──────────────────────────────────────────────────────┘ + * @endverbatim * - * Wire Format (extended, 22 bits total): + * @par Wire Format (extended, 22 bits total): + * @verbatim * ┌───────┬────────────────────┬─────────────────────────────────┐ * │ Bit 0 │ Bits 1-7 │ Bits 8-21 │ * ├───────┼────────────────────┼─────────────────────────────────┤ * │ Ext=1 │ nsnnwn=14 (7 bits) │ flags[0..13] (14 bits) │ * └───────┴────────────────────┴─────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 22 bits ≤ 56-bit READ_BITS limit. * We read all 22 bits in ONE call, then use bit arithmetic to extract: * - Extension bit at position 21 (MSB of 22-bit value) @@ -66,6 +72,7 @@ * bit21 20..8 7..0 * Extended: [Ext=1][nsnnwn:7][F0..F13] * bit21 20..14 13..0 + * @endverbatim */ #ifndef J2735_INTERNAL_DE_VEHICLEEVENTFLAGS_H #define J2735_INTERNAL_DE_VEHICLEEVENTFLAGS_H @@ -107,7 +114,8 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_VEHICLE_EVENT_FLAGS == /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_VEHICLE_EVENT_FLAGS_GET_*() accessors instead. */ +/* @note Internal use only. */ +/* Use the public J2735_VEHICLE_EVENT_FLAGS_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_VEHICLE_EVENT_FLAGS_EVENT_HAZARD_LIGHTS 0U #define J2735_INTERNAL_BIT_VEHICLE_EVENT_FLAGS_EVENT_STOP_LINE_VIOLATION 1U diff --git a/src/J2735_internal_DE_VerticalAccelerationThreshold.h b/src/J2735_internal_DE_VerticalAccelerationThreshold.h index afa70cb..33d7db5 100644 --- a/src/J2735_internal_DE_VerticalAccelerationThreshold.h +++ b/src/J2735_internal_DE_VerticalAccelerationThreshold.h @@ -21,6 +21,7 @@ * @author Yogev Neumann * @brief J2735 VerticalAccelerationThreshold Definition and Access Macros. * + * @verbatim * VerticalAccelerationThreshold ::= BIT STRING { * notEquipped (0), * leftFront (1), @@ -28,47 +29,42 @@ * rightFront (3), * rightRear (4) * } (SIZE (5)) + * @endverbatim * * Fixed BIT STRING with size 5. * - * Wire Format (5 bits total): + * @par Wire Format (5 bits total): + * @verbatim * ┌──────────────────────────────────────────────────────────────┐ * │ Bits 0-4 │ * ├──────────────────────────────────────────────────────────────┤ * │ flags[0..4] (5 bits) │ * └──────────────────────────────────────────────────────────────┘ + * @endverbatim * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = 5 bits ≤ 56-bit READ_BITS limit. * We read all 5 bits in ONE call, then use bit arithmetic to extract: * - Flags at positions 0-4 * * 5-bit read layout (left-justified from bit 0): * [F0..F4] (5 flag bits, no extension marker) + * @endverbatim */ #ifndef J2735_INTERNAL_DE_VERTICALACCELERATIONTHRESHOLD_H #define J2735_INTERNAL_DE_VERTICALACCELERATIONTHRESHOLD_H #include "J2735_internal_common.h" -/* ============================================================================================== */ -/* Constants */ -/* ============================================================================================== */ -/** - * @internal - * @brief Root size of VerticalAccelerationThreshold in bits. - */ -#define J2735_INTERNAL_ROOT_SIZE_VERTICAL_ACCELERATION_THRESHOLD 5U - /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* @note Internal use only. Use the public J2735_VERTICAL_ACCELERATION_THRESHOLD_GET_*() accessors - * instead. */ +/* @note Internal use only. */ +/* Use the public J2735_VERTICAL_ACCELERATION_THRESHOLD_GET_*() accessors instead. */ /* ============================================================================================== */ #define J2735_INTERNAL_BIT_VERTICAL_ACCELERATION_THRESHOLD_NOT_EQUIPPED 0U #define J2735_INTERNAL_BIT_VERTICAL_ACCELERATION_THRESHOLD_LEFT_FRONT 1U @@ -95,7 +91,7 @@ * @note Internal use only. Not part of the public API. */ #define J2735_INTERNAL_RAW_READ_VERTICAL_ACCELERATION_THRESHOLD(buf) \ - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_VERTICAL_ACCELERATION_THRESHOLD) + J2735_READ_BITS((buf), 0U, J2735_BW_VERTICAL_ACCELERATION_THRESHOLD) /* ============================================================================================== */ /* INTERNAL: Extension Bit Check */ @@ -123,7 +119,7 @@ * @param[in] raw5 Value previously returned by * J2735_INTERNAL_RAW_READ_VERTICAL_ACCELERATION_THRESHOLD(). * @return Right-aligned flag bits as uint8_t: - * - 5 significant bits (0x0000-0x001F) + * - 5 significant bits (0x00-0x1F) * @note Internal use only. Use J2735_VERTICAL_ACCELERATION_THRESHOLD_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_VERTICAL_ACCELERATION_THRESHOLD(raw5) ((uint8_t)((raw5) & 0x1FU)) @@ -168,7 +164,7 @@ * @return Always 5U. */ #define J2735_VERTICAL_ACCELERATION_THRESHOLD_SIZE(buf) \ - ((void)(buf), J2735_INTERNAL_ROOT_SIZE_VERTICAL_ACCELERATION_THRESHOLD) + ((void)(buf), J2735_BW_VERTICAL_ACCELERATION_THRESHOLD) /** * @brief Get all VerticalAccelerationThreshold as a single uint8_t value. diff --git a/src/J2735_internal_DF_ApproachOrLane.h b/src/J2735_internal_DF_ApproachOrLane.h index 66e0d60..3c9e672 100644 --- a/src/J2735_internal_DF_ApproachOrLane.h +++ b/src/J2735_internal_DF_ApproachOrLane.h @@ -22,33 +22,33 @@ * @brief J2735 ApproachOrLane Definition and Access Macros. * * @par ApproachOrLane Wire Format (UPER): - * @code + * @verbatim * ApproachOrLane ::= CHOICE { * approach ApproachID, -- 4 bits (J2735_BW_APPROACH_ID) * lane LaneID -- 8 bits (J2735_BW_LANE_ID) * } - * @endcode + * @endverbatim * * This is a non-extensible CHOICE with 2 alternatives. * Per ITU-T X.691 §23, the choice index uses ceil(log2(2)) = 1 bit. * * @par Wire Format (approach selected, 5 bits total): - * @code + * @verbatim * ┌───────────┬───────────────────────────┐ * │ Bit 0 │ Bits 1-4 │ * ├───────────┼───────────────────────────┤ * │ Index = 0 │ ApproachID value (4 bits) │ * └───────────┴───────────────────────────┘ - * @endcode + * @endverbatim * * @par Wire Format (lane selected, 9 bits total): - * @code + * @verbatim * ┌───────────┬───────────────────────┐ * │ Bit 0 │ Bits 1-8 │ * ├───────────┼───────────────────────┤ * │ Index = 1 │ LaneID value (8 bits) │ * └───────────┴───────────────────────┘ - * @endcode + * @endverbatim * * Performance Rationale (Single I/O Pattern): * Reading 9 bits unconditionally (even when smaller alternatives use less) is faster than @@ -92,6 +92,9 @@ */ #define J2735_INTERNAL_CHOICE_INDEX_BITS_APPROACH_OR_LANE 1U +_Static_assert(J2735_INTERNAL_CHOICE_INDEX_BITS_APPROACH_OR_LANE == J2735_BW_APPROACH_OR_LANE, + "CHOICE index bits must match J2735_BW_APPROACH_OR_LANE"); + /* ============================================================================================== */ /* INTERNAL: Structure Metadata */ /* ============================================================================================== */ diff --git a/src/J2735_internal_DF_BSMcoreData.h b/src/J2735_internal_DF_BSMcoreData.h index b25656e..dd18e4a 100644 --- a/src/J2735_internal_DF_BSMcoreData.h +++ b/src/J2735_internal_DF_BSMcoreData.h @@ -22,7 +22,7 @@ * @brief J2735 BSMcoreData Definition and Access Macros. * * @par BSMcoreData Wire Format (UPER): - * @code + * @verbatim * BSMcoreData ::= SEQUENCE { * msgCnt MsgCount, -- 7 bits (unsigned, 0..127) * id TemporaryID, -- 32 bits @@ -39,10 +39,10 @@ * brakes BrakeSystemStatus, -- 15 bits * size VehicleSize -- 22 bits * } - * @endcode + * @endverbatim * * @par Wire Format (290 bits): - * @code + * @verbatim * ┌──────────────┬───────────────────────────────────────────────┐ * │ Bits │ Content │ * ├──────────────┼───────────────────────────────────────────────┤ @@ -61,7 +61,7 @@ * │ 253-267 │ brakes (15) │ * │ 268-289 │ size (22) │ * └──────────────┴───────────────────────────────────────────────┘ - * @endcode + * @endverbatim */ #ifndef J2735_INTERNAL_DF_BSMCOREDATA_H #define J2735_INTERNAL_DF_BSMCOREDATA_H @@ -184,6 +184,10 @@ #define J2735_INTERNAL_OFF_BSM_CORE_DATA_SIZE(buf) \ (J2735_INTERNAL_OFF_BSM_CORE_DATA_BRAKES(buf) + J2735_BW_BRAKE_SYSTEM_STATUS) /* 268 */ +_Static_assert((J2735_INTERNAL_OFF_BSM_CORE_DATA_SIZE(0) + J2735_BW_VEHICLE_SIZE) == + J2735_BW_BSM_CORE_DATA, + "BSMcoreData offset chain must sum to J2735_BW_BSM_CORE_DATA"); + /* ============================================================================================== */ /* PUBLIC API: Field Getters */ /* ============================================================================================== */ diff --git a/src/J2735_internal_DF_IntersectionReferenceID.h b/src/J2735_internal_DF_IntersectionReferenceID.h index 8ce0141..55a1e9c 100644 --- a/src/J2735_internal_DF_IntersectionReferenceID.h +++ b/src/J2735_internal_DF_IntersectionReferenceID.h @@ -22,30 +22,30 @@ * @brief J2735 IntersectionReferenceID Definition and Access Macros. * * @par IntersectionReferenceID Wire Format (UPER): - * @code + * @verbatim * IntersectionReferenceID ::= SEQUENCE { * region RoadRegulatorID OPTIONAL, -- 16 bits (unsigned, 0..65535) * id IntersectionID -- 16 bits (unsigned, 0..65535) * } - * @endcode + * @endverbatim * * @par Wire Format (region ABSENT, 17 bits): - * @code + * @verbatim * ┌────────────┬──────────────┐ * │ Bit 0 │ Bits 1-16 │ * ├────────────┼──────────────┤ * │ Opt=0 │ id (16) │ * └────────────┴──────────────┘ - * @endcode + * @endverbatim * * @par Wire Format (region PRESENT, 33 bits): - * @code + * @verbatim * ┌────────────┬──────────────┬──────────────┐ * │ Bit 0 │ Bits 1-16 │ Bits 17-32 │ * ├────────────┼──────────────┼──────────────┤ * │ Opt=1 │ region (16) │ id (16) │ * └────────────┴──────────────┴──────────────┘ - * @endcode + * @endverbatim */ #ifndef J2735_INTERNAL_DF_INTERSECTIONREFERENCEID_H #define J2735_INTERNAL_DF_INTERSECTIONREFERENCEID_H diff --git a/src/J2735_internal_DF_PathPrediction.h b/src/J2735_internal_DF_PathPrediction.h index 199ebe0..3df796f 100644 --- a/src/J2735_internal_DF_PathPrediction.h +++ b/src/J2735_internal_DF_PathPrediction.h @@ -22,31 +22,31 @@ * @brief J2735 PathPrediction Definition and Access Macros. * * @par PathPrediction Wire Format (UPER): - * @code + * @verbatim * PathPrediction ::= SEQUENCE { * radiusOfCurve RadiusOfCurvature, -- 16 bits (signed, -32767..32767) * confidence Confidence, -- 8 bits (unsigned, 0..200) * ... * } - * @endcode + * @endverbatim * * @par Wire Format (no extensions, 25 bits): - * @code + * @verbatim * ┌─────────┬─────────────────────┬──────────────────┐ * │ Bit 0 │ Bits 1-16 │ Bits 17-24 │ * ├─────────┼─────────────────────┼──────────────────┤ * │ Ext=0 │ radiusOfCurve (16) │ confidence (8) │ * └─────────┴─────────────────────┴──────────────────┘ - * @endcode + * @endverbatim * * @par Wire Format (with extensions, variable): - * @code + * @verbatim * ┌─────────┬─────────────────────┬──────────────────┬──────────────────┐ * │ Bit 0 │ Bits 1-16 │ Bits 17-24 │ Bits 25+ │ * ├─────────┼─────────────────────┼──────────────────┼──────────────────┤ * │ Ext=1 │ radiusOfCurve (16) │ confidence (8) │ (extension data) │ * └─────────┴─────────────────────┴──────────────────┴──────────────────┘ - * @endcode + * @endverbatim */ #ifndef J2735_INTERNAL_DF_PATHPREDICTION_H #define J2735_INTERNAL_DF_PATHPREDICTION_H @@ -77,6 +77,10 @@ (J2735_INTERNAL_PREFIX_BITS_PATH_PREDICTION + J2735_BW_RADIUS_OF_CURVATURE + \ J2735_BW_CONFIDENCE) /* 25 bits */ +_Static_assert(J2735_INTERNAL_ROOT_SIZE_BITS_PATH_PREDICTION == + (J2735_INTERNAL_PREFIX_BITS_PATH_PREDICTION + J2735_BW_PATH_PREDICTION), + "ROOT_SIZE_BITS must equal prefix + J2735_BW_PATH_PREDICTION"); + /* ============================================================================================== */ /* INTERNAL: Field Offsets */ /* (Cumulative bit offset: prev_offset + prev_width) */ diff --git a/src/J2735_toolkit.h b/src/J2735_toolkit.h index 7e81cf2..bd28417 100644 --- a/src/J2735_toolkit.h +++ b/src/J2735_toolkit.h @@ -42,7 +42,7 @@ J2735_PACK_START * @brief Container for BSMcoreData (37 bytes / 290 bits). * * @par BSMcoreData Wire Format (UPER): - * @code + * @verbatim * | Offset | Bits 7-0 | * |:-------|:-------------------------------------------------------------------------------------| * | +0 | MsgCount(7) | TemporaryID (Bit 31) | @@ -82,7 +82,7 @@ J2735_PACK_START * | +34 | VehicleSize (Bits 17-10) | * | +35 | VehicleSize (Bits 9-2) | * | +36 | VehicleSize (Bits 1-0) | - * @endcode + * @endverbatim */ struct J2735_BSMcoreData { uint8_t raw_bytes[J2735_SIZE_BSM_CORE_DATA]; @@ -93,11 +93,11 @@ typedef struct J2735_BSMcoreData J2735_BSMcoreData_t; /** * @struct J2735_SPAT_Header_t * @par SPAT Header Wire Format: - * @code + * @verbatim * | Octet 0 | Octet 1 | * |:-----------------------------------------|:-------------------------------| * | name_present(1) | intersections_pres(1) | moy_pres(1) | ts_pres(1) | ... | - * @endcode + * @endverbatim * * @todo Implement SPAT (Signal Phase and Timing) message parsing. * Currently unused - reserved for future development. diff --git a/tests/J2735_UPER_test.c b/tests/J2735_UPER_test.c index eaddf18..509a5de 100644 --- a/tests/J2735_UPER_test.c +++ b/tests/J2735_UPER_test.c @@ -29,8 +29,6 @@ * These are comprehensive edge-case tests covering all branches and error paths. */ -#include -#include #include #include "unity.h" @@ -69,13 +67,30 @@ /** * @brief Test length determinant short form: minimum value (0). * - * @par Encoding: - * - Short form: bit[0] = 0, bits[1:7] = 0 - * - Binary: 00000000 - * - * @par Expected: - * - Length: 0 - * - Bits consumed: 8 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: short (bit[0] = 0) + * - Length value: 0 (0x00) + * + * @par Wire Format (8 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|---------| + * | 0 | 1 | form | 0 | + * | 1 | 7 | length | 0000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------| + * | 0 | 0x00 | 00000000 | form(0) + length(0) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_short_form_min(void) { @@ -94,13 +109,30 @@ void test_inline_read_length_determinant_short_form_min(void) { /** * @brief Test length determinant short form: maximum value (127). * - * @par Encoding: - * - Short form: bit[0] = 0, bits[1:7] = 127 - * - Binary: 01111111 - * - * @par Expected: - * - Length: 127 - * - Bits consumed: 8 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: short (bit[0] = 0) + * - Length value: 127 (0x7F) + * + * @par Wire Format (8 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|---------| + * | 0 | 1 | form | 0 | + * | 1 | 7 | length | 1111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-----------------------| + * | 0 | 0x7F | 01111111 | form(0) + length(127) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_short_form_max(void) { @@ -119,13 +151,30 @@ void test_inline_read_length_determinant_short_form_max(void) { /** * @brief Test length determinant short form: typical value (1). * - * @par Encoding: - * - Short form: bit[0] = 0, bits[1:7] = 1 - * - Binary: 00000001 - * - * @par Expected: - * - Length: 1 - * - Bits consumed: 8 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: short (bit[0] = 0) + * - Length value: 1 (0x01) + * + * @par Wire Format (8 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|---------| + * | 0 | 1 | form | 0 | + * | 1 | 7 | length | 0000001 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------| + * | 0 | 0x01 | 00000001 | form(0) + length(1) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_short_form_typical(void) { @@ -144,13 +193,31 @@ void test_inline_read_length_determinant_short_form_typical(void) { /** * @brief Test length determinant long form: minimum value (0). * - * @par Encoding: - * - Long form: bits[0:1] = 10, bits[2:15] = 0 - * - Binary: 10000000 00000000 - * - * @par Expected: - * - Length: 0 - * - Bits consumed: 16 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: long (bits[0:1] = 10) + * - Length value: 0 (0x0000) + * + * @par Wire Format (16 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|----------------| + * | 0 | 2 | form | 10 | + * | 2 | 14 | length | 00000000000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------| + * | 0 | 0x80 | 10000000 | form(10) + length[13:8] | + * | 1 | 0x00 | 00000000 | length[7:0] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_long_form_min(void) { @@ -169,13 +236,31 @@ void test_inline_read_length_determinant_long_form_min(void) { /** * @brief Test length determinant long form: just above short form range (128). * - * @par Encoding: - * - Long form: bits[0:1] = 10, bits[2:15] = 128 - * - Binary: 10000000 10000000 - * - * @par Expected: - * - Length: 128 - * - Bits consumed: 16 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: long (bits[0:1] = 10) + * - Length value: 128 (0x0080) + * + * @par Wire Format (16 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|----------------| + * | 0 | 2 | form | 10 | + * | 2 | 14 | length | 00000010000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------| + * | 0 | 0x80 | 10000000 | form(10) + length[13:8] | + * | 1 | 0x80 | 10000000 | length[7:0] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_long_form_128(void) { @@ -194,13 +279,31 @@ void test_inline_read_length_determinant_long_form_128(void) { /** * @brief Test length determinant long form: maximum value (16383). * - * @par Encoding: - * - Long form: bits[0:1] = 10, bits[2:15] = 16383 - * - Binary: 10111111 11111111 - * - * @par Expected: - * - Length: 16383 - * - Bits consumed: 16 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: long (bits[0:1] = 10) + * - Length value: 16383 (0x3FFF) + * + * @par Wire Format (16 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|----------------| + * | 0 | 2 | form | 10 | + * | 2 | 14 | length | 11111111111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-----------------------------| + * | 0 | 0xBF | 10111111 | form(10) + length[13:8] | + * | 1 | 0xFF | 11111111 | length[7:0] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_long_form_max(void) { @@ -219,13 +322,29 @@ void test_inline_read_length_determinant_long_form_max(void) { /** * @brief Test length determinant fragmented form: returns error. * - * @par Encoding: - * - Fragmented: bits[0:1] = 11 - * - Binary: 11xxxxxx... - * - * @par Expected: - * - Length: 0 (error) - * - Bits consumed: 0 (error) + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Form: fragmented (bits[0:1] = 11) — unsupported, returns error + * - Expected: length = 0, bits consumed = 0 + * + * @par Wire Format (2+ bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-------|-------| + * | 0 | 2 | form | 11 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-----------------| + * | 0 | 0xC0 | 11000000 | form(11) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_fragmented_error(void) { @@ -244,13 +363,33 @@ void test_inline_read_length_determinant_fragmented_error(void) { /** * @brief Test length determinant with non-zero bit offset. * - * @par Encoding: - * - 3 bits padding + short form length=42 - * - Binary: xxx_00101010 (short form 0 + 42) - * - * @par Expected: - * - Length: 42 - * - Bits consumed: 8 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Bit offset: 3 + * - Form: short (bit[0] = 0) + * - Length value: 42 (0x2A) + * + * @par Wire Format (8 bits at offset 3): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|---------|---------| + * | 0 | 3 | padding | 111 | + * | 3 | 1 | form | 0 | + * | 4 | 7 | length | 0101010 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0xE5 | 11100101 | pad(111) + form(0) + length[6:3] | + * | 1 | 0x40 | 01000000 | length[2:0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_length_determinant_nonzero_bit_offset(void) { @@ -268,6 +407,53 @@ void test_inline_read_length_determinant_nonzero_bit_offset(void) { TEST_ASSERT_EQUAL_UINT16_MESSAGE(42U, length, "Length should be 42"); } +/** + * @brief Test read_length_determinant with deliberately misaligned buffer pointer. + * + * @par ASN.1 Definition: + * @code + * -- X.691 §11.9 Length Determinant (Unconstrained) + * length-determinant ::= CHOICE { + * short-form BIT STRING (SIZE(8)), -- 0xxxxxxx → 0..127 + * long-form BIT STRING (SIZE(16)), -- 10xxxxxx xxxxxxxx → 0..16383 + * fragmented BIT STRING (SIZE(8+)) -- 11xxxxxx... (unsupported) + * } + * @endcode + * + * @par Test Vector: + * - Buffer: &payload[1] (deliberately misaligned) + * - Form: short (bit[0] = 0) + * - Length value: 42 (0x2A) + * + * @par Wire Format (8 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|--------|---------| + * | 0 | 1 | form | 0 | + * | 1 | 7 | length | 0101010 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|----------------------| + * | 0 | 0xFF | 11111111 | junk (misalignment) | + * | 1 | 0x2A | 00101010 | form(0) + length(42) | + */ +/* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ +void test_inline_read_length_determinant_misaligned_access(void) { + static const uint8_t payload[] = { + 0xFF, /* junk byte for misalignment */ + 0x2A, /* 0_0101010: short form, length=42 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ + }; + const uint8_t *unaligned_ptr = &payload[1]; + + uint16_t length = TEST_POISON_U16; + uint8_t bits_consumed = j2735_internal_inline_read_length_determinant(unaligned_ptr, 0U, &length); + + TEST_ASSERT_EQUAL_UINT8_MESSAGE(8U, bits_consumed, + "Misaligned: should consume 8 bits for short form"); + TEST_ASSERT_EQUAL_UINT16_MESSAGE(42U, length, "Misaligned: length should be 42"); +} + /* ============================================================================ * j2735_internal_inline_read_nsnnwn() Tests * ============================================================================ @@ -280,13 +466,33 @@ void test_inline_read_length_determinant_nonzero_bit_offset(void) { /** * @brief Test nsnnwn small form: minimum value (0). * - * @par Encoding: - * - Small form: bit[0] = 0, bits[1:6] = 0 - * - Binary: 0_000000 - * - * @par Expected: + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: small (bit[0] = 0) * - Value: 0 - * - Bits consumed: 7 + * + * @par Wire Format (7 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-------|--------| + * | 0 | 1 | form | 0 | + * | 1 | 6 | value | 000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------| + * | 0 | 0x00 | 00000000 | form(0) + value(0) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_small_form_min(void) { @@ -305,13 +511,33 @@ void test_inline_read_nsnnwn_small_form_min(void) { /** * @brief Test nsnnwn small form: maximum value (63). * - * @par Encoding: - * - Small form: bit[0] = 0, bits[1:6] = 63 - * - Binary: 0_111111 - * - * @par Expected: - * - Value: 63 - * - Bits consumed: 7 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: small (bit[0] = 0) + * - Value: 63 (0x3F) + * + * @par Wire Format (7 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-------|--------| + * | 0 | 1 | form | 0 | + * | 1 | 6 | value | 111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|----------------------------| + * | 0 | 0x7E | 01111110 | form(0) + value(63) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_small_form_max(void) { @@ -330,13 +556,33 @@ void test_inline_read_nsnnwn_small_form_max(void) { /** * @brief Test nsnnwn small form: typical value (5). * - * @par Encoding: - * - Small form: bit[0] = 0, bits[1:6] = 5 - * - Binary: 0_000101 - * - * @par Expected: - * - Value: 5 - * - Bits consumed: 7 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: small (bit[0] = 0) + * - Value: 5 (0x05) + * + * @par Wire Format (7 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-------|--------| + * | 0 | 1 | form | 0 | + * | 1 | 6 | value | 000101 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------| + * | 0 | 0x0A | 00001010 | form(0) + value(5) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_small_form_typical(void) { @@ -355,15 +601,36 @@ void test_inline_read_nsnnwn_small_form_typical(void) { /** * @brief Test nsnnwn large form: minimum large value (64). * - * @par Encoding: - * - Large form: 1 + len_det(1 octet) + value(0x40) - * - Binary: 1_0000000_1 01000000 - * ^ ^ ^ - * large len=1 value=64 - * - * @par Expected: - * - Value: 64 - * - Bits consumed: 1 + 8 + 8 = 17 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 1 octet + * - Value: 64 (0x40) + * + * @par Wire Format (17 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000001 | + * | 9 | 8 | value | 01000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x80 | 10000000 | prefix(1) + length_det[7:1] | + * | 1 | 0xA0 | 10100000 | length_det[0] + value[7:1] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_64(void) { @@ -383,13 +650,36 @@ void test_inline_read_nsnnwn_large_form_64(void) { /** * @brief Test nsnnwn large form: value 100. * - * @par Encoding: - * - Large form: 1 + len_det(1 octet) + value(0x64) - * - Binary: 1_0000000_1 01100100 - * - * @par Expected: - * - Value: 100 - * - Bits consumed: 17 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 1 octet + * - Value: 100 (0x64) + * + * @par Wire Format (17 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000001 | + * | 9 | 8 | value | 01100100 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x80 | 10000000 | prefix(1) + length_det[7:1] | + * | 1 | 0xB2 | 10110010 | length_det[0] + value[7:1] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_100(void) { @@ -408,13 +698,37 @@ void test_inline_read_nsnnwn_large_form_100(void) { /** * @brief Test nsnnwn large form: value 255 (max 1 byte). * - * @par Encoding: - * - Large form: 1 + len_det(1 octet) + value(0xFF) - * - Binary: 1_0000000_1 11111111 - * - * @par Expected: - * - Value: 255 - * - Bits consumed: 17 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 1 octet + * - Value: 255 (0xFF) + * + * @par Wire Format (17 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000001 | + * | 9 | 8 | value | 11111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x80 | 10000000 | prefix(1) + length_det[7:1] | + * | 1 | 0xFF | 11111111 | length_det[0] + value[7:1] | + * | 2 | 0x80 | 10000000 | value[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_255(void) { @@ -433,15 +747,37 @@ void test_inline_read_nsnnwn_large_form_255(void) { /** * @brief Test nsnnwn large form: value 256 (needs 2 bytes). * - * @par Encoding: - * - Large form: 1 + len_det(2 octets) + value(0x0100) - * - Binary: 1_0000001_0 00000001 00000000 - * ^ ^ ^ - * large len=2 value=256 - * - * @par Expected: - * - Value: 256 - * - Bits consumed: 1 + 8 + 16 = 25 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 2 octets + * - Value: 256 (0x0100) + * + * @par Wire Format (25 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|------------------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000010 | + * | 9 | 16 | value | 0000000100000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x81 | 10000001 | prefix(1) + length_det[7:1] | + * | 1 | 0x00 | 00000000 | length_det[0] + value[15:9] | + * | 2 | 0x80 | 10000000 | value[8:1] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_256(void) { @@ -461,12 +797,38 @@ void test_inline_read_nsnnwn_large_form_256(void) { /** * @brief Test nsnnwn large form: value 65535 (max 2 bytes). * - * @par Encoding: - * - Large form: 1 + len_det(2 octets) + value(0xFFFF) - * - * @par Expected: - * - Value: 65535 - * - Bits consumed: 25 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 2 octets + * - Value: 65535 (0xFFFF) + * + * @par Wire Format (25 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|------------------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000010 | + * | 9 | 16 | value | 1111111111111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x81 | 10000001 | prefix(1) + length_det[7:1] | + * | 1 | 0x7F | 01111111 | length_det[0] + value[15:9] | + * | 2 | 0xFF | 11111111 | value[8:1] | + * | 3 | 0x80 | 10000000 | value[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_65535(void) { @@ -485,12 +847,39 @@ void test_inline_read_nsnnwn_large_form_65535(void) { /** * @brief Test nsnnwn large form: 4-byte value (max supported). * - * @par Encoding: - * - Large form: 1 + len_det(4 octets) + value(0x12345678) - * - * @par Expected: + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 4 octets * - Value: 0x12345678 - * - Bits consumed: 1 + 8 + 32 = 41 + * + * @par Wire Format (41 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------------------------------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000100 | + * | 9 | 32 | value | 00010010001101000101011001111000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x82 | 10000010 | prefix(1) + length_det[7:1] | + * | 1 | 0x09 | 00001001 | length_det[0] + value[31:25] | + * | 2 | 0x1A | 00011010 | value[24:17] | + * | 3 | 0x2B | 00101011 | value[16:9] | + * | 4 | 0x3C | 00111100 | value[8:1] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_4_bytes(void) { @@ -511,12 +900,35 @@ void test_inline_read_nsnnwn_large_form_4_bytes(void) { /** * @brief Test nsnnwn large form: 5-byte value (error - exceeds uint32_t). * - * @par Encoding: - * - Large form: 1 + len_det(5 octets) + ... - * - * @par Expected: - * - Value: 0 (error) - * - Bits consumed: 0 (error) + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: short form, 5 octets — exceeds uint32_t, returns error + * - Expected: value = 0, bits consumed = 0 + * + * @par Wire Format (9+ bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------| + * | 0 | 1 | prefix | 1 | + * | 1 | 8 | length_det | 00000101 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x82 | 10000010 | prefix(1) + length_det[7:1] | + * | 1 | 0x80 | 10000000 | length_det[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_large_form_5_bytes_error(void) { @@ -535,12 +947,34 @@ void test_inline_read_nsnnwn_large_form_5_bytes_error(void) { /** * @brief Test nsnnwn large form: fragmented length (error). * - * @par Encoding: - * - Large form: 1 + fragmented_len_det (11_...) - * - * @par Expected: - * - Value: 0 (error) - * - Bits consumed: 0 (error) + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Form: large (bit[0] = 1) + * - Length determinant: fragmented form (bits[1:2] = 11) — unsupported, returns error + * - Expected: value = 0, bits consumed = 0 + * + * @par Wire Format (3+ bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|----------------|-------| + * | 0 | 1 | prefix | 1 | + * | 1 | 2 | length_det_tag | 11 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-----------------------------------| + * | 0 | 0xE0 | 11100000 | prefix(1) + length_det(11) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_fragmented_error(void) { @@ -559,19 +993,36 @@ void test_inline_read_nsnnwn_fragmented_error(void) { /** * @brief Test nsnnwn with non-zero bit offset. * - * @par Encoding: - * - 3 bits padding + small form value=31 - * - Binary: xxx_0_011111 (7 bits: small form 0 + 31) - * - * @par Layout (10 bits): - * - Bits 0-2: 111 (padding) - * - Bit 3: 0 (small form) - * - Bits 4-9: 011111 (value 31) - * - Bytes: 0xE7, 0xC0 = 1110_0111 1100_0000 - * - * @par Expected: - * - Value: 31 - * - Bits consumed: 7 + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Bit offset: 3 + * - Form: small (bit[0] = 0) + * - Value: 31 (0x1F) + * + * @par Wire Format (7 bits at offset 3): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|---------|--------| + * | 0 | 3 | padding | 111 | + * | 3 | 1 | form | 0 | + * | 4 | 6 | value | 011111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|------------------------------------| + * | 0 | 0xE7 | 11100111 | pad(111) + form(0) + value[5:2] | + * | 1 | 0xC0 | 11000000 | value[1:0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_read_nsnnwn_nonzero_bit_offset(void) { @@ -588,6 +1039,56 @@ void test_inline_read_nsnnwn_nonzero_bit_offset(void) { TEST_ASSERT_EQUAL_UINT32_MESSAGE(31U, value, "Value should be 31"); } +/** + * @brief Test read_nsnnwn with deliberately misaligned buffer pointer. + * + * @par ASN.1 Definition: + * @code + * -- X.691 §11.6 Normally-Small Non-Negative Whole Number + * nsnnwn ::= CHOICE { + * small-form BIT STRING (SIZE(7)), -- 0xxxxxx → 0..63 + * large-form SEQUENCE { -- 1 + length_det + value + * prefix BIT STRING (SIZE(1)), -- always 1 + * length length-determinant, -- octet count + * value OCTET STRING (SIZE(1..4)) -- big-endian unsigned + * } + * } + * @endcode + * + * @par Test Vector: + * - Buffer: &payload[1] (deliberately misaligned) + * - Form: small (bit[0] = 0) + * - Value: 10 (0x0A) + * + * @par Wire Format (7 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-------|--------| + * | 0 | 1 | form | 0 | + * | 1 | 6 | value | 001010 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|----------------------------| + * | 0 | 0xFF | 11111111 | junk (misalignment) | + * | 1 | 0x14 | 00010100 | form(0) + value(10) + pad | + */ +/* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ +void test_inline_read_nsnnwn_misaligned_access(void) { + static const uint8_t payload[] = { + 0xFF, /* junk byte for misalignment */ + 0x14, /* 0_001010_0: small form, value=10 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ + }; + const uint8_t *unaligned_ptr = &payload[1]; + + uint32_t value = TEST_POISON_U32; + uint8_t bits_consumed = j2735_internal_inline_read_nsnnwn(unaligned_ptr, 0U, &value); + + TEST_ASSERT_EQUAL_UINT8_MESSAGE(7U, bits_consumed, + "Misaligned: should consume 7 bits for small form"); + TEST_ASSERT_EQUAL_UINT32_MESSAGE(10U, value, "Misaligned: value should be 10"); +} + /* ============================================================================ * j2735_internal_inline_skip_extensions() Tests * ============================================================================ @@ -601,13 +1102,34 @@ void test_inline_read_nsnnwn_nonzero_bit_offset(void) { /** * @brief Test skip extensions: 1 slot defined, none present. * - * @par Encoding: - * - nsnnwn = 0 (small form, 7 bits) → 1 extension slot - * - bitmap = 0 (1 bit) → extension not present - * - * @par Expected: - * - Extension bits: 7 + 1 = 8 - * - Result: 0 (success) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 0 (1 bit) → extension not present + * + * @par Wire Format (8 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|--------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000000 | + * | 7 | 1 | bitmap | 0 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------------| + * | 0 | 0x00 | 00000000 | nsn_form(0) + count(0) + bmp(0) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_one_slot_none_present(void) { @@ -626,24 +1148,40 @@ void test_inline_skip_extensions_one_slot_none_present(void) { /** * @brief Test skip extensions: 1 slot defined, present with 1 byte content. * - * @par Encoding: - * - nsnnwn = 0 (7 bits) → 1 extension slot - * - bitmap = 1 (1 bit) → extension present - * - length = 1 (8 bits, short form) - * - content = 0xAB (8 bits) - * - * @par Layout (24 bits): - * - Bits 0-6: 0_000000 (nsnnwn=0) - * - Bit 7: 1 (bitmap=1) - * - Bits 8-15: 0_0000001 (length=1) - * - Bits 16-23: 10101011 (content=0xAB) - * - Byte 0: 0_000000_1 = 0x01 - * - Byte 1: 0_0000001 = 0x01 - * - Byte 2: 10101011 = 0xAB - * - * @par Expected: - * - Extension bits: 7 + 1 + 8 + 8 = 24 - * - Result: 0 (success) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 1 (1 bit) → extension present + * - length: 1 byte (short form) + * - content: 0xAB + * + * @par Wire Format (24 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|----------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000000 | + * | 7 | 1 | bitmap | 1 | + * | 8 | 8 | length | 00000001 | + * | 16 | 8 | content | 10101011 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------------| + * | 0 | 0x01 | 00000001 | nsn_form(0) + count(0) + bmp(1) | + * | 1 | 0x01 | 00000001 | length(1) | + * | 2 | 0xAB | 10101011 | content(0xAB) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_one_slot_present(void) { @@ -662,19 +1200,46 @@ void test_inline_skip_extensions_one_slot_present(void) { /** * @brief Test skip extensions: 2 slots defined, both present. * - * @par Encoding: - * - nsnnwn = 1 (7 bits) → 2 extension slots - * - bitmap = 11 (2 bits) → both present + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 1 (small form) → 2 extension slots + * - bitmap: 11 (2 bits) → both present * - ext0: length = 1, content = 0xAA (16 bits) * - ext1: length = 2, content = 0xBBCC (24 bits) * - * @par Layout (49 bits): - * - Bit stream: 0000001 11 00000001 10101010 00000010 1011101111001100 - * - Bytes: 0x03 0x80 0xD5 0x01 0x5D 0xE6 0x00 - * - * @par Expected: - * - Extension bits: 7 + 2 + 16 + 24 = 49 - * - Result: 0 (success) + * @par Wire Format (49 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|------------------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000001 | + * | 7 | 2 | bitmap | 11 | + * | 9 | 8 | ext0_len | 00000001 | + * | 17 | 8 | ext0_data | 10101010 | + * | 25 | 8 | ext1_len | 00000010 | + * | 33 | 16 | ext1_data | 1011101111001100 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0x03 | 00000011 | nsn_form(0) + count(1) + bitmap[1] | + * | 1 | 0x80 | 10000000 | bitmap[0] + ext0_len[7:1] | + * | 2 | 0xD5 | 11010101 | ext0_len[0] + ext0_data[7:1] | + * | 3 | 0x01 | 00000001 | ext0_data[0] + ext1_len[7:1] | + * | 4 | 0x5D | 01011101 | ext1_len[0] + ext1_data[15:9] | + * | 5 | 0xE6 | 11100110 | ext1_data[8:1] | + * | 6 | 0x00 | 00000000 | ext1_data[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_two_slots_both_present(void) { @@ -694,19 +1259,45 @@ void test_inline_skip_extensions_two_slots_both_present(void) { /** * @brief Test skip extensions: 2 slots defined, first only present. * - * @par Encoding: - * - nsnnwn = 1 (7 bits) → 2 extension slots - * - bitmap = 10 (2 bits) → first present, second absent + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 1 (small form) → 2 extension slots + * - bitmap: 10 (2 bits) → first present, second absent * - ext0: length = 1, content = 0xAA (16 bits) * - * @par Expected: - * - Extension bits: 7 + 2 + 16 = 25 - * - Result: 0 (success) + * @par Wire Format (25 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|----------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000001 | + * | 7 | 2 | bitmap | 10 | + * | 9 | 8 | ext0_len | 00000001 | + * | 17 | 8 | ext0_data | 10101010 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0x03 | 00000011 | nsn_form(0) + count(1) + bitmap[1] | + * | 1 | 0x00 | 00000000 | bitmap[0] + ext0_len[7:1] | + * | 2 | 0xD5 | 11010101 | ext0_len[0] + ext0_data[7:1] | + * | 3 | 0x00 | 00000000 | ext0_data[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_two_slots_first_only(void) { static const uint8_t buf[] = { - 0x02, 0x80, 0xAA, /* 0_000001 0_1_000000 0_1010101 0: nsnnwn=1,bitmap=10,len=1,cont */ + 0x03, 0x00, 0xD5, 0x00, /* nsnnwn=1,bitmap=10,len=1,content=0xAA */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; @@ -720,18 +1311,40 @@ void test_inline_skip_extensions_two_slots_first_only(void) { /** * @brief Test skip extensions: 2 slots defined, second only present. * - * @par Encoding: - * - nsnnwn = 1 (7 bits) → 2 extension slots - * - bitmap = 01 (2 bits) → first absent, second present + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 1 (small form) → 2 extension slots + * - bitmap: 01 (2 bits) → first absent, second present * - ext1: length = 1, content = 0xBB (16 bits) * - * @par Layout (25 bits): - * - Bit stream: 0000001 01 00000001 10111011 - * - Bytes: 0x02 0x80 0xDD 0x80 - * - * @par Expected: - * - Extension bits: 7 + 2 + 16 = 25 - * - Result: 0 (success) + * @par Wire Format (25 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|----------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000001 | + * | 7 | 2 | bitmap | 01 | + * | 9 | 8 | ext1_len | 00000001 | + * | 17 | 8 | ext1_data | 10111011 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0x02 | 00000010 | nsn_form(0) + count(1) + bitmap[1] | + * | 1 | 0x80 | 10000000 | bitmap[0] + ext1_len[7:1] | + * | 2 | 0xDD | 11011101 | ext1_len[0] + ext1_data[7:1] | + * | 3 | 0x80 | 10000000 | ext1_data[0] + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_two_slots_second_only(void) { @@ -750,14 +1363,37 @@ void test_inline_skip_extensions_two_slots_second_only(void) { /** * @brief Test skip extensions: empty content (length = 0). * - * @par Encoding: - * - nsnnwn = 0 (7 bits) → 1 extension slot - * - bitmap = 1 (1 bit) → present - * - length = 0 (8 bits) → empty content - * - * @par Expected: - * - Extension bits: 7 + 1 + 8 + 0 = 16 - * - Result: 0 (success) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 1 (1 bit) → present + * - length: 0 bytes (short form) → empty content + * + * @par Wire Format (16 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|----------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000000 | + * | 7 | 1 | bitmap | 1 | + * | 8 | 8 | length | 00000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------------| + * | 0 | 0x01 | 00000001 | nsn_form(0) + count(0) + bmp(1) | + * | 1 | 0x00 | 00000000 | length(0) | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_empty_content(void) { @@ -776,12 +1412,33 @@ void test_inline_skip_extensions_empty_content(void) { /** * @brief Test skip extensions: nsnnwn parse error. * - * @par Encoding: - * - nsnnwn large form with fragmented length - * - * @par Expected: - * - Extension bits: 0 (error) - * - Result: -1 (error) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): large form with fragmented length — returns error + * - Expected: ext_bits = 0, result = -1 + * + * @par Wire Format (3+ bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|----------------|-------| + * | 0 | 1 | nsn_prefix | 1 | + * | 1 | 2 | length_det_tag | 11 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0xE0 | 11100000 | nsn_prefix(1) + len_det(11) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_nsnnwn_error(void) { @@ -800,13 +1457,38 @@ void test_inline_skip_extensions_nsnnwn_error(void) { /** * @brief Test skip extensions: length determinant error in open type. * - * @par Encoding: - * - nsnnwn = 0, bitmap = 1 (extension present) - * - length = fragmented (error) - * - * @par Expected: - * - Extension bits: 0 (error) - * - Result: -1 (error) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 1 (1 bit) → extension present + * - length: fragmented form (bits[0:1] = 11) — returns error + * - Expected: ext_bits = 0, result = -1 + * + * @par Wire Format (10+ bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|--------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000000 | + * | 7 | 1 | bitmap | 1 | + * | 8 | 2 | len_det_tag| 11 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0x01 | 00000001 | nsn_form(0) + count(0) + bmp(1) | + * | 1 | 0xE0 | 11100000 | len_det(11) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_length_error(void) { @@ -825,12 +1507,37 @@ void test_inline_skip_extensions_length_error(void) { /** * @brief Test skip extensions: non-zero start offset. * - * @par Encoding: - * - 5 bits padding + nsnnwn = 0, bitmap = 0 - * - * @par Expected: - * - Extension bits: 8 - * - Result: 0 (success) + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - Bit offset: 5 + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 0 (1 bit) → extension not present + * + * @par Wire Format (8 bits at offset 5): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|--------| + * | 0 | 5 | padding | 11111 | + * | 5 | 1 | nsn_form | 0 | + * | 6 | 6 | ext_count | 000000 | + * | 12 | 1 | bitmap | 0 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0xF8 | 11111000 | pad(11111) + nsn_form(0) + count[5:4]| + * | 1 | 0x00 | 00000000 | count[3:0] + bmp(0) + pad | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_nonzero_offset(void) { @@ -849,12 +1556,35 @@ void test_inline_skip_extensions_nonzero_offset(void) { /** * @brief Test skip extensions: ext_count > 63 returns error. * - * @par Encoding: - * - Large form nsnnwn with value 64 (too many extensions for 64-bit bitmap) - * - * @par Expected: - * - Result: -1 (error, ext_count > 63 exceeds bitmap capacity) - * - Extension bits: 0 + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - ext-count (nsnnwn): 64 (large form) — exceeds 63-bit bitmap, returns error + * - Expected: ext_bits = 0, result = -1 + * + * @par Wire Format (17 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|------------|----------| + * | 0 | 1 | nsn_prefix | 1 | + * | 1 | 8 | length_det | 00000001 | + * | 9 | 8 | value | 01000000 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|-------------------------------------| + * | 0 | 0x80 | 10000000 | nsn_prefix(1) + length_det[7:1] | + * | 1 | 0xA0 | 10100000 | length_det[0] + value[7:1] | */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_inline_skip_extensions_too_many_extensions(void) { @@ -862,9 +1592,9 @@ void test_inline_skip_extensions_too_many_extensions(void) { * bit[0] = 1 (large form) * length_det = short form, length = 1 byte * value = 64 = 0x40 - * Binary: 1_00000001_01000000 = 0x81, 0x40 */ + * Binary: 1_00000001_01000000 = 0x80, 0xA0 */ static const uint8_t buf[] = { - 0x81, 0x40, /* large form nsnnwn = 64 */ + 0x80, 0xA0, /* large form nsnnwn = 64 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; @@ -875,6 +1605,72 @@ void test_inline_skip_extensions_too_many_extensions(void) { TEST_ASSERT_EQUAL_UINT32_MESSAGE(0U, ext_bits, "Ext bits should be 0 on error"); } +/** + * @brief Test skip_extensions with deliberately misaligned buffer pointer. + * + * @par ASN.1 Definition: + * @code + * -- X.691 §18.8 Extension Additions + * extension-additions ::= SEQUENCE { + * ext-count nsnnwn, -- number of extensions - 1 + * bitmap BIT STRING (SIZE(ext-count+1)),-- presence bitmap + * extensions SEQUENCE (SIZE(0..ext-count)) OF SEQUENCE { + * length length-determinant, -- open type wrapper + * content OCTET STRING (SIZE(length)) -- extension content + * } + * } + * @endcode + * + * @par Test Vector: + * - Buffer: &payload[1] (deliberately misaligned) + * - ext-count (nsnnwn): 0 (small form) → 1 extension slot + * - bitmap: 1 (1 bit) → extension present + * - length: 1 byte (short form) + * - content: 0xAB + * + * @par Wire Format (24 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|-----------|----------| + * | 0 | 1 | nsn_form | 0 | + * | 1 | 6 | ext_count | 000000 | + * | 7 | 1 | bitmap | 1 | + * | 8 | 8 | length | 00000001 | + * | 16 | 8 | content | 10101011 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|---------------------------------| + * | 0 | 0xFF | 11111111 | junk (misalignment) | + * | 1 | 0x01 | 00000001 | nsn_form(0) + count(0) + bmp(1) | + * | 2 | 0x01 | 00000001 | length(1) | + * | 3 | 0xAB | 10101011 | content(0xAB) | + */ +/* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ +void test_inline_skip_extensions_misaligned_access(void) { + /* Same encoding as test_inline_skip_extensions_one_slot_present: + * nsnnwn small form: 0_000000 = ext_count 0 → 1 slot + * bitmap: 1 = extension[0] present + * length_det short form: 0_0000001 = 1 byte + * extension content: 0xAB (8 bits) + * Binary: 0000000_1_00000001_10101011 + * Bytes: 00000001 00000001 10101011 + * 0x01 0x01 0xAB */ + static const uint8_t payload[] = { + 0xFF, /* junk byte for misalignment */ + 0x01, /* nsnnwn=0 + bitmap=1 */ + 0x01, /* length_det=00000001 (1 byte) */ + 0xAB, /* extension content (8 bits) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ + }; + const uint8_t *unaligned_ptr = &payload[1]; + + uint32_t ext_bits = TEST_POISON_U32; + int result = j2735_internal_inline_skip_extensions(unaligned_ptr, 0U, &ext_bits); + + TEST_ASSERT_EQUAL_INT_MESSAGE(0, result, "Misaligned: should succeed"); + TEST_ASSERT_EQUAL_UINT32_MESSAGE(24U, ext_bits, "Misaligned: should consume 24 bits (7+1+8+8)"); +} + /* ============================================================================ * Test Suite Runner * ============================================================================ @@ -890,6 +1686,7 @@ void run_testsuite_uper(void) { RUN_TEST(test_inline_read_length_determinant_long_form_max); RUN_TEST(test_inline_read_length_determinant_fragmented_error); RUN_TEST(test_inline_read_length_determinant_nonzero_bit_offset); + RUN_TEST(test_inline_read_length_determinant_misaligned_access); /* j2735_internal_inline_read_nsnnwn tests */ RUN_TEST(test_inline_read_nsnnwn_small_form_min); @@ -904,6 +1701,7 @@ void run_testsuite_uper(void) { RUN_TEST(test_inline_read_nsnnwn_large_form_5_bytes_error); RUN_TEST(test_inline_read_nsnnwn_fragmented_error); RUN_TEST(test_inline_read_nsnnwn_nonzero_bit_offset); + RUN_TEST(test_inline_read_nsnnwn_misaligned_access); /* j2735_internal_inline_skip_extensions tests */ RUN_TEST(test_inline_skip_extensions_one_slot_none_present); @@ -916,4 +1714,5 @@ void run_testsuite_uper(void) { RUN_TEST(test_inline_skip_extensions_length_error); RUN_TEST(test_inline_skip_extensions_nonzero_offset); RUN_TEST(test_inline_skip_extensions_too_many_extensions); + RUN_TEST(test_inline_skip_extensions_misaligned_access); } diff --git a/tests/J2735_UPER_test.h b/tests/J2735_UPER_test.h index a9627f0..e5cc4d5 100644 --- a/tests/J2735_UPER_test.h +++ b/tests/J2735_UPER_test.h @@ -39,6 +39,7 @@ void test_inline_read_length_determinant_long_form_128(void); void test_inline_read_length_determinant_long_form_max(void); void test_inline_read_length_determinant_fragmented_error(void); void test_inline_read_length_determinant_nonzero_bit_offset(void); +void test_inline_read_length_determinant_misaligned_access(void); /* j2735_internal_inline_read_nsnnwn() tests */ void test_inline_read_nsnnwn_small_form_min(void); @@ -53,6 +54,7 @@ void test_inline_read_nsnnwn_large_form_4_bytes(void); void test_inline_read_nsnnwn_large_form_5_bytes_error(void); void test_inline_read_nsnnwn_fragmented_error(void); void test_inline_read_nsnnwn_nonzero_bit_offset(void); +void test_inline_read_nsnnwn_misaligned_access(void); /* j2735_internal_inline_skip_extensions() tests */ void test_inline_skip_extensions_one_slot_none_present(void); @@ -65,6 +67,7 @@ void test_inline_skip_extensions_nsnnwn_error(void); void test_inline_skip_extensions_length_error(void); void test_inline_skip_extensions_nonzero_offset(void); void test_inline_skip_extensions_too_many_extensions(void); +void test_inline_skip_extensions_misaligned_access(void); /** * @brief Run all UPER primitive tests. diff --git a/tests/J2735_internal_DE_AllowedManeuvers_test.h b/tests/J2735_internal_DE_AllowedManeuvers_test.h index 101370b..c965757 100644 --- a/tests/J2735_internal_DE_AllowedManeuvers_test.h +++ b/tests/J2735_internal_DE_AllowedManeuvers_test.h @@ -24,8 +24,8 @@ * AllowedManeuvers is SIZE(12): a fixed BIT STRING with 12 bits. */ -#ifndef J2735_DE_INTERNAL_ALLOWEDMANEUVERS_TEST_H -#define J2735_DE_INTERNAL_ALLOWEDMANEUVERS_TEST_H +#ifndef J2735_INTERNAL_DE_ALLOWEDMANEUVERS_TEST_H +#define J2735_INTERNAL_DE_ALLOWEDMANEUVERS_TEST_H /* Core tests */ void test_allowed_maneuvers_all_zeros(void); @@ -45,4 +45,4 @@ void test_allowed_maneuvers_misaligned_access(void); void run_testsuite_allowed_maneuvers(void); -#endif /* J2735_DE_INTERNAL_ALLOWEDMANEUVERS_TEST_H */ +#endif /* J2735_INTERNAL_DE_ALLOWEDMANEUVERS_TEST_H */ diff --git a/tests/J2735_internal_DE_BrakeAppliedStatus_test.h b/tests/J2735_internal_DE_BrakeAppliedStatus_test.h index 8399bd6..6d470f7 100644 --- a/tests/J2735_internal_DE_BrakeAppliedStatus_test.h +++ b/tests/J2735_internal_DE_BrakeAppliedStatus_test.h @@ -24,8 +24,8 @@ * BrakeAppliedStatus is SIZE(5): a fixed BIT STRING with 5 bits. */ -#ifndef J2735_DE_INTERNAL_BRAKEAPPLIEDSTATUS_TEST_H -#define J2735_DE_INTERNAL_BRAKEAPPLIEDSTATUS_TEST_H +#ifndef J2735_INTERNAL_DE_BRAKEAPPLIEDSTATUS_TEST_H +#define J2735_INTERNAL_DE_BRAKEAPPLIEDSTATUS_TEST_H /* Core tests */ void test_brake_applied_status_all_zeros(void); @@ -44,4 +44,4 @@ void test_brake_applied_status_misaligned_access(void); void run_testsuite_brake_applied_status(void); -#endif /* J2735_DE_INTERNAL_BRAKEAPPLIEDSTATUS_TEST_H */ +#endif /* J2735_INTERNAL_DE_BRAKEAPPLIEDSTATUS_TEST_H */ diff --git a/tests/J2735_internal_DE_ExteriorLights_test.h b/tests/J2735_internal_DE_ExteriorLights_test.h index bc1573a..851ea50 100644 --- a/tests/J2735_internal_DE_ExteriorLights_test.h +++ b/tests/J2735_internal_DE_ExteriorLights_test.h @@ -25,8 +25,8 @@ * 9-bit root and 9-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_EXTERIORLIGHTS_TEST_H -#define J2735_DE_INTERNAL_EXTERIORLIGHTS_TEST_H +#ifndef J2735_INTERNAL_DE_EXTERIORLIGHTS_TEST_H +#define J2735_INTERNAL_DE_EXTERIORLIGHTS_TEST_H /* Basic form tests */ void test_exterior_lights_non_extended(void); @@ -55,4 +55,4 @@ void test_exterior_lights_misaligned_access(void); void run_testsuite_exterior_lights(void); -#endif /* J2735_DE_INTERNAL_EXTERIORLIGHTS_TEST_H */ +#endif /* J2735_INTERNAL_DE_EXTERIORLIGHTS_TEST_H */ diff --git a/tests/J2735_internal_DE_GNSSstatus_test.h b/tests/J2735_internal_DE_GNSSstatus_test.h index 64f4b62..23590a0 100644 --- a/tests/J2735_internal_DE_GNSSstatus_test.h +++ b/tests/J2735_internal_DE_GNSSstatus_test.h @@ -24,8 +24,8 @@ * GNSSstatus is SIZE(8): a fixed BIT STRING with 8 bits. */ -#ifndef J2735_DE_INTERNAL_GNSSSTATUS_TEST_H -#define J2735_DE_INTERNAL_GNSSSTATUS_TEST_H +#ifndef J2735_INTERNAL_DE_GNSSSTATUS_TEST_H +#define J2735_INTERNAL_DE_GNSSSTATUS_TEST_H /* Core tests */ void test_gnss_status_all_zeros(void); @@ -45,4 +45,4 @@ void test_gnss_status_misaligned_access(void); void run_testsuite_gnss_status(void); -#endif /* J2735_DE_INTERNAL_GNSSSTATUS_TEST_H */ +#endif /* J2735_INTERNAL_DE_GNSSSTATUS_TEST_H */ diff --git a/tests/J2735_internal_DE_LaneDirection_test.h b/tests/J2735_internal_DE_LaneDirection_test.h index ad4cf23..984ac77 100644 --- a/tests/J2735_internal_DE_LaneDirection_test.h +++ b/tests/J2735_internal_DE_LaneDirection_test.h @@ -24,8 +24,8 @@ * LaneDirection is SIZE(2): a fixed BIT STRING with 2 bits. */ -#ifndef J2735_DE_INTERNAL_LANEDIRECTION_TEST_H -#define J2735_DE_INTERNAL_LANEDIRECTION_TEST_H +#ifndef J2735_INTERNAL_DE_LANEDIRECTION_TEST_H +#define J2735_INTERNAL_DE_LANEDIRECTION_TEST_H /* Core tests */ void test_lane_direction_all_zeros(void); @@ -42,4 +42,4 @@ void test_lane_direction_misaligned_access(void); void run_testsuite_lane_direction(void); -#endif /* J2735_DE_INTERNAL_LANEDIRECTION_TEST_H */ +#endif /* J2735_INTERNAL_DE_LANEDIRECTION_TEST_H */ diff --git a/tests/J2735_internal_DE_LaneSharing_test.h b/tests/J2735_internal_DE_LaneSharing_test.h index 21360e0..18d8cfc 100644 --- a/tests/J2735_internal_DE_LaneSharing_test.h +++ b/tests/J2735_internal_DE_LaneSharing_test.h @@ -24,8 +24,8 @@ * LaneSharing is SIZE(10): a fixed BIT STRING with 10 bits. */ -#ifndef J2735_DE_INTERNAL_LANESHARING_TEST_H -#define J2735_DE_INTERNAL_LANESHARING_TEST_H +#ifndef J2735_INTERNAL_DE_LANESHARING_TEST_H +#define J2735_INTERNAL_DE_LANESHARING_TEST_H /* Core tests */ void test_lane_sharing_all_zeros(void); @@ -45,4 +45,4 @@ void test_lane_sharing_misaligned_access(void); void run_testsuite_lane_sharing(void); -#endif /* J2735_DE_INTERNAL_LANESHARING_TEST_H */ +#endif /* J2735_INTERNAL_DE_LANESHARING_TEST_H */ diff --git a/tests/J2735_internal_DE_PersonalAssistive_test.h b/tests/J2735_internal_DE_PersonalAssistive_test.h index 411eb91..084f830 100644 --- a/tests/J2735_internal_DE_PersonalAssistive_test.h +++ b/tests/J2735_internal_DE_PersonalAssistive_test.h @@ -25,8 +25,8 @@ * 6-bit root and 6-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_PERSONALASSISTIVE_TEST_H -#define J2735_DE_INTERNAL_PERSONALASSISTIVE_TEST_H +#ifndef J2735_INTERNAL_DE_PERSONALASSISTIVE_TEST_H +#define J2735_INTERNAL_DE_PERSONALASSISTIVE_TEST_H /* Basic form tests */ void test_personal_assistive_non_extended(void); @@ -55,4 +55,4 @@ void test_personal_assistive_misaligned_access(void); void run_testsuite_personal_assistive(void); -#endif /* J2735_DE_INTERNAL_PERSONALASSISTIVE_TEST_H */ +#endif /* J2735_INTERNAL_DE_PERSONALASSISTIVE_TEST_H */ diff --git a/tests/J2735_internal_DE_PersonalDeviceUsageState_test.h b/tests/J2735_internal_DE_PersonalDeviceUsageState_test.h index 0623c64..4d90752 100644 --- a/tests/J2735_internal_DE_PersonalDeviceUsageState_test.h +++ b/tests/J2735_internal_DE_PersonalDeviceUsageState_test.h @@ -25,8 +25,8 @@ * 9-bit root and 9-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_PERSONALDEVICEUSAGESTATE_TEST_H -#define J2735_DE_INTERNAL_PERSONALDEVICEUSAGESTATE_TEST_H +#ifndef J2735_INTERNAL_DE_PERSONALDEVICEUSAGESTATE_TEST_H +#define J2735_INTERNAL_DE_PERSONALDEVICEUSAGESTATE_TEST_H /* Basic form tests */ void test_personal_device_usage_state_non_extended(void); @@ -55,4 +55,4 @@ void test_personal_device_usage_state_misaligned_access(void); void run_testsuite_personal_device_usage_state(void); -#endif /* J2735_DE_INTERNAL_PERSONALDEVICEUSAGESTATE_TEST_H */ +#endif /* J2735_INTERNAL_DE_PERSONALDEVICEUSAGESTATE_TEST_H */ diff --git a/tests/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity_test.h b/tests/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity_test.h index cb33591..655875a 100644 --- a/tests/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity_test.h +++ b/tests/J2735_internal_DE_PublicSafetyAndRoadWorkerActivity_test.h @@ -25,8 +25,8 @@ * with 6-bit root and 6-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H -#define J2735_DE_INTERNAL_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H +#ifndef J2735_INTERNAL_DE_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H +#define J2735_INTERNAL_DE_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H /* Basic form tests */ void test_public_safety_and_road_worker_activity_non_extended(void); @@ -55,4 +55,4 @@ void test_public_safety_and_road_worker_activity_misaligned_access(void); void run_testsuite_public_safety_and_road_worker_activity(void); -#endif /* J2735_DE_INTERNAL_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H */ +#endif /* J2735_INTERNAL_DE_PUBLICSAFETYANDROADWORKERACTIVITY_TEST_H */ diff --git a/tests/J2735_internal_DE_PublicSafetyDirectingTrafficSubType_test.h b/tests/J2735_internal_DE_PublicSafetyDirectingTrafficSubType_test.h index 3617338..1257907 100644 --- a/tests/J2735_internal_DE_PublicSafetyDirectingTrafficSubType_test.h +++ b/tests/J2735_internal_DE_PublicSafetyDirectingTrafficSubType_test.h @@ -25,8 +25,8 @@ * with 7-bit root and 7-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H -#define J2735_DE_INTERNAL_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H +#ifndef J2735_INTERNAL_DE_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H +#define J2735_INTERNAL_DE_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H /* Basic form tests */ void test_public_safety_directing_traffic_sub_type_non_extended(void); @@ -55,4 +55,4 @@ void test_public_safety_directing_traffic_sub_type_misaligned_access(void); void run_testsuite_public_safety_directing_traffic_sub_type(void); -#endif /* J2735_DE_INTERNAL_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H */ +#endif /* J2735_INTERNAL_DE_PUBLICSAFETYDIRECTINGTRAFFICSUBTYPE_TEST_H */ diff --git a/tests/J2735_internal_DE_TrafficLightOperationStatus_test.h b/tests/J2735_internal_DE_TrafficLightOperationStatus_test.h index 193b32e..521b688 100644 --- a/tests/J2735_internal_DE_TrafficLightOperationStatus_test.h +++ b/tests/J2735_internal_DE_TrafficLightOperationStatus_test.h @@ -25,8 +25,8 @@ * 8-bit root and 8-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_TRAFFICLIGHTOPERATIONSTATUS_TEST_H -#define J2735_DE_INTERNAL_TRAFFICLIGHTOPERATIONSTATUS_TEST_H +#ifndef J2735_INTERNAL_DE_TRAFFICLIGHTOPERATIONSTATUS_TEST_H +#define J2735_INTERNAL_DE_TRAFFICLIGHTOPERATIONSTATUS_TEST_H /* Basic form tests */ void test_traffic_light_operation_status_non_extended(void); @@ -55,4 +55,4 @@ void test_traffic_light_operation_status_misaligned_access(void); void run_testsuite_traffic_light_operation_status(void); -#endif /* J2735_DE_INTERNAL_TRAFFICLIGHTOPERATIONSTATUS_TEST_H */ +#endif /* J2735_INTERNAL_DE_TRAFFICLIGHTOPERATIONSTATUS_TEST_H */ diff --git a/tests/J2735_internal_DE_TransitStatus_test.h b/tests/J2735_internal_DE_TransitStatus_test.h index 9fb94d9..ae9b7d8 100644 --- a/tests/J2735_internal_DE_TransitStatus_test.h +++ b/tests/J2735_internal_DE_TransitStatus_test.h @@ -24,8 +24,8 @@ * TransitStatus is SIZE(6): a fixed BIT STRING with 6 bits. */ -#ifndef J2735_DE_INTERNAL_TRANSITSTATUS_TEST_H -#define J2735_DE_INTERNAL_TRANSITSTATUS_TEST_H +#ifndef J2735_INTERNAL_DE_TRANSITSTATUS_TEST_H +#define J2735_INTERNAL_DE_TRANSITSTATUS_TEST_H /* Core tests */ void test_transit_status_all_zeros(void); @@ -44,4 +44,4 @@ void test_transit_status_misaligned_access(void); void run_testsuite_transit_status(void); -#endif /* J2735_DE_INTERNAL_TRANSITSTATUS_TEST_H */ +#endif /* J2735_INTERNAL_DE_TRANSITSTATUS_TEST_H */ diff --git a/tests/J2735_internal_DE_UserSizeAndBehaviour_test.h b/tests/J2735_internal_DE_UserSizeAndBehaviour_test.h index ab4de30..29cf702 100644 --- a/tests/J2735_internal_DE_UserSizeAndBehaviour_test.h +++ b/tests/J2735_internal_DE_UserSizeAndBehaviour_test.h @@ -25,8 +25,8 @@ * 5-bit root and 5-bit extension (same size). */ -#ifndef J2735_DE_INTERNAL_USERSIZEANDBEHAVIOUR_TEST_H -#define J2735_DE_INTERNAL_USERSIZEANDBEHAVIOUR_TEST_H +#ifndef J2735_INTERNAL_DE_USERSIZEANDBEHAVIOUR_TEST_H +#define J2735_INTERNAL_DE_USERSIZEANDBEHAVIOUR_TEST_H /* Basic form tests */ void test_user_size_and_behaviour_non_extended(void); @@ -55,4 +55,4 @@ void test_user_size_and_behaviour_misaligned_access(void); void run_testsuite_user_size_and_behaviour(void); -#endif /* J2735_DE_INTERNAL_USERSIZEANDBEHAVIOUR_TEST_H */ +#endif /* J2735_INTERNAL_DE_USERSIZEANDBEHAVIOUR_TEST_H */ diff --git a/tests/J2735_internal_DE_VehicleEventFlags_test.c b/tests/J2735_internal_DE_VehicleEventFlags_test.c index 3250310..921dcbf 100644 --- a/tests/J2735_internal_DE_VehicleEventFlags_test.c +++ b/tests/J2735_internal_DE_VehicleEventFlags_test.c @@ -191,11 +191,6 @@ void test_vehicle_event_flags_extended(void) { * | 0 | 0x48 | 01001000 | ext(0) + flags[12:6] | * | 1 | 0xD0 | 11010000 | flags[5:0] + pad(2) | * - * @par Byte Derivation: - * - flags = 0x1234 = 1 0010 0011 0100 (13 bits, ASN.1 bit 0 at left) - * - Wire = [ext=0][flags] = 0|1001000110100 = 01001000 11010000 (with 2-bit pad) - * - Byte 0: 0100 1000 = 0x48 - * - Byte 1: 1101 0000 = 0xD0 */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_individual_non_extended_flags_0_to_4(void) { @@ -332,13 +327,6 @@ void test_vehicle_event_flags_individual_non_extended_flags_10_to_13(void) { * | 1 | 0xFF | 11111111 | flags[13:6] | * | 2 | 0xFC | 11111100 | flags[5:0] + pad(2) | * - * @par Byte Derivation: - * - nsnnwn = 14: small form (bit 0 = 0), value = 14 → 0|001110 = 0001110 - * - flags = 0x3FFF = 11 1111 1111 1111 (14 bits) - * - Wire = [ext=1][nsnnwn][flags] = 1|0001110|11111111111111 - * - Byte 0: 1000 1110 = 0x8E - * - Byte 1: 1111 1111 = 0xFF - * - Byte 2: 1111 1100 = 0xFC (with 2-bit padding) */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_individual_extended_flags_0_to_4(void) { @@ -751,11 +739,6 @@ void test_vehicle_event_flags_non_extended_alternating_0x1555_metadata(void) { * | 0 | 0x55 | 01010101 | ext(0) + flags[12:6]=1010101 | * | 1 | 0x54 | 01010100 | flags[5:0]=010101 + pad(2) | * - * @par Byte Derivation: - * - flags = 0x1555 = 1 0101 0101 0101 (13 bits) - * - Wire = [ext=0][flags] = 0|1010101010101 = 01010101 01010100 - * - Byte 0: 0101 0101 = 0x55 - * - Byte 1: 0101 0100 = 0x54 (with 2-bit padding) */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_non_extended_alternating_0x1555_0_to_4(void) { @@ -916,11 +899,6 @@ void test_vehicle_event_flags_non_extended_alternating_0x0AAA_metadata(void) { * | 0 | 0x2A | 00101010 | ext(0) + flags[12:6]=0101010 | * | 1 | 0xA8 | 10101000 | flags[5:0]=101010 + pad(2) | * - * @par Byte Derivation: - * - flags = 0x0AAA = 0 1010 1010 1010 (13 bits) - * - Wire = [ext=0][flags] = 0|0101010101010 = 00101010 10101000 - * - Byte 0: 0010 1010 = 0x2A - * - Byte 1: 1010 1000 = 0xA8 (with 2-bit padding) */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_non_extended_alternating_0x0AAA_0_to_4(void) { @@ -1042,11 +1020,6 @@ void test_vehicle_event_flags_non_extended_alternating_0x0AAA_10_to_12(void) { * | 0 | 0x40 | 01000000 | ext(0) + flags[0:6]=1000000 | * | 1 | 0x00 | 00000000 | flags[7:12]=000000 + pad(2) | * - * @par Byte Derivation: - * - flags = 0x1000 = 1 0000 0000 0000 (13 bits, bit 0 = hazardLights = 1) - * - Wire = [ext=0][flags] = 0|1000000000000 = 01000000 00000000 - * - Byte 0: ext(0) + flags bits 0-6 = 0|1000000 = 0x40 - * - Byte 1: flags bits 7-12 + pad = 000000|00 = 0x00 */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_single_bit_0_hazard_lights(void) { @@ -1088,11 +1061,6 @@ void test_vehicle_event_flags_single_bit_0_hazard_lights(void) { * | 0 | 0x00 | 00000000 | ext(0) + flags[0:6]=0000000 | * | 1 | 0x04 | 00000100 | flags[7:12]=000001 + pad(2) | * - * @par Byte Derivation: - * - flags = 0x0001 = 0 0000 0000 0001 (13 bits, bit 12 = airBagDeployment = 1) - * - Wire = [ext=0][flags] = 0|0000000000001 = 00000000 00000100 - * - Byte 0: ext(0) + flags bits 0-6 = 0|0000000 = 0x00 - * - Byte 1: flags bits 7-12 + pad = 000001|00 = 0x04 */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_single_bit_12_airbag(void) { @@ -1133,11 +1101,6 @@ void test_vehicle_event_flags_single_bit_12_airbag(void) { * | 0 | 0x00 | 00000000 | ext(0) + flags[0:6]=0000000 | * | 1 | 0x80 | 10000000 | flags[7:12]=100000 + pad(2) | * - * @par Byte Derivation: - * - flags = 0x0020 = 0 0000 0010 0000 (13 bits, bit 7 = hardBraking = 1) - * - Wire = [ext=0][flags] = 0|0000000100000 = 00000000 10000000 - * - Byte 0: ext(0) + flags bits 0-6 = 0|0000000 = 0x00 - * - Byte 1: flags bits 7-12 + pad = 100000|00 = 0x80 */ /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_vehicle_event_flags_single_bit_7_hard_braking(void) { @@ -1205,6 +1168,56 @@ void test_vehicle_event_flags_extended_single_hazard_lights(void) { "bit 13: eventJackKnife should be OFF"); } +/** + * @brief Test VehicleEventFlags with deliberately misaligned buffer pointer. + * + * @par Test Vector: + * - Extended: NO (root form) + * - Flags: 0x1FFF (all 13 root bits ON) + * + * @par Wire Format (14 bits total): + * | Offset (bits) | Width | Field | Value | + * |---------------|-------|---------|-----------------| + * | 0 | 1 | ext_bit | 0 | + * | 1 | 13 | flags | 1111111111111 | + * + * @par Byte Encoding: + * | Byte | Hex | Binary | Fields | + * |------|------|----------|--------------------------------| + * | 0 | 0x7F | 01111111 | ext(0) + flags[12:6]=1111111 | + * | 1 | 0xFC | 11111100 | flags[5:0]=111111 + pad(2) | + */ +/* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ +void test_vehicle_event_flags_misaligned_access(void) { + static const uint8_t payload[] = { + 0x00, /* junk byte for misalignment */ + 0x7F, /* ext(0) + flags[12:6] = 01111111 */ + 0xFC, /* flags[5:0] + padding = 11111100 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ + }; + const uint8_t *unaligned_ptr = &payload[1]; + + /* Verify non-extended */ + bool is_extended = J2735_VEHICLE_EVENT_FLAGS_IS_EXTENDED(unaligned_ptr); + TEST_ASSERT_FALSE_MESSAGE(is_extended, "Misaligned: extension bit should be 0"); + + /* Verify all 13 root flags ON */ + TEST_ASSERT_EQUAL_HEX16_MESSAGE(0x1FFFU, J2735_VEHICLE_EVENT_FLAGS_GET(unaligned_ptr), + "Misaligned: all 13 root flags should be ON (0x1FFF)"); + + /* Verify size */ + TEST_ASSERT_EQUAL_UINT32_MESSAGE(14U, J2735_VEHICLE_EVENT_FLAGS_SIZE(unaligned_ptr), + "Misaligned: non-extended form should be 14 bits"); + + /* Verify individual flag accessors on unaligned data */ + TEST_ASSERT_EQUAL_UINT8_MESSAGE(1U, + J2735_VEHICLE_EVENT_FLAGS_GET_EVENT_HAZARD_LIGHTS(unaligned_ptr), + "Misaligned: eventHazardLights should be ON"); + TEST_ASSERT_EQUAL_UINT8_MESSAGE( + 1U, J2735_VEHICLE_EVENT_FLAGS_GET_EVENT_AIR_BAG_DEPLOYMENT(unaligned_ptr), + "Misaligned: eventAirBagDeployment should be ON"); +} + /* cppcheck-suppress-end misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ /** @@ -1276,4 +1289,10 @@ void run_testsuite_vehicle_event_flags(void) { RUN_TEST(test_vehicle_event_flags_single_bit_12_airbag); RUN_TEST(test_vehicle_event_flags_single_bit_7_hard_braking); RUN_TEST(test_vehicle_event_flags_extended_single_hazard_lights); + /** + * Misaligned buffer pointer test + * + * - Misaligned access: Forces &payload[1] to verify alignment safety + */ + RUN_TEST(test_vehicle_event_flags_misaligned_access); } diff --git a/tests/J2735_internal_DE_VehicleEventFlags_test.h b/tests/J2735_internal_DE_VehicleEventFlags_test.h index 1ec6252..acd8ec2 100644 --- a/tests/J2735_internal_DE_VehicleEventFlags_test.h +++ b/tests/J2735_internal_DE_VehicleEventFlags_test.h @@ -25,8 +25,8 @@ * 13-bit root and 14-bit extension. */ -#ifndef J2735_DE_INTERNAL_VEHICLEEVENTFLAGS_TEST_H -#define J2735_DE_INTERNAL_VEHICLEEVENTFLAGS_TEST_H +#ifndef J2735_INTERNAL_DE_VEHICLEEVENTFLAGS_TEST_H +#define J2735_INTERNAL_DE_VEHICLEEVENTFLAGS_TEST_H /* Basic form tests */ void test_vehicle_event_flags_non_extended(void); @@ -40,9 +40,6 @@ void test_vehicle_event_flags_individual_non_extended_flags_0_to_4(void); void test_vehicle_event_flags_individual_non_extended_flags_5_to_9(void); void test_vehicle_event_flags_individual_non_extended_flags_10_to_13(void); -/* Individual flag accessor tests (extended) */ -void test_vehicle_event_flags_individual_extended_all_on(void); - /* SIZE macro tests */ void test_vehicle_event_flags_size_non_extended(void); void test_vehicle_event_flags_size_extended(void); @@ -68,6 +65,9 @@ void test_vehicle_event_flags_single_bit_12_airbag(void); void test_vehicle_event_flags_single_bit_7_hard_braking(void); void test_vehicle_event_flags_extended_single_hazard_lights(void); +/* Misaligned buffer pointer test */ +void test_vehicle_event_flags_misaligned_access(void); + void run_testsuite_vehicle_event_flags(void); -#endif /* J2735_DE_INTERNAL_VEHICLEEVENTFLAGS_TEST_H */ +#endif /* J2735_INTERNAL_DE_VEHICLEEVENTFLAGS_TEST_H */ diff --git a/tests/J2735_internal_DE_VerticalAccelerationThreshold_test.h b/tests/J2735_internal_DE_VerticalAccelerationThreshold_test.h index a88f52f..0f38e9f 100644 --- a/tests/J2735_internal_DE_VerticalAccelerationThreshold_test.h +++ b/tests/J2735_internal_DE_VerticalAccelerationThreshold_test.h @@ -24,8 +24,8 @@ * VerticalAccelerationThreshold is SIZE(5): a fixed BIT STRING with 5 bits. */ -#ifndef J2735_DE_INTERNAL_VERTICALACCELERATIONTHRESHOLD_TEST_H -#define J2735_DE_INTERNAL_VERTICALACCELERATIONTHRESHOLD_TEST_H +#ifndef J2735_INTERNAL_DE_VERTICALACCELERATIONTHRESHOLD_TEST_H +#define J2735_INTERNAL_DE_VERTICALACCELERATIONTHRESHOLD_TEST_H /* Core tests */ void test_vertical_acceleration_threshold_all_zeros(void); @@ -44,4 +44,4 @@ void test_vertical_acceleration_threshold_misaligned_access(void); void run_testsuite_vertical_acceleration_threshold(void); -#endif /* J2735_DE_INTERNAL_VERTICALACCELERATIONTHRESHOLD_TEST_H */ +#endif /* J2735_INTERNAL_DE_VERTICALACCELERATIONTHRESHOLD_TEST_H */ diff --git a/tests/J2735_internal_DF_ApproachOrLane_test.c b/tests/J2735_internal_DF_ApproachOrLane_test.c index 9daee20..cfd5fd1 100644 --- a/tests/J2735_internal_DF_ApproachOrLane_test.c +++ b/tests/J2735_internal_DF_ApproachOrLane_test.c @@ -19,7 +19,7 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for ApproachOrLane CHOICE type. + * @brief Tests for ApproachOrLane CHOICE type. * * ApproachOrLane is a non-extensible CHOICE with 2 alternatives. * This validates CHOICE index reading and alternative value extraction. @@ -31,8 +31,6 @@ * @endcode */ -#include -#include #include #include "unity.h" @@ -41,9 +39,7 @@ #include "J2735_internal_DF_ApproachOrLane.h" #include "J2735_internal_DF_ApproachOrLane_test.h" -/* ============================================================================================== */ -/* Happy Path Tests */ -/* ============================================================================================== */ +/* cppcheck-suppress-begin misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ /** * @brief Test ApproachOrLane with 'approach' alternative selected (typical value). @@ -79,7 +75,6 @@ void test_approach_or_lane_approach_typical(void) { }; /* Single I/O read (9 bits) - all subsequent operations are pure computation */ - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); /* Verify WHICH returns the correct alternative index (1 bit) */ @@ -136,7 +131,6 @@ void test_approach_or_lane_lane_typical(void) { }; /* Single I/O read (9 bits) - all subsequent operations are pure computation */ - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); /* Verify WHICH returns the correct alternative index (1 bit) */ @@ -153,10 +147,6 @@ void test_approach_or_lane_lane_typical(void) { TEST_ASSERT_EQUAL_UINT8_MESSAGE(9U, size, "size should be 9 bits (1 index + 8 value)"); } -/* ============================================================================================== */ -/* Boundary Value Tests - Approach */ -/* ============================================================================================== */ - /** * @brief Test ApproachOrLane boundary: approach with minimum value 0. * @@ -190,7 +180,6 @@ void test_approach_or_lane_approach_boundary_min(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); uint8_t const which = J2735_APPROACH_OR_LANE_WHICH(raw9); @@ -237,7 +226,6 @@ void test_approach_or_lane_approach_boundary_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); uint8_t const which = J2735_APPROACH_OR_LANE_WHICH(raw9); @@ -251,10 +239,6 @@ void test_approach_or_lane_approach_boundary_max(void) { TEST_ASSERT_EQUAL_UINT8_MESSAGE(5U, size, "size should be 5 bits for approach"); } -/* ============================================================================================== */ -/* Boundary Value Tests - Lane */ -/* ============================================================================================== */ - /** * @brief Test ApproachOrLane boundary: lane with minimum value 0. * @@ -290,7 +274,6 @@ void test_approach_or_lane_lane_boundary_min(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); uint8_t const which = J2735_APPROACH_OR_LANE_WHICH(raw9); @@ -339,7 +322,6 @@ void test_approach_or_lane_lane_boundary_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(payload); uint8_t const which = J2735_APPROACH_OR_LANE_WHICH(raw9); @@ -353,10 +335,6 @@ void test_approach_or_lane_lane_boundary_max(void) { TEST_ASSERT_EQUAL_UINT8_MESSAGE(9U, size, "size should be 9 bits for lane"); } -/* ============================================================================================== */ -/* Misalignment Tests */ -/* ============================================================================================== */ - /** * @brief Test ApproachOrLane with misaligned buffer access (approach alternative). * @@ -399,7 +377,6 @@ void test_approach_or_lane_misaligned_access(void) { /* Offset pointer by 1 byte to force misalignment */ const uint8_t *unaligned_ptr = &payload[1]; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t const raw9 = J2735_APPROACH_OR_LANE_RAW_READ(unaligned_ptr); uint8_t const which = J2735_APPROACH_OR_LANE_WHICH(raw9); @@ -414,6 +391,8 @@ void test_approach_or_lane_misaligned_access(void) { TEST_ASSERT_EQUAL_UINT8_MESSAGE(5U, size, "size should be 5 bits for approach (misaligned)"); } +/* cppcheck-suppress-end misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ + void run_testsuite_approach_or_lane(void) { /* Happy path tests */ RUN_TEST(test_approach_or_lane_approach_typical); diff --git a/tests/J2735_internal_DF_ApproachOrLane_test.h b/tests/J2735_internal_DF_ApproachOrLane_test.h index b8bfc91..b0e17fc 100644 --- a/tests/J2735_internal_DF_ApproachOrLane_test.h +++ b/tests/J2735_internal_DF_ApproachOrLane_test.h @@ -19,7 +19,7 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for ApproachOrLane CHOICE type. + * @brief Tests for ApproachOrLane CHOICE type. * * ApproachOrLane is a non-extensible CHOICE with 2 alternatives. * This validates CHOICE index reading and alternative value extraction. diff --git a/tests/J2735_internal_DF_BSMcoreData_test.c b/tests/J2735_internal_DF_BSMcoreData_test.c index 3f50a16..96e3968 100644 --- a/tests/J2735_internal_DF_BSMcoreData_test.c +++ b/tests/J2735_internal_DF_BSMcoreData_test.c @@ -19,12 +19,11 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for BSMcoreData. + * @brief Tests for BSMcoreData non-extensible SEQUENCE. * * The data frame BSMcoreData is a simple case with no extensions or optional fields. */ -#include #include #include "unity.h" @@ -33,9 +32,7 @@ #include "J2735_internal_DF_BSMcoreData.h" #include "J2735_internal_DF_BSMcoreData_test.h" -/* ============================================================================================== */ -/* Happy Path Tests */ -/* ============================================================================================== */ +/* cppcheck-suppress-begin misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ /** * @brief Test BSMcoreData field extraction (fixed-layout SEQUENCE). @@ -106,19 +103,13 @@ void test_bsm_core_data_fixed_data(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t msg_cnt = J2735_BSM_CORE_DATA_GET_MSG_CNT(payload); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int32_t lat = J2735_BSM_CORE_DATA_GET_LAT(payload); TEST_ASSERT_EQUAL_INT_MESSAGE(10, msg_cnt, "msgCnt should be 10"); TEST_ASSERT_EQUAL_INT_MESSAGE(410123450, lat, "lat should be 410123450"); } -/* ============================================================================================== */ -/* Boundary Value Tests */ -/* ============================================================================================== */ - /** * @brief Test BSMcoreData with negative latitude at southern boundary. * @@ -193,7 +184,6 @@ void test_bsm_core_data_latitude_negative_min(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int32_t lat = J2735_BSM_CORE_DATA_GET_LAT(payload); TEST_ASSERT_EQUAL_INT32_MESSAGE(-900000000, lat, "lat should be -900000000 (min valid)"); } @@ -247,7 +237,6 @@ void test_bsm_core_data_latitude_positive_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int32_t lat = J2735_BSM_CORE_DATA_GET_LAT(payload); TEST_ASSERT_EQUAL_INT32_MESSAGE(900000000, lat, "lat should be 900000000 (max practical)"); } @@ -296,7 +285,6 @@ void test_bsm_core_data_steering_angle_negative(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int8_t angle = J2735_BSM_CORE_DATA_GET_ANGLE(payload); TEST_ASSERT_EQUAL_INT8_MESSAGE(-126, angle, "angle should be -126 (min valid)"); } @@ -331,15 +319,10 @@ void test_bsm_core_data_steering_angle_positive_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int8_t angle = J2735_BSM_CORE_DATA_GET_ANGLE(payload); TEST_ASSERT_EQUAL_INT8_MESSAGE(127, angle, "angle should be 127 (max)"); } -/* ============================================================================================== */ -/* Misalignment Tests */ -/* ============================================================================================== */ - /** * @brief Test BSMcoreData with misaligned buffer access. * @@ -374,15 +357,15 @@ void test_bsm_core_data_misaligned_access(void) { /* Offset pointer by 1 byte to force misalignment */ const uint8_t *unaligned_ptr = &payload[1]; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t msg_cnt = J2735_BSM_CORE_DATA_GET_MSG_CNT(unaligned_ptr); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int32_t lat = J2735_BSM_CORE_DATA_GET_LAT(unaligned_ptr); TEST_ASSERT_EQUAL_INT_MESSAGE(10, msg_cnt, "msgCnt should be 10 (misaligned)"); TEST_ASSERT_EQUAL_INT_MESSAGE(410123450, lat, "lat should be 410123450 (misaligned)"); } +/* cppcheck-suppress-end misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ + void run_testsuite_bsm_core_data(void) { /* Happy path tests */ RUN_TEST(test_bsm_core_data_fixed_data); diff --git a/tests/J2735_internal_DF_BSMcoreData_test.h b/tests/J2735_internal_DF_BSMcoreData_test.h index 9271782..2810651 100644 --- a/tests/J2735_internal_DF_BSMcoreData_test.h +++ b/tests/J2735_internal_DF_BSMcoreData_test.h @@ -19,7 +19,7 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for BSMcoreData. + * @brief Tests for BSMcoreData non-extensible SEQUENCE. * * The data frame BSMcoreData is a simple case with no extensions or optional fields. */ diff --git a/tests/J2735_internal_DF_IntersectionReferenceID_test.c b/tests/J2735_internal_DF_IntersectionReferenceID_test.c index 2d2519c..6a0ba07 100644 --- a/tests/J2735_internal_DF_IntersectionReferenceID_test.c +++ b/tests/J2735_internal_DF_IntersectionReferenceID_test.c @@ -19,13 +19,11 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for IntersectionReferenceID. + * @brief Tests for IntersectionReferenceID non-extensible SEQUENCE. * - * The data frame IntersectionReferenceID is a simple case with an optional fields. + * The data frame IntersectionReferenceID is a simple case with an optional field. */ -#include -#include #include #include "unity.h" @@ -34,9 +32,7 @@ #include "J2735_internal_DF_IntersectionReferenceID.h" #include "J2735_internal_DF_IntersectionReferenceID_test.h" -/* ============================================================================================== */ -/* Happy Path Tests */ -/* ============================================================================================== */ +/* cppcheck-suppress-begin misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ /** * @brief Test IntersectionReferenceID with OPTIONAL field ABSENT. @@ -74,11 +70,9 @@ void test_intersection_reference_id_optional_field_absent(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(payload); TEST_ASSERT_FALSE_MESSAGE(region_present, "Region field should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0x1234U, intersection_id, "id should be 0x1234"); } @@ -124,23 +118,16 @@ void test_intersection_reference_id_optional_field_present(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(payload); TEST_ASSERT_TRUE_MESSAGE(region_present, "Region field should be present"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t region = J2735_INTERSECTION_REFERENCE_ID_GET_REGION(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0x00FFU, region, "region should be 0x00FF"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0x1234U, intersection_id, "id should be 0x1234"); } -/* ============================================================================================== */ -/* Boundary Value Tests */ -/* ============================================================================================== */ - /** * @brief Test IntersectionReferenceID with boundary minimum values (all zeros). * @@ -182,15 +169,12 @@ void test_intersection_reference_id_boundary_min(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(payload); TEST_ASSERT_TRUE_MESSAGE(region_present, "Region field should be present"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t region = J2735_INTERSECTION_REFERENCE_ID_GET_REGION(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0U, region, "region should be 0 (minimum)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0U, intersection_id, "id should be 0 (minimum)"); } @@ -237,15 +221,12 @@ void test_intersection_reference_id_boundary_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(payload); TEST_ASSERT_TRUE_MESSAGE(region_present, "Region field should be present"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t region = J2735_INTERSECTION_REFERENCE_ID_GET_REGION(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0xFFFFU, region, "region should be 0xFFFF (maximum)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0xFFFFU, intersection_id, "id should be 0xFFFF (maximum)"); } @@ -287,19 +268,13 @@ void test_intersection_reference_id_absent_region_max_id(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(payload); TEST_ASSERT_FALSE_MESSAGE(region_present, "Region field should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(payload); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0xFFFFU, intersection_id, "id should be 0xFFFF (maximum)"); } -/* ============================================================================================== */ -/* Misalignment Tests */ -/* ============================================================================================== */ - /** * @brief Test IntersectionReferenceID with misaligned buffer access. * @@ -324,19 +299,18 @@ void test_intersection_reference_id_misaligned_access(void) { /* Offset pointer by 1 byte to force misalignment */ const uint8_t *unaligned_ptr = &payload[1]; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool region_present = J2735_INTERSECTION_REFERENCE_ID_HAS_REGION(unaligned_ptr); TEST_ASSERT_TRUE_MESSAGE(region_present, "Region should be present (misaligned)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t region = J2735_INTERSECTION_REFERENCE_ID_GET_REGION(unaligned_ptr); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0x00FFU, region, "region should be 0x00FF (misaligned)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint16_t intersection_id = J2735_INTERSECTION_REFERENCE_ID_GET_ID(unaligned_ptr); TEST_ASSERT_EQUAL_UINT16_MESSAGE(0x1234U, intersection_id, "id should be 0x1234 (misaligned)"); } +/* cppcheck-suppress-end misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ + void run_testsuite_intersection_reference_id(void) { /* Happy path tests */ RUN_TEST(test_intersection_reference_id_optional_field_absent); diff --git a/tests/J2735_internal_DF_IntersectionReferenceID_test.h b/tests/J2735_internal_DF_IntersectionReferenceID_test.h index 112da37..0c11263 100644 --- a/tests/J2735_internal_DF_IntersectionReferenceID_test.h +++ b/tests/J2735_internal_DF_IntersectionReferenceID_test.h @@ -19,9 +19,9 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for IntersectionReferenceID. + * @brief Tests for IntersectionReferenceID non-extensible SEQUENCE. * - * The data frame IntersectionReferenceID is a simple case with an optional fields. + * The data frame IntersectionReferenceID is a simple case with an optional field. */ #ifndef J2735_INTERNAL_DF_INTERSECTIONREFERENCEID_TEST_H diff --git a/tests/J2735_internal_DF_PathPrediction_test.c b/tests/J2735_internal_DF_PathPrediction_test.c index b62c9e7..2a5b884 100644 --- a/tests/J2735_internal_DF_PathPrediction_test.c +++ b/tests/J2735_internal_DF_PathPrediction_test.c @@ -19,13 +19,11 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for PathPrediction. + * @brief Tests for PathPrediction extensible SEQUENCE. * * The data frame PathPrediction is a simple case with an extension field. */ -#include -#include #include #include "unity.h" @@ -34,9 +32,7 @@ #include "J2735_internal_DF_PathPrediction.h" #include "J2735_internal_DF_PathPrediction_test.h" -/* ============================================================================================== */ -/* Happy Path Tests */ -/* ============================================================================================== */ +/* cppcheck-suppress-begin misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ /** * @brief Test PathPrediction with NO extension (extension bit = 0). @@ -79,15 +75,12 @@ void test_path_prediction_no_extension(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(1000, radius, "radiusOfCurve should be 1000"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(100U, conf, "confidence should be 100"); @@ -140,25 +133,22 @@ void test_path_prediction_no_extension(void) { /* cppcheck-suppress misra-c2012-8.7 ; Unity RUN_TEST requires external linkage */ void test_path_prediction_with_extension(void) { static const uint8_t payload[] = { - 0x80, /* ext(1) + radiusOfCurve[15:9] */ - 0xFA, /* radiusOfCurve[8:1] */ - 0x19, /* radiusOfCurve[0] + conf[7:1] */ - 0x00, /* conf[0] + nsnnwn[6:0] */ - 0x80, /* bitmap(1) + lenDet[7:1] */ - 0xD5, /* lenDet[0] + content[7:1] */ - 0x80, /* content[0] + padding */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8-byte alignment padding for J2735_READ_BITS */ + 0x80, /* ext(1) + radiusOfCurve[15:9] */ + 0xFA, /* radiusOfCurve[8:1] */ + 0x19, /* radiusOfCurve[0] + conf[7:1] */ + 0x00, /* conf[0] + nsnnwn[6:0] */ + 0x80, /* bitmap(1) + lenDet[7:1] */ + 0xD5, /* lenDet[0] + content[7:1] */ + 0x80, /* content[0] + padding */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_TRUE_MESSAGE(has_ext, "Extension should be present"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(500, radius, "radiusOfCurve should be 500"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(50U, conf, "confidence should be 50"); @@ -210,23 +200,16 @@ void test_path_prediction_signed_negative(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(-1000, radius, "radiusOfCurve should be -1000"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(150U, conf, "confidence should be 150"); } -/* ============================================================================================== */ -/* Boundary Value Tests */ -/* ============================================================================================== */ - /** * @brief Test PathPrediction with minimum radiusOfCurve (-32767). * @@ -268,15 +251,12 @@ void test_path_prediction_radius_boundary_min(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(-32767, radius, "radiusOfCurve should be -32767 (min)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(0U, conf, "confidence should be 0 (min)"); } @@ -322,15 +302,12 @@ void test_path_prediction_radius_boundary_max(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(32767, radius, "radiusOfCurve should be 32767 (max)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(200U, conf, "confidence should be 200 (max valid)"); } @@ -376,23 +353,16 @@ void test_path_prediction_radius_zero(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* safety padding */ }; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(payload); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(payload); TEST_ASSERT_EQUAL_INT16_MESSAGE(0, radius, "radiusOfCurve should be 0 (straight)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(payload); TEST_ASSERT_EQUAL_UINT8_MESSAGE(100U, conf, "confidence should be 100"); } -/* ============================================================================================== */ -/* Misalignment Tests */ -/* ============================================================================================== */ - /** * @brief Test PathPrediction with misaligned buffer access. * @@ -416,19 +386,18 @@ void test_path_prediction_misaligned_access(void) { /* Offset pointer by 1 byte to force misalignment */ const uint8_t *unaligned_ptr = &payload[1]; - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ bool has_ext = J2735_PATH_PREDICTION_HAS_EXTENSION(unaligned_ptr); TEST_ASSERT_FALSE_MESSAGE(has_ext, "Extension should be absent (misaligned)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ int16_t radius = J2735_PATH_PREDICTION_GET_RADIUS_OF_CURVE(unaligned_ptr); TEST_ASSERT_EQUAL_INT16_MESSAGE(1000, radius, "radiusOfCurve should be 1000 (misaligned)"); - /* cppcheck-suppress misra-c2012-11.3 ; Zero-copy macro uses packed-cast */ uint8_t conf = J2735_PATH_PREDICTION_GET_CONFIDENCE(unaligned_ptr); TEST_ASSERT_EQUAL_UINT8_MESSAGE(100U, conf, "confidence should be 100 (misaligned)"); } +/* cppcheck-suppress-end misra-c2012-11.3 ; J2735_READ_BITS requires pointer cast */ + void run_testsuite_path_prediction(void) { /* Happy path tests */ RUN_TEST(test_path_prediction_no_extension); diff --git a/tests/J2735_internal_DF_PathPrediction_test.h b/tests/J2735_internal_DF_PathPrediction_test.h index f189169..9772899 100644 --- a/tests/J2735_internal_DF_PathPrediction_test.h +++ b/tests/J2735_internal_DF_PathPrediction_test.h @@ -19,7 +19,7 @@ /** * @file * @author Yogev Neumann - * @brief Sanity tests for PathPrediction. + * @brief Tests for PathPrediction extensible SEQUENCE. * * The data frame PathPrediction is a simple case with an extension field. */ diff --git a/tools/j2735_c_generator_jinja.py b/tools/j2735_c_generator_jinja.py index fc446dd..7aa8fd3 100644 --- a/tools/j2735_c_generator_jinja.py +++ b/tools/j2735_c_generator_jinja.py @@ -20,12 +20,19 @@ Provides Jinja2 environment setup and template loading for C code generation. """ +from functools import lru_cache from pathlib import Path from re import sub -from jinja2 import Environment, FileSystemLoader, Template, select_autoescape +from jinja2 import ( + Environment, + FileSystemLoader, + StrictUndefined, + Template, + select_autoescape, +) -from .j2735_spec_constraints import SequenceField +from .j2735_spec_constraints import IntegerConstraint, SequenceField # ============================================================================= # Constants @@ -128,11 +135,8 @@ def filter_format_range(field: SequenceField) -> str: >>> filter_format_range(f) '-900000000..900000001' """ - if hasattr(field.type, "min_value") and hasattr(field.type, "max_value"): - min_val = getattr(field.type, "min_value", None) - max_val = getattr(field.type, "max_value", None) - if min_val is not None and max_val is not None: - return f"{min_val}..{max_val}" + if isinstance(field.type, IntegerConstraint): + return f"{field.type.min_value}..{field.type.max_value}" return "" @@ -157,9 +161,8 @@ def filter_is_signed(field: SequenceField) -> bool: >>> filter_is_signed(f) True """ - if hasattr(field.type, "min_value"): - min_val = getattr(field.type, "min_value", None) - return min_val is not None and min_val < 0 + if isinstance(field.type, IntegerConstraint): + return field.type.min_value < 0 return False @@ -186,14 +189,22 @@ def filter_screaming_snake(name: str) -> str: 'BSM_CORE_DATA' >>> filter_screaming_snake("AccelerationSet4Way") 'ACCELERATION_SET_4_WAY' + >>> filter_screaming_snake("Offset-B10") + 'OFFSET_B_10' + >>> filter_screaming_snake("Node-LL-24B") + 'NODE_LL_24_B' + >>> filter_screaming_snake("NMEA-MsgType") + 'NMEA_MSG_TYPE' """ - # Step 1: Insert underscore after abbreviation (2+ uppercase) before lowercase + # Step 1: Replace hyphens with underscores (ASN.1 names like Offset-B10, Node-LL-24B) + name = name.replace("-", "_") + # Step 2: Insert underscore after abbreviation (2+ uppercase) before lowercase result = sub(r"([A-Z]{2,})([a-z])", r"\1_\2", name) - # Step 2: Insert underscore between lowercase and uppercase + # Step 3: Insert underscore between lowercase and uppercase result = sub(r"([a-z])([A-Z])", r"\1_\2", result) - # Step 3: Insert underscore between letter and digit + # Step 4: Insert underscore between letter and digit result = sub(r"([a-zA-Z])([0-9])", r"\1_\2", result) - # Step 4: Insert underscore between digit and letter + # Step 5: Insert underscore between digit and letter result = sub(r"([0-9])([a-zA-Z])", r"\1_\2", result) return result.upper() @@ -220,6 +231,10 @@ def filter_snake_case(name: str) -> str: 'bsm_core_data' >>> filter_snake_case("AccelerationSet4Way") 'acceleration_set_4_way' + >>> filter_snake_case("Offset-B10") + 'offset_b_10' + >>> filter_snake_case("Node-LL-24B") + 'node_ll_24_b' """ return filter_screaming_snake(name).lower() @@ -229,14 +244,20 @@ def filter_snake_case(name: str) -> str: # ============================================================================= +@lru_cache(maxsize=1) def create_jinja_env() -> Environment: - """Create and configure the Jinja2 environment.""" + """Create and configure the Jinja2 environment. + + The result is cached - the Environment and its filters are never mutated + after construction, so a single shared instance is safe. + """ env = Environment( - loader=FileSystemLoader(_TEMPLATES_DIR), autoescape=select_autoescape(default=False), - trim_blocks=True, - lstrip_blocks=True, keep_trailing_newline=True, + loader=FileSystemLoader(_TEMPLATES_DIR), + lstrip_blocks=True, + trim_blocks=True, + undefined=StrictUndefined, ) env.filters[_FILTER_BYTES_FROM_BITS] = filter_bytes_from_bits env.filters[_FILTER_C_TYPE] = filter_c_type diff --git a/tools/j2735_c_generator_size_constants.py b/tools/j2735_c_generator_size_constants.py index 6f4051d..a36deae 100644 --- a/tools/j2735_c_generator_size_constants.py +++ b/tools/j2735_c_generator_size_constants.py @@ -33,7 +33,7 @@ from .j2735_c_generator_jinja import create_jinja_env, get_template from .j2735_spec_parser import J2735Specification -_TEMPLATE_NAME = "size_constants.j2" +_SIZE_CONSTANTS_TEMPLATE_NAME = "size_constants.j2" def generate_size_constants(spec: J2735Specification) -> str: @@ -60,7 +60,7 @@ def generate_size_constants(spec: J2735Specification) -> str: fixed_types, _ = spec.collect_fixed_width_types() env = create_jinja_env() - template = get_template(env, _TEMPLATE_NAME) + template = get_template(env, _SIZE_CONSTANTS_TEMPLATE_NAME) return template.render( types=fixed_types, diff --git a/tools/j2735_c_generator_wire_format.py b/tools/j2735_c_generator_wire_format.py index e82fb2c..99085f6 100644 --- a/tools/j2735_c_generator_wire_format.py +++ b/tools/j2735_c_generator_wire_format.py @@ -23,14 +23,22 @@ from collections.abc import Sequence from dataclasses import dataclass -from typing import TypedDict +from typing import Final, TypedDict -from .j2735_spec_constraints import SequenceField, SequenceType +from .j2735_spec_constraints import ( + SequenceField, + SequenceType, + TypeReference, +) # ============================================================================= # SEQUENCE Wire Format # ============================================================================= +_VARIABLE_BITS: Final[str] = ( + "variable" # Sentinel for ``total_bits`` when wire size cannot be computed +) + @dataclass(frozen=True, slots=True) class SequenceWireVariant: @@ -48,6 +56,81 @@ class SequenceWireVariant: total_bits: int | str # int for fixed, "variable" for ext=1 +def _build_optional_variants( + all_fields: tuple[SequenceField, ...], + is_ext: bool, +) -> list[SequenceWireVariant]: + """Build ABSENT / PRESENT variants for a SEQUENCE with OPTIONAL fields. + + Args: + all_fields: All fields in the SEQUENCE. + is_ext: Whether the SEQUENCE is extensible. + + Returns: + Two-element list: [absent_variant, present_variant]. + """ + opt_count = sum(1 for f in all_fields if f.is_optional) + required_fields = tuple(f for f in all_fields if not f.is_optional) + optional_names = [f.name for f in all_fields if f.is_optional] + preamble = (1 if is_ext else 0) + opt_count + ext_bit = 0 if is_ext else None + + absent_opt = "0" if opt_count == 1 else f"0..0 ({opt_count})" + present_opt = "1" if opt_count == 1 else f"1..1 ({opt_count})" + + if len(optional_names) == 1: + absent_name = f"{optional_names[0]} ABSENT" + present_name = f"{optional_names[0]} PRESENT" + else: + absent_name = "all optional ABSENT" + present_name = "all optional PRESENT" + + absent_total, absent_label = _total_and_label( + required_fields, + preamble, + absent_name, + ) + present_total, present_label = _total_and_label( + all_fields, + preamble, + present_name, + ) + + return [ + SequenceWireVariant( + name=absent_label, + fields=required_fields, + ext_bit=ext_bit, + opt_bitmap=absent_opt, + total_bits=absent_total, + ), + SequenceWireVariant( + name=present_label, + fields=all_fields, + ext_bit=ext_bit, + opt_bitmap=present_opt, + total_bits=present_total, + ), + ] + + +def _has_variable_width( + fields: tuple[SequenceField, ...], +) -> bool: + """Return True when any field has a variable-width type. + + Variable-width types (e.g. ``SequenceOfType``) legitimately + have ``uper_bit_width is None`` even after resolution. + + Args: + fields: Tuple of SequenceField objects. + + Returns: + ``True`` if at least one field has ``None`` bit width. + """ + return any(f.type.uper_bit_width is None for f in fields) + + def _pluralize_bits(n: int) -> str: """Format a bit count with correct singular/plural grammar. @@ -68,20 +151,97 @@ def _pluralize_bits(n: int) -> str: return f"{n} bit" if n == 1 else f"{n} bits" -def _sum_field_bits(fields: tuple[SequenceField, ...]) -> int: +def _sum_field_bits( + fields: tuple[SequenceField, ...], +) -> int: """Sum the UPER bit widths of the given fields. + Callers **must** check ``_has_variable_width()`` first; calling + this function on variable-width fields is a programming error. + Args: fields: Tuple of SequenceField objects. Returns: - Total bit width (treating ``None`` widths as 0). + Total bit width. + + Raises: + TypeError: If any ``uper_bit_width`` is ``None`` + (variable-width field present). Examples: >>> _sum_field_bits(()) 0 """ - return sum(f.type.uper_bit_width or 0 for f in fields) + total = 0 + for f in fields: + bw = f.type.uper_bit_width + if bw is None: + raise TypeError( + f"Field '{f.name}' has variable-width " + "type - call _has_variable_width() " + "before _sum_field_bits()." + ) + total += bw + return total + + +def _total_and_label( + fields: tuple[SequenceField, ...], + prefix_bits: int, + name: str, +) -> tuple[int | str, str]: + """Compute ``(total_bits, label)`` for a set of fields. + + If *fields* contain a variable-width type the total is + ``_VARIABLE_BITS``; otherwise it is the numeric sum of + *prefix_bits* + field bit widths. + + When *name* is non-empty the label is ``", "``; + when empty the label is just the total portion. + + Args: + fields: Tuple of SequenceField objects. + prefix_bits: Fixed overhead (ext bit, opt bitmap, etc.). + name: Human-readable variant name prefix. + + Returns: + ``(total_bits, label)`` pair. + """ + if _has_variable_width(fields): + suffix = _VARIABLE_BITS + total: int | str = _VARIABLE_BITS + else: + total = prefix_bits + _sum_field_bits(fields) + suffix = _pluralize_bits(total) + label = f"{name}, {suffix}" if name else suffix + return total, label + + +def _validate_fields_resolved( + fields: tuple[SequenceField, ...], +) -> None: + """Reject fields whose type is still an unresolved TypeReference. + + This is a **precondition guard** -- it catches programming errors + where type resolution was skipped or failed *before* any + computation is attempted. + + Args: + fields: Tuple of SequenceField objects. + + Raises: + ValueError: If any field's type is an unresolved + ``TypeReference``. + """ + for f in fields: + if isinstance(f.type, TypeReference): + raise ValueError( + f"Field '{f.name}' (type '{f.type_name}') " + "still has an unresolved TypeReference. " + "All type references must be resolved " + "before computing wire format." + ) def get_sequence_variants(constraint: SequenceType) -> list[SequenceWireVariant]: @@ -112,15 +272,15 @@ def get_sequence_variants(constraint: SequenceType) -> list[SequenceWireVariant] is_ext = constraint.is_extensible opt_count = constraint.optional_count all_fields = constraint.fields - required_fields = tuple(f for f in all_fields if not f.is_optional) - optional_names = [f.name for f in all_fields if f.is_optional] + + _validate_fields_resolved(all_fields) # Case 1: Fixed SEQUENCE (no OPTIONAL, not extensible) if not is_ext and opt_count == 0: - total = _sum_field_bits(all_fields) + total, label = _total_and_label(all_fields, 0, "") return [ SequenceWireVariant( - name=_pluralize_bits(total), + name=label, fields=all_fields, ext_bit=None, opt_bitmap="", @@ -130,57 +290,26 @@ def get_sequence_variants(constraint: SequenceType) -> list[SequenceWireVariant] # Case 2: Extensible SEQUENCE with no OPTIONAL if is_ext and opt_count == 0: - total_no_ext = 1 + _sum_field_bits(all_fields) # 1 for ext bit + total, label = _total_and_label(all_fields, 1, "no extensions") return [ SequenceWireVariant( - name=f"no extensions, {_pluralize_bits(total_no_ext)}", + name=label, fields=all_fields, ext_bit=0, opt_bitmap="", - total_bits=total_no_ext, + total_bits=total, ), SequenceWireVariant( - name="with extensions, variable", + name=f"with extensions, {_VARIABLE_BITS}", fields=all_fields, ext_bit=1, opt_bitmap="", - total_bits="variable", + total_bits=_VARIABLE_BITS, ), ] # Case 3: SEQUENCE with OPTIONAL fields (may also be extensible) - ext_prefix = 1 if is_ext else 0 - - # Variant: all optional ABSENT - absent_bits = ext_prefix + opt_count + _sum_field_bits(required_fields) - absent_opt = "0" if opt_count == 1 else f"0..0 ({opt_count})" - absent_name = ( - f"{optional_names[0]} ABSENT" if len(optional_names) == 1 else "all optional ABSENT" - ) - - # Variant: all optional PRESENT - present_bits = ext_prefix + opt_count + _sum_field_bits(all_fields) - present_opt = "1" if opt_count == 1 else f"1..1 ({opt_count})" - present_name = ( - f"{optional_names[0]} PRESENT" if len(optional_names) == 1 else "all optional PRESENT" - ) - - return [ - SequenceWireVariant( - name=f"{absent_name}, {_pluralize_bits(absent_bits)}", - fields=required_fields, - ext_bit=0 if is_ext else None, - opt_bitmap=absent_opt, - total_bits=absent_bits, - ), - SequenceWireVariant( - name=f"{present_name}, {_pluralize_bits(present_bits)}", - fields=all_fields, - ext_bit=0 if is_ext else None, - opt_bitmap=present_opt, - total_bits=present_bits, - ), - ] + return _build_optional_variants(all_fields, is_ext) # ============================================================================= diff --git a/tools/j2735_spec_constraints.py b/tools/j2735_spec_constraints.py index b6d2be8..ab5500f 100644 --- a/tools/j2735_spec_constraints.py +++ b/tools/j2735_spec_constraints.py @@ -53,6 +53,12 @@ _BITS_PER_BYTE: Final[int] = 8 # 8 bits per byte/octet in OCTET STRING +# UPER encoding overhead constants (ITU-T X.691) +# These mirror the C constants J2735_INTERNAL_EXTENSION_MARKER_BITS and +# J2735_INTERNAL_NSNNWN_SMALL_BITS defined in J2735_internal_constants.h. +_UPER_EXTENSION_MARKER_BITS: Final[int] = 1 # Extension presence bit (§11.9) +_UPER_NSNNWN_SMALL_BITS: Final[int] = 7 # Normally-small-non-negative-whole-number field (§11.6) + # J2735-specific constants # In J2735 CHOICE types, "regional" fields contain SEQUENCE OF RegionalExtension # which are variable-length and should be excluded from bit-width calculations @@ -303,7 +309,8 @@ def read_bits(self) -> int: """Return total bits to read for single-read optimization. For non-extensible BIT STRING, returns the root size. - For extensible BIT STRING, returns 1 (ext marker) + 7 (nsnnwn) + ext_bits. + For extensible BIT STRING, returns + _UPER_EXTENSION_MARKER_BITS + _UPER_NSNNWN_SMALL_BITS + ext_bits. This value is used by code generators to read all possible bits in a single J2735_READ_BITS call. @@ -326,7 +333,7 @@ def read_bits(self) -> int: 22 """ if self.is_extensible: - return 1 + 7 + self.ext_bits + return _UPER_EXTENSION_MARKER_BITS + _UPER_NSNNWN_SMALL_BITS + self.ext_bits return self.root_size @classmethod diff --git a/tools/j2735_spec_parser.py b/tools/j2735_spec_parser.py index c1d3b33..d88baec 100644 --- a/tools/j2735_spec_parser.py +++ b/tools/j2735_spec_parser.py @@ -184,10 +184,10 @@ class ASN1TypeDefinition: All type information is stored in the unified `constraint` field, which holds the appropriate constraint class for each type: - - INTEGER → IntegerConstraint - - BIT STRING → BitStringConstraint - - BOOLEAN → BooleanType - - SEQUENCE → SequenceType (with resolved SequenceField.type) + - INTEGER -> IntegerConstraint + - BIT STRING -> BitStringConstraint + - BOOLEAN -> BooleanType + - SEQUENCE -> SequenceType (with resolved SequenceField.type) - etc. Attributes: diff --git a/tools/templates/asn1_definition.j2 b/tools/templates/asn1_definition.j2 index 922df4e..ac4a658 100644 --- a/tools/templates/asn1_definition.j2 +++ b/tools/templates/asn1_definition.j2 @@ -20,50 +20,81 @@ J2735 ASN.1 Definition Block Template Renders the ASN.1 type definition in Doxygen comment format. - Works directly with ASN1TypeDefinition - no intermediate data structures. + Handles SEQUENCE, CHOICE, and BIT STRING types via typedef.type_class. Context variables: - typedef: ASN1TypeDefinition object + - alternatives: (CHOICE only) List of dicts with name, type_ref, bit_width - Required filters: - - is_signed(field): Returns True if field.type.min_value < 0 - - format_range(field): Returns "min..max" or "" + Filters used: + - is_signed(field): Returns True if field.type.min_value < 0 (SEQUENCE only) + - format_range(field): Returns "min..max" or "" (SEQUENCE only) + - screaming_snake: Converts camelCase to SCREAMING_SNAKE_CASE (CHOICE only) Output format: - * BSMcoreData ::= SEQUENCE { - * msgCnt MsgCount, -- 7 bits (unsigned, 0..127) - * ... + * TypeName ::= SEQUENCE { + * field Type, -- NN bits (unsigned, 0..127) * } + + * TypeName ::= CHOICE { + * alt TypeRef, -- NN bits (J2735_BW_TYPE_REF) + * } + + * TypeName ::= BIT STRING { + * name (0), + * name (1) + * } (SIZE (N, ..., M)) -#} -{#- Compute column widths for alignment -#} -{%- set name_width = namespace(val=14) -%} -{%- set type_width = namespace(val=22) -%} -{%- for field in typedef.constraint.fields -%} -{%- if field.name | length > name_width.val - 2 -%} -{%- set name_width.val = field.name | length + 2 -%} -{%- endif -%} -{%- set type_len = field.type_name | length + (9 if field.is_optional else 0) + 1 -%} -{%- if type_len > type_width.val - 2 -%} -{%- set type_width.val = type_len + 2 -%} -{%- endif -%} -{%- endfor %} +{%- set type_class = typedef.type_class.name -%} +{% if type_class == "SEQUENCE" %} +{#- SEQUENCE: fields with types, optional markers, sign/range comments -#} +{%- set name_width = namespace(val=14) -%} +{%- set type_width = namespace(val=22) -%} +{%- for field in typedef.constraint.fields -%} +{%- if field.name | length > name_width.val - 2 -%} +{%- set name_width.val = field.name | length + 2 -%} +{%- endif -%} +{%- set type_len = field.type_name | length + (9 if field.is_optional else 0) + 1 -%} +{%- if type_len > type_width.val - 2 -%} +{%- set type_width.val = type_len + 2 -%} +{%- endif -%} +{%- endfor %} * {{ typedef.name }} ::= SEQUENCE { -{% for field in typedef.constraint.fields %} -{%- set optional_marker = " OPTIONAL" if field.is_optional else "" -%} -{%- set is_last = loop.last and not typedef.constraint.is_extensible -%} -{%- set comma = "" if is_last else "," -%} -{%- set type_part = field.type_name ~ optional_marker ~ comma -%} -{%- set sign_str = "signed" if field | is_signed else "unsigned" -%} -{%- set range_str = field | format_range -%} -{%- set bits = field.type.uper_bit_width or 0 -%} -{%- if range_str -%} -{%- set comment = "-- " ~ "%2d" | format(bits) ~ " bits (" ~ sign_str ~ ", " ~ range_str ~ ")" -%} -{%- else -%} -{%- set comment = "-- " ~ "%2d" | format(bits) ~ " bits" -%} -{%- endif %} +{% for field in typedef.constraint.fields %} +{%- set optional_marker = " OPTIONAL" if field.is_optional else "" -%} +{%- set is_last = loop.last and not typedef.constraint.is_extensible -%} +{%- set comma = "" if is_last else "," -%} +{%- set type_part = field.type_name ~ optional_marker ~ comma -%} +{%- set sign_str = "signed" if field | is_signed else "unsigned" -%} +{%- set range_str = field | format_range -%} +{%- set bits = field.type.uper_bit_width -%} +{%- if bits is none -%} +{%- set comment = "-- variable" -%} +{%- elif range_str -%} +{%- set comment = "-- " ~ "%2d" | format(bits) ~ " bits (" ~ sign_str ~ ", " ~ range_str ~ ")" -%} +{%- else -%} +{%- set comment = "-- " ~ "%2d" | format(bits) ~ " bits" -%} +{%- endif %} * {{ "%-*s" | format(name_width.val, field.name) }}{{ "%-*s" | format(type_width.val, type_part) }}{{ comment }} -{% endfor %} -{%- if typedef.constraint.is_extensible %} +{% endfor %} +{%- if typedef.constraint.is_extensible %} * ... -{% endif %} +{% endif %} + * } +{% elif type_class == "CHOICE" %} +{#- CHOICE: alternatives with type refs and bit-width comments -#} + * {{ typedef.name }} ::= CHOICE { +{% for alt in alternatives %} + * {{ alt.name }} {{ alt.type_ref }}{{ "," if not loop.last else "" }} -- {{ alt.bit_width }} bits (J2735_BW_{{ alt.type_ref | screaming_snake }}) +{% endfor %} * } +{% elif type_class == "BIT_STRING" %} +{#- BIT STRING: named bits with indices and SIZE constraint -#} +{%- set root_size = typedef.constraint.root_size -%} +{%- set ext_bits = typedef.constraint.ext_bits %} + * {{ typedef.name }} ::= BIT STRING { +{% for name, index in typedef.constraint.named_bits.items() | sort(attribute='1') %} + * {{ name }} ({{ index }}){{ "," if not loop.last else "" }} +{% endfor %} + * } (SIZE ({{ root_size }}{{ ", ..." if typedef.constraint.is_extensible else "" }}{{ ", " ~ ext_bits if typedef.constraint.is_extensible and ext_bits != root_size else "" }})) +{% endif %} diff --git a/tools/templates/assemble_de_bitstring.j2 b/tools/templates/assemble_de_bitstring.j2 index f2793b9..193cdec 100644 --- a/tools/templates/assemble_de_bitstring.j2 +++ b/tools/templates/assemble_de_bitstring.j2 @@ -17,11 +17,12 @@ SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: assemble_de_bitstring.j2 -Purpose: Generate C header file for a BIT STRING data element (DE). + BIT STRING Data Element Assembly Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates a complete C header file for a BIT STRING data element (DE). + + Context variables: + - typedef: The full typedef object containing the metadata -#} {%- set data_type = typedef.type_class.name | lower -%} {%- set is_extensible = typedef.constraint.is_extensible -%} @@ -52,42 +53,46 @@ Template Context (from generator): * @author Yogev Neumann * @brief J2735 {{ typedef_name }} Definition and Access Macros. * - * {{ typedef_name }} ::= BIT STRING { -{% for name, index in typedef.constraint.named_bits.items() | sort(attribute='1') %} - * {{ name }} ({{ index }}){{ "," if not loop.last else "" }} -{% endfor %} - * } (SIZE ({{ root_size }}{{ ", ..." if is_extensible else "" }}{{ ", " ~ ext_bits if is_extensible and ext_bits != root_size else "" }})) + * @verbatim +{% include 'asn1_definition.j2' %} + * @endverbatim * {% if is_extensible %} * Extensible BIT STRING with root size {{ root_size }} and known extension size {{ ext_bits }}. * - * Wire Format (non-extended, {{ root_wire_size }} bits total): + * @par Wire Format (non-extended, {{ root_wire_size }} bits total): + * @verbatim * ┌{{ "─" * (COL1 + 2) }}┬{{ "─" * (COL2_NONEXT + 2) }}┐ * │ {{ "%-*s"|format(COL1, "Bit 0") }} │ {{ "%-*s"|format(COL2_NONEXT, "Bits 1-" ~ root_size) }} │ * ├{{ "─" * (COL1 + 2) }}┼{{ "─" * (COL2_NONEXT + 2) }}┤ * │ {{ "%-*s"|format(COL1, "Ext=0") }} │ {{ "%-*s"|format(COL2_NONEXT, "flags[0.." ~ (root_size - 1) ~ "] (" ~ root_size ~ " bits)") }} │ * └{{ "─" * (COL1 + 2) }}┴{{ "─" * (COL2_NONEXT + 2) }}┘ + * @endverbatim * - * Wire Format (extended, {{ read_bits }} bits total): + * @par Wire Format (extended, {{ read_bits }} bits total): + * @verbatim * ┌{{ "─" * (COL1 + 2) }}┬{{ "─" * (COL2_EXT + 2) }}┬{{ "─" * (COL3_EXT + 2) }}┐ * │ {{ "%-*s"|format(COL1, "Bit 0") }} │ {{ "%-*s"|format(COL2_EXT, "Bits 1-7") }} │ {{ "%-*s"|format(COL3_EXT, "Bits 8-" ~ (read_bits - 1)) }} │ * ├{{ "─" * (COL1 + 2) }}┼{{ "─" * (COL2_EXT + 2) }}┼{{ "─" * (COL3_EXT + 2) }}┤ * │ {{ "%-*s"|format(COL1, "Ext=1") }} │ {{ "%-*s"|format(COL2_EXT, "nsnnwn=" ~ ext_bits ~ " (7 bits)") }} │ {{ "%-*s"|format(COL3_EXT, "flags[0.." ~ (ext_bits - 1) ~ "] (" ~ ext_bits ~ " bits)") }} │ * └{{ "─" * (COL1 + 2) }}┴{{ "─" * (COL2_EXT + 2) }}┴{{ "─" * (COL3_EXT + 2) }}┘ + * @endverbatim {% else %} {%- set COL_FIXED = 60 %} * Fixed BIT STRING with size {{ root_size }}. * - * Wire Format ({{ root_wire_size }} bits total): + * @par Wire Format ({{ root_wire_size }} bits total): + * @verbatim * ┌{{ "─" * (COL_FIXED + 2) }}┐ * │ {{ "%-*s"|format(COL_FIXED, "Bits 0-" ~ (root_size - 1)) }} │ * ├{{ "─" * (COL_FIXED + 2) }}┤ * │ {{ "%-*s"|format(COL_FIXED, "flags[0.." ~ (root_size - 1) ~ "] (" ~ root_size ~ " bits)") }} │ * └{{ "─" * (COL_FIXED + 2) }}┘ + * @endverbatim {% endif %} * - * Optimization: Single-Read Strategy - * ────────────────────────────────────────────────────────────────────────────────────────── + * @par Optimization: Single-Read Strategy + * @verbatim * Max wire size = {{ read_bits }} bits ≤ 56-bit READ_BITS limit. * We read all {{ read_bits }} bits in ONE call, then use bit arithmetic to extract: {% if is_extensible %} @@ -105,17 +110,18 @@ Template Context (from generator): * {{ read_bits }}-bit read layout (left-justified from bit 0): * [F0..F{{ root_size - 1 }}] ({{ root_size }} flag bits, no extension marker) {% endif %} + * @endverbatim */ #ifndef J2735_INTERNAL_DE_{{ typedef_name | upper }}_H #define J2735_INTERNAL_DE_{{ typedef_name | upper }}_H #include "J2735_internal_common.h" +{% if is_extensible %} /* ============================================================================================== */ /* Constants */ /* ============================================================================================== */ {% include 'bitstring/bitstring_internal_root_size.j2' %} -{% if is_extensible %} {% include 'bitstring/bitstring_internal_ext_size.j2' %} @@ -128,14 +134,15 @@ _Static_assert(J2735_INTERNAL_MAX_WIRE_BITS_{{ typedef_name_upper }} == {% endif %} {% if named_bits %} -{% set bit_position_constants_line = "@note Internal use only. Use the public J2735_" ~ (typedef_name_upper) ~ "_GET_*() accessors instead." %} +{% set note_line2 = "Use the public J2735_" ~ typedef_name_upper ~ "_GET_*() accessors instead." %} /* ============================================================================================== */ /* INTERNAL: Bit Position Constants */ /* */ /* ASN.1 BIT STRING numbering convention: bit 0 = MSB (leftmost in wire order). */ /* These constants map semantic flag names to their ASN.1 bit positions. */ /* */ -/* {{ "%-*s"|format(W, bit_position_constants_line) }} */ +/* @note Internal use only. */ +/* {{ "%-*s"|format(W, note_line2) }} */ /* ============================================================================================== */ {% include 'bitstring/bitstring_internal_bit_pos.j2' %} {% endif %} diff --git a/tools/templates/assemble_df_choice.j2 b/tools/templates/assemble_df_choice.j2 index be1fbcb..588dbfe 100644 --- a/tools/templates/assemble_df_choice.j2 +++ b/tools/templates/assemble_df_choice.j2 @@ -21,7 +21,7 @@ Assembles all sub-templates into a complete C header file for a CHOICE type. - Template Context (from generator): + Context variables: - typedef: ASN1TypeDefinition for the CHOICE type - alternatives: List of dicts with name, type_ref, bit_width, index, shift, needs_shift @@ -43,13 +43,9 @@ * @brief J2735 {{ typedef_name }} Definition and Access Macros. * * @par {{ typedef_name }} Wire Format (UPER): - * @code - * {{ typedef_name }} ::= CHOICE { -{% for alt in alternatives %} - * {{ alt.name }} {{ alt.type_ref }}{{ "," if not loop.last else "" }} -- {{ alt.bit_width }} bits (J2735_BW_{{ alt.type_ref | screaming_snake }}) -{% endfor %} - * } - * @endcode + * @verbatim +{% include 'asn1_definition.j2' %} + * @endverbatim * * This is a {{ "extensible" if typedef.constraint.is_extensible else "non-extensible" }} CHOICE with {{ alternatives | length }} alternatives. * Per ITU-T X.691 §23, the choice index uses ceil(log2({{ alternatives | length }})) = {{ index_bits }} bit{{ "s" if index_bits != 1 else "" }}. @@ -89,6 +85,9 @@ /* ============================================================================================== */ {% include 'choice/choice_internal_choice_index_bits.j2' %} +_Static_assert(J2735_INTERNAL_CHOICE_INDEX_BITS_{{ typedef_name_upper }} == J2735_BW_{{ typedef_name_upper }}, + "CHOICE index bits must match J2735_BW_{{ typedef_name_upper }}"); + /* ============================================================================================== */ /* INTERNAL: Structure Metadata */ /* ============================================================================================== */ diff --git a/tools/templates/assemble_df_sequence.j2 b/tools/templates/assemble_df_sequence.j2 index 64d5774..6c3d985 100644 --- a/tools/templates/assemble_df_sequence.j2 +++ b/tools/templates/assemble_df_sequence.j2 @@ -22,14 +22,14 @@ Main assembly template for SEQUENCE types. Generates a complete C header file by including sub-templates from the sequence/ directory. - Template Context (from generator): + Context variables: - typedef: ASN1TypeDefinition for the SEQUENCE type - sequence_variants: list[SequenceWireVariant] from get_sequence_variants() - opt_count: Number of OPTIONAL fields (for bitmap width) -#} {%- set has_optional = typedef.constraint.optional_count > 0 -%} {%- set typedef_name = typedef.name -%} -{% include 'license.j2' %} +{% include 'license.j2' %} /** * @file * @author Yogev Neumann @@ -43,66 +43,77 @@ #include "J2735_internal_common.h" #include "J2735_internal_constants.h" -{% if typedef.constraint.is_extensible %} +{% if typedef.constraint.is_extensible %} #include "J2735_internal_inline.h" -{% endif %} +{% endif %} /* ============================================================================================== */ /* INTERNAL: Structure Metadata */ /* ============================================================================================== */ -{% include 'sequence/sequence_internal_prefix_bits.j2' %} +{% include 'sequence/sequence_internal_prefix_bits.j2' %} -{% if typedef.constraint.is_extensible %} -{% include 'sequence/sequence_internal_root_size_bits.j2' %} +{% if typedef.constraint.is_extensible %} +{% include 'sequence/sequence_internal_root_size_bits.j2' %} -{% endif %} -{% if has_optional %} +_Static_assert(J2735_INTERNAL_ROOT_SIZE_BITS_{{ typedef_name | screaming_snake }} == + (J2735_INTERNAL_PREFIX_BITS_{{ typedef_name | screaming_snake }} + J2735_BW_{{ typedef_name | screaming_snake }}), + "ROOT_SIZE_BITS must equal prefix + J2735_BW_{{ typedef_name | screaming_snake }}"); + +{% endif %} +{% if has_optional %} /* ============================================================================================== */ /* INTERNAL: Optional Field Indices */ /* (Bitmap position for each OPTIONAL field, 0-indexed from MSB) */ /* ============================================================================================== */ -{% include 'sequence/sequence_internal_opt.j2' %} +{% include 'sequence/sequence_internal_opt.j2' %} -{% endif %} -{% if has_optional %} +{% endif %} +{% if has_optional %} /* ============================================================================================== */ /* INTERNAL: Dynamic Widths */ /* (Returns 0 if field absent, else J2735_BW_ or computed width) */ /* ============================================================================================== */ -{% include 'sequence/sequence_internal_width.j2' %} +{% include 'sequence/sequence_internal_width.j2' %} -{% endif %} +{% endif %} /* ============================================================================================== */ /* INTERNAL: Field Offsets */ /* (Cumulative bit offset: prev_offset + prev_width) */ /* ============================================================================================== */ -{% include 'sequence/sequence_internal_off.j2' %} +{% include 'sequence/sequence_internal_off.j2' %} + +{% if not typedef.constraint.is_extensible and not has_optional and typedef.constraint.uper_bit_width is not none %} +{%- set last_field = typedef.constraint.fields[-1] -%} +_Static_assert((J2735_INTERNAL_OFF_{{ typedef_name | screaming_snake }}_{{ last_field.name | screaming_snake }}(0) + J2735_BW_{{ last_field.type_name | screaming_snake }}) == + J2735_BW_{{ typedef_name | screaming_snake }}, + "{{ typedef_name }} offset chain must sum to J2735_BW_{{ typedef_name | screaming_snake }}"); -{% if has_optional %} +{% endif %} +{% if has_optional %} /* ============================================================================================== */ /* PUBLIC API: Has-Checkers (OPTIONAL Fields) */ /* ============================================================================================== */ -{% include 'sequence/sequence_has_field.j2' %} +{% include 'sequence/sequence_has_field.j2' %} -{% endif %} -{% if typedef.constraint.is_extensible %} +{% endif %} +{% if typedef.constraint.is_extensible %} /* ============================================================================================== */ /* PUBLIC API: Has-Extension Checker */ /* ============================================================================================== */ -{% include 'sequence/sequence_has_extension.j2' %} +{% include 'sequence/sequence_has_extension.j2' %} -{% endif %} +{% endif %} /* ============================================================================================== */ /* PUBLIC API: Field Getters */ /* ============================================================================================== */ -{% include 'sequence/sequence_get.j2' %} +{% include 'sequence/sequence_get.j2' %} -{% if typedef.constraint.is_extensible %} +{% if typedef.constraint.is_extensible %} /* ============================================================================================== */ /* PUBLIC API: Size Function */ /* ============================================================================================== */ -{% include 'sequence/sequence_size.j2' %} +{% include 'sequence/sequence_size.j2' %} -{% endif %} +{% endif %} #endif /* J2735_INTERNAL_DF_{{ typedef_name | upper }}_H */ {# Blank Line #} \ No newline at end of file diff --git a/tools/templates/bitstring/bitstring_get.j2 b/tools/templates/bitstring/bitstring_get.j2 index f5acff4..b0c6216 100644 --- a/tools/templates/bitstring/bitstring_get.j2 +++ b/tools/templates/bitstring/bitstring_get.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_get.j2 -Purpose: Generate public C macro for getting all flags as a single integer value. + BIT STRING Public Get-All Macro Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates public C macro for getting all flags as a single integer value. -Example output (is_extensible=True, root_size=13, ext_bits=14): + Context variables: + - typedef: The full typedef object containing the metadata + + Output format: /** * @brief Get all VehicleEventFlags as a single uint16_t value. * ... diff --git a/tools/templates/bitstring/bitstring_get_one.j2 b/tools/templates/bitstring/bitstring_get_one.j2 index c1dc01d..fb4ed0a 100644 --- a/tools/templates/bitstring/bitstring_get_one.j2 +++ b/tools/templates/bitstring/bitstring_get_one.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_get_one.j2 -Purpose: Generate public C macros for accessing individual flags from a BIT STRING. + BIT STRING Public Get-One Macro Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates public C macros for accessing individual flags from a BIT STRING. -Example output: + Context variables: + - typedef: The full typedef object containing the metadata + + Output format: /** * @brief Get eventHazardLights flag (ASN.1 bit 0). * ... diff --git a/tools/templates/bitstring/bitstring_internal_get_all.j2 b/tools/templates/bitstring/bitstring_internal_get_all.j2 index a6f7abe..91da709 100644 --- a/tools/templates/bitstring/bitstring_internal_get_all.j2 +++ b/tools/templates/bitstring/bitstring_internal_get_all.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_internal_get_all.j2 -Purpose: Generate C macro for extracting all flag bits from pre-read raw value. + BIT STRING Internal Get-All Macro Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates C macro for extracting all flag bits from pre-read raw value. -Example output (is_extensible=True, read_bits=22, root_size=13, ext_bits=14): + Context variables: + - typedef: The full typedef object containing the metadata + + Output format: /** * @internal * @brief Extract all flag bits from pre-read 22-bit raw value. @@ -43,6 +44,9 @@ Example output (is_extensible=True, read_bits=22, root_size=13, ext_bits=14): {%- set root_shift = read_bits - 1 - root_size -%}{#- Right shift for root extraction (e.g., 8) -#} {%- set root_mask = (2 ** root_size) - 1 -%}{#- Mask for root bits (e.g., 0x1FFF) -#} {%- set return_type = ext_bits | c_type -%} +{%- set hex_digits = {"uint8_t": 2, "uint16_t": 4, "uint32_t": 8, "uint64_t": 16}[return_type] -%} +{%- set hex_fmt = "%0" ~ hex_digits|string ~ "X" -%} +{%- set hex_zero = "0" * hex_digits -%} {% if typedef.constraint.is_extensible %} /** * @internal @@ -62,8 +66,8 @@ Example output (is_extensible=True, read_bits=22, root_size=13, ext_bits=14): * * @param[in] raw{{ read_bits }} Value previously returned by J2735_INTERNAL_RAW_READ_{{ typedef_name_upper }}(). * @return Right-aligned flag bits as {{ return_type }}: - * - {{ root_size }} significant bits (0x0000-0x{{ "%04X" | format(root_mask) }}) if non-extended - * - {{ ext_bits }} significant bits (0x0000-0x{{ "%04X" | format(ext_mask) }}) if extended + * - {{ root_size }} significant bits (0x{{ hex_zero }}-0x{{ hex_fmt | format(root_mask) }}) if non-extended + * - {{ ext_bits }} significant bits (0x{{ hex_zero }}-0x{{ hex_fmt | format(ext_mask) }}) if extended * @note Uses 1ULL for bit shifts to prevent undefined behavior if size >= 32 bits. * The compiler optimizes the truncation to {{ return_type }}. * @note Internal use only. Use J2735_{{ typedef_name_upper }}_GET() for public API. @@ -82,7 +86,7 @@ Example output (is_extensible=True, read_bits=22, root_size=13, ext_bits=14): * * @param[in] raw{{ read_bits }} Value previously returned by J2735_INTERNAL_RAW_READ_{{ typedef_name_upper }}(). * @return Right-aligned flag bits as {{ return_type }}: - * - {{ root_size }} significant bits (0x0000-0x{{ "%04X" | format(root_mask) }}) + * - {{ root_size }} significant bits (0x{{ hex_zero }}-0x{{ hex_fmt | format(root_mask) }}) * @note Internal use only. Use J2735_{{ typedef_name_upper }}_GET() for public API. */ #define J2735_INTERNAL_GET_ALL_{{ typedef_name_upper }}(raw{{ read_bits }}) \ diff --git a/tools/templates/bitstring/bitstring_internal_get_one.j2 b/tools/templates/bitstring/bitstring_internal_get_one.j2 index 47f087c..92d1a58 100644 --- a/tools/templates/bitstring/bitstring_internal_get_one.j2 +++ b/tools/templates/bitstring/bitstring_internal_get_one.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_internal_get_one.j2 -Purpose: Generate C macro for extracting a single flag bit from pre-read raw value. + BIT STRING Internal Get-One Macro Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates C macro for extracting a single flag bit from pre-read raw value. -Example output (is_extensible=True, read_bits=22, ext_flag_base=13, root_flag_base=20): + Context variables: + - typedef: The full typedef object containing the metadata + + Output format: /** * @internal * @brief Extract a single flag bit from pre-read 22-bit raw value. diff --git a/tools/templates/bitstring/bitstring_internal_raw_read.j2 b/tools/templates/bitstring/bitstring_internal_raw_read.j2 index f1f25e4..924007b 100644 --- a/tools/templates/bitstring/bitstring_internal_raw_read.j2 +++ b/tools/templates/bitstring/bitstring_internal_raw_read.j2 @@ -62,5 +62,5 @@ {% if is_extensible %} J2735_READ_BITS((buf), 0U, J2735_INTERNAL_MAX_WIRE_BITS_{{ typedef_name_upper }}) {% else %} - J2735_READ_BITS((buf), 0U, J2735_INTERNAL_ROOT_SIZE_{{ typedef_name_upper }}) + J2735_READ_BITS((buf), 0U, J2735_BW_{{ typedef_name_upper }}) {% endif %} diff --git a/tools/templates/bitstring/bitstring_is_extended.j2 b/tools/templates/bitstring/bitstring_is_extended.j2 index 0f6bba5..92b3874 100644 --- a/tools/templates/bitstring/bitstring_is_extended.j2 +++ b/tools/templates/bitstring/bitstring_is_extended.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_is_extended.j2 -Purpose: Generate public C macro for checking if a BIT STRING is in extended form. + BIT STRING Public Is-Extended Macro Template -Template Context (from generator): - - typedef: The full typedef object containing the metadata + Generates public C macro for checking if a BIT STRING is in extended form. -Example output (is_extensible=True): + Context variables: + - typedef: The full typedef object containing the metadata + + Output format: /** * @brief Check if VehicleEventFlags is in extended form. * ... diff --git a/tools/templates/bitstring/bitstring_size.j2 b/tools/templates/bitstring/bitstring_size.j2 index 325c174..6696a02 100644 --- a/tools/templates/bitstring/bitstring_size.j2 +++ b/tools/templates/bitstring/bitstring_size.j2 @@ -1,29 +1,30 @@ {#- -Copyright 2026 Yogev Neumann + Copyright 2026 Yogev Neumann -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 + 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 - http://www.apache.org/licenses/LICENSE-2.0 + http://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. + 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. -SPDX-License-Identifier: Apache-2.0 -SPDX-FileCopyrightText: 2026 Yogev Neumann + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2026 Yogev Neumann -#} {#- -Template: bitstring_size.j2 -Purpose: Generate public C macro for getting wire size of a BIT STRING in bits. + BIT STRING Public Size Macro Template -Template Context (from generator): + Generates public C macro for getting wire size of a BIT STRING in bits. + + Context variables: - typedef: The full typedef object containing the metadata -Example output (is_extensible=True, read_bits=22): + Output format: /** * @brief Get wire size of VehicleEventFlags in bits. * ... @@ -62,5 +63,5 @@ Example output (is_extensible=True, read_bits=22): * @param[in] buf Pointer to the start of the {{ typedef_name }} UPER encoding (const uint8_t*). * @return Always {{ root_size }}U. */ -#define J2735_{{ typedef_name_upper }}_SIZE(buf) ((void)(buf), J2735_INTERNAL_ROOT_SIZE_{{ typedef_name_upper }}) +#define J2735_{{ typedef_name_upper }}_SIZE(buf) ((void)(buf), J2735_BW_{{ typedef_name_upper }}) {% endif %} diff --git a/tools/templates/bitwidth_constants.j2 b/tools/templates/bitwidth_constants.j2 index 0717352..c1017e0 100644 --- a/tools/templates/bitwidth_constants.j2 +++ b/tools/templates/bitwidth_constants.j2 @@ -51,7 +51,7 @@ {#- Build the comment based on type_class -#} {%- if t.type_class.name == "BIT_STRING" -%} {#- BIT STRING: Show SIZE -#} -{%- set comment = t.name ~ ": BIT STRING (SIZE(" ~ t.constraint.size ~ "))" -%} +{%- set comment = t.name ~ ": BIT STRING (SIZE(" ~ t.constraint.root_size ~ "))" -%} {%- elif t.type_class.name == "BOOLEAN" -%} {#- BOOLEAN: Simple -#} {%- set comment = t.name ~ ": BOOLEAN" -%} diff --git a/tools/templates/wire_format_choice_section.j2 b/tools/templates/wire_format_choice_section.j2 index 0c20519..ec00d7a 100644 --- a/tools/templates/wire_format_choice_section.j2 +++ b/tools/templates/wire_format_choice_section.j2 @@ -34,18 +34,18 @@ Output (rendered inside an existing Doxygen comment block): * @par Wire Format (approach selected, 5 bits): - * @code + * @verbatim * ┌──────────┬──────────────────────────────┐ * │ Bit 0 │ Bits 1-4 │ * ├──────────┼──────────────────────────────┤ * │ Index = 0│ ApproachID value (4 bits) │ * └──────────┴──────────────────────────────┘ - * @endcode + * @endverbatim -#} {% for variant in choice_variants %} * * @par Wire Format ({{ variant.name }}): - * @code + * @verbatim {# Build segments from domain data (header, content, min_width) -#} {%- set segments = [] -%} {#- Index segment -#} @@ -79,5 +79,5 @@ * │{% for i in range(segments | length) %} {{ "%-*s" | format(widths[i], segments[i][1]) }} │{% endfor %} * └{% for w in widths %}{{ "─" * (w + 2) }}{% if not loop.last %}┴{% endif %}{% endfor %}┘ - * @endcode + * @endverbatim {% endfor %} diff --git a/tools/templates/wire_format_sequence_section.j2 b/tools/templates/wire_format_sequence_section.j2 index 90c12e2..3516ac3 100644 --- a/tools/templates/wire_format_sequence_section.j2 +++ b/tools/templates/wire_format_sequence_section.j2 @@ -39,18 +39,18 @@ * Wire Format (N bits): * ┌─── ...table... ───┘ #} - * @code + * @verbatim {% include 'asn1_definition.j2' %} - * @endcode + * @endverbatim {% for variant in sequence_variants %} * * @par Wire Format ({{ variant.name }}): - * @code + * @verbatim {% set segment_count = variant.fields | length + (1 if variant.ext_bit is not none else 0) + (1 if variant.opt_bitmap else 0) + (1 if variant.ext_bit == 1 else 0) %} {% if segment_count > 6 %} {% include 'wire_format_compact.j2' %} {% else %} {% include 'wire_format_table.j2' %} {% endif %} - * @endcode + * @endverbatim {% endfor %} diff --git a/tools/tests/c_generator/test_assemble_de_bitstring.py b/tools/tests/c_generator/test_assemble_de_bitstring.py index c081053..ba6f2b4 100644 --- a/tools/tests/c_generator/test_assemble_de_bitstring.py +++ b/tools/tests/c_generator/test_assemble_de_bitstring.py @@ -22,7 +22,7 @@ """ from tools.j2735_c_generator_data_element import generate_data_element -from tools.tests.conftest import SpecLoadingTestBase +from tools.tests.conftest import NON_EXTENSIBLE_BITSTRING_TYPES, SpecLoadingTestBase class TestAssembleBitstringWireFormatDocs(SpecLoadingTestBase): @@ -49,6 +49,75 @@ def test_extensible_wire_format_doc_shows_both_forms(self) -> None: self.assertIn("Non-extended:", code) self.assertIn("Extended:", code) + def test_non_extensible_no_constants_banner(self) -> None: + """Non-extensible output must not emit an empty Constants section.""" + for type_name, _prefix in NON_EXTENSIBLE_BITSTRING_TYPES: + with self.subTest(type_name=type_name): + code = generate_data_element(type_name, self.spec) + self.assertNotIn( + "/* Constants", + code, + f"{type_name} should not have a Constants banner", + ) + + def test_extensible_has_constants_banner(self) -> None: + """Extensible output must include the Constants section.""" + code = generate_data_element("VehicleEventFlags", self.spec) + self.assertIn("/* Constants", code) + + def test_non_extensible_assembled_uses_bw_not_root_size(self) -> None: + """Non-extensible assembled output must use J2735_BW_*, not ROOT_SIZE.""" + for type_name, prefix in NON_EXTENSIBLE_BITSTRING_TYPES: + with self.subTest(type_name=type_name): + code = generate_data_element(type_name, self.spec) + self.assertNotIn( + f"J2735_INTERNAL_ROOT_SIZE_{prefix}", + code, + f"{type_name} assembled output should not contain ROOT_SIZE", + ) + self.assertIn( + f"J2735_BW_{prefix}", + code, + f"{type_name} assembled output should reference J2735_BW_*", + ) + + def test_extensible_assembled_uses_root_size(self) -> None: + """Extensible assembled output must define ROOT_SIZE.""" + code = generate_data_element("VehicleEventFlags", self.spec) + self.assertIn("J2735_INTERNAL_ROOT_SIZE_VEHICLE_EVENT_FLAGS", code) + + def test_wire_format_uses_doxygen_verbatim_blocks(self) -> None: + """Wire format diagrams must be wrapped in @par + @verbatim/@endverbatim.""" + for type_name, _prefix in NON_EXTENSIBLE_BITSTRING_TYPES: + with self.subTest(type_name=type_name): + code = generate_data_element(type_name, self.spec) + self.assertIn("@par Wire Format", code) + self.assertIn("@verbatim", code) + self.assertIn("@endverbatim", code) + self.assertNotIn( + "@code", + code, + f"{type_name}: wire-format diagrams should use @verbatim, not @code", + ) + self.assertNotIn( + "@endcode", + code, + f"{type_name}: wire-format diagrams should use @endverbatim, not @endcode", + ) + self.assertEqual( + code.count("@verbatim"), + code.count("@endverbatim"), + f"{type_name}: @verbatim/@endverbatim tags must be paired", + ) + + def test_asn1_definition_uses_verbatim(self) -> None: + """ASN.1 grammar block must use @verbatim, not @code.""" + for type_name, _prefix in NON_EXTENSIBLE_BITSTRING_TYPES: + with self.subTest(type_name=type_name): + code = generate_data_element(type_name, self.spec) + self.assertIn("@verbatim", code) + self.assertIn("@endverbatim", code) + def test_extensible_wire_format_doc_correct_bit_counts(self) -> None: """Extensible wire format shows correct bit counts for both forms.""" code = generate_data_element("VehicleEventFlags", self.spec) diff --git a/tools/tests/c_generator/test_assemble_df_sequence.py b/tools/tests/c_generator/test_assemble_df_sequence.py new file mode 100644 index 0000000..e9f0f29 --- /dev/null +++ b/tools/tests/c_generator/test_assemble_df_sequence.py @@ -0,0 +1,83 @@ +# Copyright 2026 Yogev Neumann +# +# 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 +# +# http://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. +# +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: 2026 Yogev Neumann +"""Tests for SEQUENCE type assembly via generate_data_frame().""" + +from typing import ClassVar +from unittest import TestCase + +from tools.j2735_c_generator_data_frame import generate_data_frame +from tools.j2735_spec_constraints import SequenceType +from tools.j2735_spec_parser import J2735Specification, parse_spec_file +from tools.tests.conftest import SPEC_FILE_PATH + + +class TestAssembleDfSequenceStaticAsserts(TestCase): + """Tests for _Static_assert generation in assembled SEQUENCE headers.""" + + spec: ClassVar[J2735Specification] + + @classmethod + def setUpClass(cls) -> None: + """Load spec once for all tests.""" + cls.spec = parse_spec_file(SPEC_FILE_PATH) + + def test_extensible_sequence_has_root_size_assert(self) -> None: + """Extensible SEQUENCE validates ROOT_SIZE_BITS == PREFIX + BW_*.""" + code = generate_data_frame("PathPrediction", self.spec) + self.assertIn( + "_Static_assert(J2735_INTERNAL_ROOT_SIZE_BITS_PATH_PREDICTION", + code, + ) + self.assertIn("J2735_BW_PATH_PREDICTION", code) + + def test_non_extensible_no_optional_has_offset_chain_assert(self) -> None: + """Non-extensible SEQUENCE with no OPTIONAL validates offset chain.""" + code = generate_data_frame("BSMcoreData", self.spec) + self.assertIn("_Static_assert(", code) + self.assertIn("J2735_BW_BSM_CORE_DATA", code) + self.assertIn("J2735_BW_VEHICLE_SIZE", code) + + def test_non_extensible_with_optional_has_no_offset_assert(self) -> None: + """Non-extensible SEQUENCE with OPTIONAL has no offset chain assert.""" + code = generate_data_frame("IntersectionReferenceID", self.spec) + self.assertNotIn("_Static_assert(", code) + + def test_variable_width_sequence_excluded_from_offset_assert(self) -> None: + """Non-extensible, no-optional SEQUENCE with variable-width fields skips assert. + + The template guard requires ``uper_bit_width is not none`` so that + SEQUENCE types containing SEQUENCE OF or unresolved inline types + (e.g. RestrictionClassAssignment, Sample) do not emit a + _Static_assert referencing a nonexistent J2735_BW_* constant. + """ + # These are non-extensible, no-optional but have variable-width fields + variable_width_types = ["Circle", "PrivilegedEvents"] + for type_name in variable_width_types: + with self.subTest(type_name=type_name): + typedef = self.spec.lookup_type(type_name) + self.assertIsNotNone(typedef, f"{type_name} not in spec") + assert typedef is not None # narrow for type checkers + constraint = typedef.constraint + assert isinstance(constraint, SequenceType) + self.assertFalse(constraint.is_extensible) + self.assertFalse( + any(f.is_optional for f in constraint.fields), + ) + self.assertIsNone( + constraint.uper_bit_width, + f"{type_name} should have variable width (None)", + ) diff --git a/tools/tests/c_generator/test_bitstring_internal_raw_read.py b/tools/tests/c_generator/test_bitstring_internal_raw_read.py index fe14776..7cb7a53 100644 --- a/tools/tests/c_generator/test_bitstring_internal_raw_read.py +++ b/tools/tests/c_generator/test_bitstring_internal_raw_read.py @@ -67,15 +67,20 @@ def test_non_extensible_must_not_reference_max_wire_bits(self) -> None: f"{type_name} RAW_READ must not reference undefined MAX_WIRE_BITS", ) - def test_non_extensible_raw_read_uses_root_size(self) -> None: - """Non-extensible RAW_READ should read ROOT_SIZE bits, not MAX_WIRE_BITS.""" + def test_non_extensible_raw_read_uses_bw_constant(self) -> None: + """Non-extensible RAW_READ should read J2735_BW_* bits, not ROOT_SIZE.""" for type_name, prefix in NON_EXTENSIBLE_BITSTRING_TYPES: with self.subTest(type_name=type_name): code = generate_bitstring_code(_TEMPLATE_NAME, self.spec, type_name) self.assertIn( + f"J2735_BW_{prefix}", + code, + f"{type_name} RAW_READ should use J2735_BW_* as bit count", + ) + self.assertNotIn( f"J2735_INTERNAL_ROOT_SIZE_{prefix}", code, - f"{type_name} RAW_READ should use ROOT_SIZE as bit count", + f"{type_name} RAW_READ should not reference ROOT_SIZE", ) def test_extensible_raw_read_uses_max_wire_bits(self) -> None: diff --git a/tools/tests/c_generator/test_bitstring_size.py b/tools/tests/c_generator/test_bitstring_size.py index 62b237e..37f7fb7 100644 --- a/tools/tests/c_generator/test_bitstring_size.py +++ b/tools/tests/c_generator/test_bitstring_size.py @@ -79,10 +79,10 @@ def test_non_extensible_size_must_not_include_extension_marker_bits(self) -> Non f"(non-extensible types have no extension marker on wire)", ) - def test_non_extensible_size_evaluates_to_root_size(self) -> None: - """Non-extensible SIZE #define should expand to just ROOT_SIZE. + def test_non_extensible_size_evaluates_to_bw_constant(self) -> None: + """Non-extensible SIZE #define should expand to J2735_BW_*. - The #define line for SIZE must reference ROOT_SIZE and nothing else. + The #define line for SIZE must reference J2735_BW_* and nothing else. """ for type_name, prefix in NON_EXTENSIBLE_BITSTRING_TYPES: with self.subTest(type_name=type_name): @@ -99,9 +99,9 @@ def test_non_extensible_size_evaluates_to_root_size(self) -> None: ) for line in size_defines: self.assertIn( - f"J2735_INTERNAL_ROOT_SIZE_{prefix}", + f"J2735_BW_{prefix}", line, - f"{type_name} SIZE #define should reference ROOT_SIZE: {line}", + f"{type_name} SIZE #define should reference J2735_BW_*: {line}", ) self.assertNotIn( "EXTENSION_MARKER_BITS", diff --git a/tools/tests/c_generator/test_choice_type.py b/tools/tests/c_generator/test_choice_type.py index bda0a6c..f3c8ad0 100644 --- a/tools/tests/c_generator/test_choice_type.py +++ b/tools/tests/c_generator/test_choice_type.py @@ -127,6 +127,15 @@ def test_misra_compliant_no_shift_by_zero(self) -> None: # The mask-only pattern should have a comment indicating no shift needed self.assertIn("no shift needed", code.lower()) + def test_generates_static_assert_choice_index_bits(self) -> None: + """Generated code validates CHOICE index bits with _Static_assert.""" + code = generate_data_frame("ApproachOrLane", self.spec) + self.assertIn( + "_Static_assert(J2735_INTERNAL_CHOICE_INDEX_BITS_APPROACH_OR_LANE" + " == J2735_BW_APPROACH_OR_LANE", + code, + ) + def test_unknown_type_raises(self) -> None: """generate_data_frame raises ValueError for unknown type.""" with self.assertRaises(ValueError) as cm: diff --git a/tools/tests/c_generator/test_jinja_filters.py b/tools/tests/c_generator/test_jinja_filters.py index cda4d49..edbd955 100644 --- a/tools/tests/c_generator/test_jinja_filters.py +++ b/tools/tests/c_generator/test_jinja_filters.py @@ -19,8 +19,8 @@ Tests cover all filters registered in create_jinja_env(): - filter_format_range: value range formatting for SequenceField - filter_is_signed: signed/unsigned detection for SequenceField - - filter_screaming_snake: CamelCase → SCREAMING_SNAKE_CASE - - filter_snake_case: CamelCase → snake_case + - filter_screaming_snake: CamelCase -> SCREAMING_SNAKE_CASE + - filter_snake_case: CamelCase -> snake_case """ from unittest import TestCase @@ -31,6 +31,7 @@ filter_screaming_snake, filter_snake_case, ) +from tools.j2735_spec_constraints import SequenceField, TypeReference from tools.tests.conftest import ( make_bitstring_field, make_integer_field, @@ -62,6 +63,16 @@ def test_digits_in_name(self) -> None: self.assertEqual(filter_screaming_snake("AccelerationSet4Way"), "ACCELERATION_SET_4_WAY") self.assertEqual(filter_screaming_snake("Type2Value"), "TYPE_2_VALUE") + def test_hyphenated_asn1_names(self) -> None: + """Handle ASN.1 names with hyphens (e.g., Offset-B10, Node-LL-24B).""" + self.assertEqual(filter_screaming_snake("Offset-B10"), "OFFSET_B_10") + self.assertEqual(filter_screaming_snake("Node-LL-24B"), "NODE_LL_24_B") + self.assertEqual(filter_screaming_snake("OffsetLL-B12"), "OFFSET_LL_B_12") + self.assertEqual(filter_screaming_snake("NMEA-MsgType"), "NMEA_MSG_TYPE") + self.assertEqual(filter_screaming_snake("RTCM-Revision"), "RTCM_REVISION") + self.assertEqual(filter_screaming_snake("VertOffset-B07"), "VERT_OFFSET_B_07") + self.assertEqual(filter_screaming_snake("TimeInSecond-B16"), "TIME_IN_SECOND_B_16") + def test_already_screaming(self) -> None: """Already SCREAMING_SNAKE stays the same.""" self.assertEqual(filter_screaming_snake("MSG_COUNT"), "MSG_COUNT") @@ -92,13 +103,20 @@ def test_digits_in_name(self) -> None: self.assertEqual(filter_snake_case("AccelerationSet4Way"), "acceleration_set_4_way") self.assertEqual(filter_snake_case("Type2Value"), "type_2_value") + def test_hyphenated_asn1_names(self) -> None: + """Handle ASN.1 names with hyphens.""" + self.assertEqual(filter_snake_case("Offset-B10"), "offset_b_10") + self.assertEqual(filter_snake_case("Node-LL-24B"), "node_ll_24_b") + self.assertEqual(filter_snake_case("OffsetLL-B12"), "offset_ll_b_12") + self.assertEqual(filter_snake_case("NMEA-MsgType"), "nmea_msg_type") + def test_already_snake(self) -> None: """Already snake_case stays the same.""" self.assertEqual(filter_snake_case("msg_count"), "msg_count") # ============================================================================= -# Tests — filter_is_signed() +# Tests - filter_is_signed() # ============================================================================= @@ -130,9 +148,21 @@ def test_bitstring_field_is_unsigned(self) -> None: field = make_bitstring_field("flags", "Flags", 8) self.assertFalse(filter_is_signed(field)) + def test_unresolved_type_reference_is_unsigned(self) -> None: + """Unresolved TypeReference has no min_value and returns False.""" + field = SequenceField( + name="x", + type_name="Unknown", + type=TypeReference(name="Unknown"), + is_optional=False, + section_comment="", + inline_comment="", + ) + self.assertFalse(filter_is_signed(field)) + # ============================================================================= -# Tests — filter_format_range() +# Tests - filter_format_range() # ============================================================================= @@ -160,6 +190,18 @@ def test_single_value_range(self) -> None: self.assertEqual(filter_format_range(field), "42..42") def test_bitstring_returns_empty(self) -> None: - """BIT STRING field has no range → returns empty string.""" + """BIT STRING field has no range -> returns empty string.""" field = make_bitstring_field("flags", "Flags", 8) self.assertEqual(filter_format_range(field), "") + + def test_unresolved_type_reference_returns_empty(self) -> None: + """Unresolved TypeReference has no range and returns empty string.""" + field = SequenceField( + name="x", + type_name="Unknown", + type=TypeReference(name="Unknown"), + is_optional=False, + section_comment="", + inline_comment="", + ) + self.assertEqual(filter_format_range(field), "") diff --git a/tools/tests/c_generator/test_wire_format_templates.py b/tools/tests/c_generator/test_wire_format_templates.py index 16c475f..6b2444d 100644 --- a/tools/tests/c_generator/test_wire_format_templates.py +++ b/tools/tests/c_generator/test_wire_format_templates.py @@ -47,6 +47,7 @@ make_nested_mock_spec, make_optional_mock_spec, make_sequence, + make_variable_width_field, ) # ============================================================================= @@ -137,7 +138,7 @@ def _render_real_asn1(type_name: str, spec: J2735Specification) -> str: # ============================================================================= -# Test Helpers — Synthetic Type Builders +# Test Helpers - Synthetic Type Builders # ============================================================================= @@ -196,12 +197,12 @@ def _extract_row_widths(line: str) -> list[int]: Returns: List of cell widths (number of '─' chars per cell). """ - # Find all runs of ─ characters + # Find all runs of `─` characters return [len(m.group()) for m in re.finditer(r"─+", line)] # ============================================================================= -# Tests — ASN.1 Definition Template +# Tests - ASN.1 Definition Template # ============================================================================= @@ -358,6 +359,24 @@ def test_commas_between_fields(self) -> None: elif "TypeC" in type_part: self.assertNotIn(",", type_part) + def test_variable_width_field_shows_variable(self) -> None: + """Variable-width field shows '-- variable' comment.""" + typedef = _make_typedef( + "TestType", + fields=( + make_integer_field( + "a", + "TypeA", + 0, + 127, + ), + make_variable_width_field("items", "NodeList"), + ), + ) + output = _render_asn1(typedef) + + self.assertIn("-- variable", output) + def test_doxygen_prefix(self) -> None: """Every content line starts with ' * '.""" typedef = _make_typedef( @@ -375,7 +394,7 @@ def test_doxygen_prefix(self) -> None: # ============================================================================= -# Tests — Wire Format Table Template (Column-Based) +# Tests - Wire Format Table Template (Column-Based) # ============================================================================= @@ -534,7 +553,7 @@ def test_extension_variant_placeholder(self) -> None: # ============================================================================= -# Tests — Wire Format Compact Template (Row-Based) +# Tests - Wire Format Compact Template (Row-Based) # ============================================================================= @@ -682,7 +701,7 @@ def test_extension_variant_placeholder(self) -> None: # ============================================================================= -# Tests — Bit Position Continuity (Regression) +# Tests - Bit Position Continuity (Regression) # ============================================================================= @@ -854,7 +873,7 @@ def test_last_bit_matches_total(self) -> None: # ============================================================================= -# Tests — Template Selection Logic +# Tests - Template Selection Logic # ============================================================================= @@ -891,7 +910,7 @@ def test_large_type_uses_compact_table(self) -> None: # ============================================================================= -# Tests — With Real Spec Fixtures +# Tests - With Real Spec Fixtures # ============================================================================= diff --git a/tools/tests/c_generator/test_wire_format_variants.py b/tools/tests/c_generator/test_wire_format_variants.py index 47f0cdd..a15c5ba 100644 --- a/tools/tests/c_generator/test_wire_format_variants.py +++ b/tools/tests/c_generator/test_wire_format_variants.py @@ -25,11 +25,19 @@ from unittest import TestCase from tools.j2735_c_generator_wire_format import ( + _VARIABLE_BITS, # pyright: ignore[reportPrivateUsage] SequenceWireVariant, + _has_variable_width, # pyright: ignore[reportPrivateUsage] _pluralize_bits, # pyright: ignore[reportPrivateUsage] + _sum_field_bits, # pyright: ignore[reportPrivateUsage] + _validate_fields_resolved, # pyright: ignore[reportPrivateUsage] get_sequence_variants, ) -from tools.j2735_spec_constraints import SequenceType +from tools.j2735_spec_constraints import ( + SequenceField, + SequenceType, + TypeReference, +) from tools.j2735_spec_parser import J2735Specification from tools.tests.conftest import ( get_sequence_typedef, @@ -39,6 +47,7 @@ make_nested_mock_spec, make_optional_mock_spec, make_sequence, + make_variable_width_field, ) @@ -61,10 +70,103 @@ def _get_variants(type_name: str, spec: J2735Specification) -> list[SequenceWire # ============================================================================= -# Tests — _pluralize_bits() +# Tests - _pluralize_bits() # ============================================================================= +class TestValidateFieldsResolved(TestCase): + """Tests for _validate_fields_resolved() - precondition guard.""" + + def test_resolved_fields_pass(self) -> None: + """Fields with resolved types do not raise.""" + fields = ( + make_integer_field("a", "TypeA", 0, 127), + make_integer_field("b", "TypeB", 0, 255), + ) + _validate_fields_resolved(fields) # Should not raise + + def test_empty_tuple_passes(self) -> None: + """Empty field tuple does not raise.""" + _validate_fields_resolved(()) # Should not raise + + def test_unresolved_type_reference_raises(self) -> None: + """An unresolved TypeReference raises ValueError. + + This catches programming errors where type resolution was + skipped or failed before computing wire format. + """ + fields = ( + make_integer_field("a", "TypeA", 0, 127), + SequenceField( + name="b", + type_name="UnresolvedType", + type=TypeReference(name="UnresolvedType"), + is_optional=False, + section_comment="", + inline_comment="", + ), + ) + with self.assertRaises(ValueError): + _validate_fields_resolved(fields) + + def test_variable_width_field_passes(self) -> None: + """A resolved variable-width type does not raise.""" + fields = (make_variable_width_field("items", "NodeList"),) + _validate_fields_resolved(fields) # Should not raise + + +class TestHasVariableWidth(TestCase): + """Tests for _has_variable_width() - classification.""" + + def test_all_fixed_returns_false(self) -> None: + """All fixed-width fields return False.""" + fields = ( + make_integer_field("a", "TypeA", 0, 127), + make_integer_field("b", "TypeB", 0, 255), + ) + self.assertFalse(_has_variable_width(fields)) + + def test_empty_tuple_returns_false(self) -> None: + """Empty field tuple returns False.""" + self.assertFalse(_has_variable_width(())) + + def test_variable_width_returns_true(self) -> None: + """A SequenceOfType field returns True.""" + fields = ( + make_integer_field("a", "TypeA", 0, 127), + make_variable_width_field("items", "NodeList"), + ) + self.assertTrue(_has_variable_width(fields)) + + +class TestSumFieldBits(TestCase): + """Tests for _sum_field_bits() - sums fixed-width only.""" + + def test_resolved_fields_sum_correctly(self) -> None: + """Fields with known bit-widths sum to the correct total.""" + fields = ( + make_integer_field("a", "TypeA", 0, 127), # 7 bits + make_integer_field("b", "TypeB", 0, 255), # 8 bits + ) + self.assertEqual(_sum_field_bits(fields), 15) + + def test_empty_tuple_returns_zero(self) -> None: + """Empty field tuple returns 0.""" + self.assertEqual(_sum_field_bits(()), 0) + + def test_variable_width_raises_type_error(self) -> None: + """Variable-width fields raise TypeError. + + Callers must check _has_variable_width() first. + """ + fields = ( + make_integer_field("a", "TypeA", 0, 127), + make_variable_width_field("items", "NodeList"), + ) + with self.assertRaises(TypeError): + _sum_field_bits(fields) + + class TestPluralizeBits(TestCase): """Tests for _pluralize_bits() singular/plural grammar.""" @@ -86,7 +188,7 @@ def test_large_number_plural(self) -> None: # ============================================================================= -# Tests — get_sequence_variants() +# Tests - get_sequence_variants() # ============================================================================= @@ -223,7 +325,7 @@ def test_second_variant_variable_bits(self) -> None: ) variants = get_sequence_variants(seq) - self.assertEqual(variants[1].total_bits, "variable") + self.assertEqual(variants[1].total_bits, _VARIABLE_BITS) def test_both_variants_have_all_fields(self) -> None: """Both variants contain all fields.""" @@ -474,7 +576,7 @@ def test_mixed_widths(self) -> None: self.assertEqual(variants[0].total_bits, 48) def test_signed_fields_dont_affect_bit_width(self) -> None: - """Signed vs unsigned with same range size → same bit width.""" + """Signed vs unsigned with same range size -> same bit width.""" seq_unsigned = make_sequence( fields=(make_integer_field("u", "U", 0, 65534),), # 16 bits ) @@ -501,7 +603,7 @@ def test_bitstring_field_width(self) -> None: class TestSequenceWireVariantImmutability(TestCase): - """SequenceWireVariant is a frozen dataclass — verify immutability.""" + """SequenceWireVariant is a frozen dataclass - verify immutability.""" def test_frozen(self) -> None: """SequenceWireVariant raises on attribute assignment.""" @@ -524,7 +626,7 @@ def test_fields_is_tuple(self) -> None: # ============================================================================= -# Tests — Regression: Variants Match SequenceType Properties +# Tests - Regression: Variants Match SequenceType Properties # ============================================================================= @@ -587,7 +689,7 @@ def test_optional_absent_matches_preamble_plus_required_fields(self) -> None: # ============================================================================= -# Tests — Real Spec Fixtures +# Tests - Real Spec Fixtures # ============================================================================= diff --git a/tools/tests/conftest.py b/tools/tests/conftest.py index ae6cacd..af00140 100644 --- a/tools/tests/conftest.py +++ b/tools/tests/conftest.py @@ -32,6 +32,7 @@ BitStringConstraint, IntegerConstraint, SequenceField, + SequenceOfType, SequenceType, ) from tools.j2735_spec_parser import ( @@ -537,6 +538,51 @@ def make_bitstring_field( ) +def make_variable_width_field( + name: str, + type_name: str, + *, + is_optional: bool = False, +) -> SequenceField: + """Create a SequenceField with a SequenceOfType (variable-width). + + Args: + name: Field name. + type_name: ASN.1 type name. + is_optional: Whether the field is OPTIONAL. + + Returns: + A SequenceField with ``uper_bit_width is None``. + + Examples: + >>> field = make_variable_width_field("items", "NodeList") + >>> field.name + 'items' + >>> field.type.uper_bit_width is None + True + >>> field.is_optional + False + >>> opt = make_variable_width_field("x", "T", is_optional=True) + >>> opt.is_optional + True + """ + return SequenceField( + name=name, + type_name=type_name, + type=SequenceOfType( + element_type=IntegerConstraint( + min_value=0, + max_value=255, + ), + min_size=1, + max_size=10, + ), + is_optional=is_optional, + section_comment="", + inline_comment="", + ) + + def make_sequence( fields: tuple[SequenceField, ...], *, diff --git a/tools/tests/spec/test_spec_entry.py b/tools/tests/spec/test_spec_entry.py index 7809bc4..8bc3636 100644 --- a/tools/tests/spec/test_spec_entry.py +++ b/tools/tests/spec/test_spec_entry.py @@ -32,7 +32,7 @@ ) # ============================================================================= -# Synthetic Blocks — Data Elements (Section 7) +# Synthetic Blocks - Data Elements (Section 7) # ============================================================================= _DE_FULL_BLOCK = """\ @@ -102,7 +102,7 @@ # ============================================================================= -# Synthetic Blocks — Data Frames (Section 6) +# Synthetic Blocks - Data Frames (Section 6) # ============================================================================= _DF_FULL_BLOCK = """\ @@ -140,7 +140,7 @@ # ============================================================================= -# Synthetic Blocks — Messages (Section 5) +# Synthetic Blocks - Messages (Section 5) # ============================================================================= _MSG_WITH_ABBREVIATION_BLOCK = """\ @@ -182,7 +182,7 @@ # ============================================================================= -# Test Cases — from_data_element_block +# Test Cases - from_data_element_block # ============================================================================= @@ -300,7 +300,7 @@ def test_asn1_description_propagated(self) -> None: # ============================================================================= -# Test Cases — from_data_frame_block +# Test Cases - from_data_frame_block # ============================================================================= @@ -385,7 +385,7 @@ def test_line_number_stored(self) -> None: # ============================================================================= -# Test Cases — from_message_block +# Test Cases - from_message_block # =============================================================================