ud3tn/components/bundle6/sdnv.c
Felix Walter de31d057ca Prevent side effects in macros
- Only access each argument once
- Use parentheses around macro arguments upon use

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2023-07-25 13:15:05 +02:00

204 lines
5.1 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "bundle6/sdnv.h"
/* 0b01111111 */
#define SDNV_VALUE_MASK 0x7F
/* 0b10000000 */
#define SDNV_MARKER_MASK 0x80
void sdnv_reset(struct sdnv_state *state)
{
state->status = SDNV_IN_PROGRESS;
state->error = SDNV_ERROR_NONE;
state->bytes_parsed = 0;
}
static inline int sdnv_validate_byte_u32
(struct sdnv_state *state, uint32_t *value)
{
/* returns true if the byte can fit into the already parsed value. */
/* This is independent of the value itself, but dependent on its */
/* length. Up to four bytes definitely fit; if we get a fifth byte, */
/* the value has to be checked whether it contains bits that would */
/* ´fall out´ when left-shifting by 7... */
/* a SDNV for the max. u32 0xFFFFFFFF would be 0x8FFFFFFF7F and */
/* after reading 4 bits the *value would be equal to 0x1FFFFFF */
return (
(state->bytes_parsed < 4) ||
(state->bytes_parsed == 4 && *value <= 0x1FFFFFF)
);
}
static inline int sdnv_validate_byte_u8
(struct sdnv_state *state, uint8_t *value)
{
/* max. SDNV: 0x817F */
return (
(state->bytes_parsed < 1) ||
(state->bytes_parsed == 1 && *value <= 0x1)
);
}
static inline int sdnv_validate_byte_u16
(struct sdnv_state *state, uint16_t *value)
{
/* max. SDNV: 0x83FF7F */
return (
(state->bytes_parsed < 2) ||
(state->bytes_parsed == 2 && *value <= 0x1FF)
);
}
static inline int sdnv_validate_byte_u64
(struct sdnv_state *state, uint64_t *value)
{
/* max. SDNV: 0x81FFFFFFFFFFFFFFFF7F */
return (
(state->bytes_parsed < 9) ||
(state->bytes_parsed == 9 && *value <= 0x1FFFFFFFFFFFFFF)
);
}
#define sdnv_read_generic(state, value, byte, validate_function) \
do { \
__typeof__(state) _state = (state); \
__typeof__(value) _value = (value); \
__typeof__(byte) _byte = (byte); \
if (!(validate_function)(_state, _value)) { \
_state->error = SDNV_ERROR_OVERFLOW; \
_state->status = SDNV_ERROR; \
} else if (_state->status == SDNV_DONE) { \
_state->error = SDNV_ERROR_ALREADY_DONE; \
_state->status = SDNV_ERROR; \
} else { \
if (_state->bytes_parsed == 0) \
*_value = 0; \
else \
*_value <<= 7; \
*_value |= (_byte & SDNV_VALUE_MASK); \
if (!(_byte & SDNV_MARKER_MASK)) \
_state->status = SDNV_DONE; \
++_state->bytes_parsed; \
} \
} while (0)
void sdnv_read_u8(struct sdnv_state *state, uint8_t *value, uint8_t byte)
{
sdnv_read_generic(state, value, byte, sdnv_validate_byte_u8);
}
void sdnv_read_u16(struct sdnv_state *state, uint16_t *value, uint8_t byte)
{
sdnv_read_generic(state, value, byte, sdnv_validate_byte_u16);
}
void sdnv_read_u32(struct sdnv_state *state, uint32_t *value, uint8_t byte)
{
sdnv_read_generic(state, value, byte, sdnv_validate_byte_u32);
}
void sdnv_read_u64(struct sdnv_state *state, uint64_t *value, uint8_t byte)
{
sdnv_read_generic(state, value, byte, sdnv_validate_byte_u64);
}
int_fast8_t sdnv_get_size_u8(uint8_t value)
{
if ((value & 0x80) == 0)
return 1;
else
return 2;
}
int_fast8_t sdnv_get_size_u16(uint16_t value)
{
if ((value & 0xFF80) == 0)
return 1;
else if ((value & 0xC000) == 0)
return 2;
else
return 3;
}
int_fast8_t sdnv_get_size_u32(uint32_t value)
{
if ((value & 0xFFFFFF80) == 0)
return 1;
else if ((value & 0xFFFFC000) == 0)
return 2;
else if ((value & 0xFFE00000) == 0)
return 3;
else if ((value & 0xF0000000) == 0)
return 4;
else
return 5;
}
int_fast8_t sdnv_get_size_u64(uint64_t value)
{
if ((value & 0xFFFFFFFFFFFFFF80) == 0)
return 1;
else if ((value & 0xFFFFFFFFFFFFC000) == 0)
return 2;
else if ((value & 0xFFFFFFFFFFE00000) == 0)
return 3;
else if ((value & 0xFFFFFFFFF0000000) == 0)
return 4;
else if ((value & 0xFFFFFFF800000000) == 0)
return 5;
else if ((value & 0xFFFFFC0000000000) == 0)
return 6;
else if ((value & 0xFFFE000000000000) == 0)
return 7;
else if ((value & 0xFF00000000000000) == 0)
return 8;
else if ((value & 0x8000000000000000) == 0)
return 9;
else
return 10;
}
#define sdnv_write_generic(sdnv_bytes, buffer, value) \
do { \
__typeof__(sdnv_bytes) _sdnv_bytes = (sdnv_bytes); \
__typeof__(buffer) _buffer = (buffer); \
__typeof__(value) _value = (value); \
uint8_t *_last_byte = _buffer + _sdnv_bytes - 1; \
for (uint8_t *_b = _last_byte; _b >= _buffer; _b--) { \
(*_b) = (_value & SDNV_VALUE_MASK) | SDNV_MARKER_MASK; \
_value >>= 7; \
} \
(*_last_byte) &= SDNV_VALUE_MASK; \
} while (0)
int_fast8_t sdnv_write_u8(uint8_t *buffer, uint8_t value)
{
int_fast8_t sdnv_bytes = sdnv_get_size_u8(value);
sdnv_write_generic(sdnv_bytes, buffer, value);
return sdnv_bytes;
}
int_fast8_t sdnv_write_u16(uint8_t *buffer, uint16_t value)
{
int_fast8_t sdnv_bytes = sdnv_get_size_u16(value);
sdnv_write_generic(sdnv_bytes, buffer, value);
return sdnv_bytes;
}
int_fast8_t sdnv_write_u32(uint8_t *buffer, uint32_t value)
{
int_fast8_t sdnv_bytes = sdnv_get_size_u32(value);
sdnv_write_generic(sdnv_bytes, buffer, value);
return sdnv_bytes;
}
int_fast8_t sdnv_write_u64(uint8_t *buffer, uint64_t value)
{
int_fast8_t sdnv_bytes = sdnv_get_size_u64(value);
sdnv_write_generic(sdnv_bytes, buffer, value);
return sdnv_bytes;
}