mirror of
https://gitlab.com/d3tn/ud3tn.git
synced 2026-08-13 12:33:27 +02:00
- Only access each argument once - Use parentheses around macro arguments upon use Signed-off-by: Felix Walter <felix.walter@d3tn.com>
204 lines
5.1 KiB
C
204 lines
5.1 KiB
C
// 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;
|
||
}
|