mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-08 23:08:10 -07:00
NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
@@ -31,6 +31,8 @@ env.Append(
|
||||
File("protocols/protocol_dict.h"),
|
||||
File("pretty_format.h"),
|
||||
File("hex.h"),
|
||||
File("simple_array.h"),
|
||||
File("bit_buffer.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "bit_buffer.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define BITS_IN_BYTE (8)
|
||||
|
||||
struct BitBuffer {
|
||||
uint8_t* data;
|
||||
uint8_t* parity;
|
||||
size_t capacity_bytes;
|
||||
size_t size_bits;
|
||||
};
|
||||
|
||||
BitBuffer* bit_buffer_alloc(size_t capacity_bytes) {
|
||||
furi_assert(capacity_bytes);
|
||||
|
||||
BitBuffer* buf = malloc(sizeof(BitBuffer));
|
||||
|
||||
buf->data = malloc(capacity_bytes);
|
||||
size_t parity_buf_size = (capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
|
||||
buf->parity = malloc(parity_buf_size);
|
||||
buf->capacity_bytes = capacity_bytes;
|
||||
buf->size_bits = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void bit_buffer_free(BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
free(buf->data);
|
||||
free(buf->parity);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void bit_buffer_reset(BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
memset(buf->data, 0, buf->capacity_bytes);
|
||||
size_t parity_buf_size = (buf->capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
|
||||
memset(buf->parity, 0, parity_buf_size);
|
||||
buf->size_bits = 0;
|
||||
}
|
||||
|
||||
void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other) {
|
||||
furi_assert(buf);
|
||||
furi_assert(other);
|
||||
|
||||
if(buf == other) return;
|
||||
|
||||
furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= other->size_bits);
|
||||
|
||||
memcpy(buf->data, other->data, bit_buffer_get_size_bytes(other));
|
||||
buf->size_bits = other->size_bits;
|
||||
}
|
||||
|
||||
void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {
|
||||
furi_assert(buf);
|
||||
furi_assert(other);
|
||||
furi_assert(bit_buffer_get_size_bytes(other) > start_index);
|
||||
furi_assert(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index);
|
||||
|
||||
memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index);
|
||||
buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index) {
|
||||
furi_assert(buf);
|
||||
furi_assert(other);
|
||||
furi_assert(bit_buffer_get_capacity_bytes(buf) >= end_index);
|
||||
furi_assert(bit_buffer_get_size_bytes(other) >= end_index);
|
||||
|
||||
memcpy(buf->data, other->data, end_index);
|
||||
buf->size_bits = end_index * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {
|
||||
furi_assert(buf);
|
||||
furi_assert(data);
|
||||
furi_assert(buf->capacity_bytes >= size_bytes);
|
||||
|
||||
memcpy(buf->data, data, size_bytes);
|
||||
buf->size_bits = size_bytes * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits) {
|
||||
furi_assert(buf);
|
||||
furi_assert(data);
|
||||
furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= size_bits);
|
||||
|
||||
size_t size_bytes = (size_bits + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
|
||||
memcpy(buf->data, data, size_bytes);
|
||||
buf->size_bits = size_bits;
|
||||
}
|
||||
|
||||
void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits) {
|
||||
furi_assert(buf);
|
||||
furi_assert(data);
|
||||
|
||||
size_t bits_processed = 0;
|
||||
size_t curr_byte = 0;
|
||||
|
||||
if(size_bits < BITS_IN_BYTE + 1) {
|
||||
buf->size_bits = size_bits;
|
||||
buf->data[0] = data[0];
|
||||
} else {
|
||||
furi_assert(size_bits % (BITS_IN_BYTE + 1) == 0);
|
||||
while(bits_processed < size_bits) {
|
||||
buf->data[curr_byte] = data[bits_processed / BITS_IN_BYTE] >>
|
||||
(bits_processed % BITS_IN_BYTE);
|
||||
buf->data[curr_byte] |= data[bits_processed / BITS_IN_BYTE + 1]
|
||||
<< (BITS_IN_BYTE - bits_processed % BITS_IN_BYTE);
|
||||
uint8_t bit =
|
||||
FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], bits_processed % BITS_IN_BYTE);
|
||||
|
||||
if(bits_processed % BITS_IN_BYTE) {
|
||||
buf->parity[curr_byte / BITS_IN_BYTE] = bit;
|
||||
} else {
|
||||
buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bits_processed % BITS_IN_BYTE);
|
||||
}
|
||||
bits_processed += BITS_IN_BYTE + 1;
|
||||
curr_byte++;
|
||||
}
|
||||
buf->size_bits = curr_byte * BITS_IN_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes) {
|
||||
furi_assert(buf);
|
||||
furi_assert(dest);
|
||||
furi_assert(bit_buffer_get_size_bytes(buf) <= size_bytes);
|
||||
|
||||
memcpy(dest, buf->data, bit_buffer_get_size_bytes(buf));
|
||||
}
|
||||
|
||||
void bit_buffer_write_bytes_with_parity(
|
||||
const BitBuffer* buf,
|
||||
void* dest,
|
||||
size_t size_bytes,
|
||||
size_t* bits_written) {
|
||||
furi_assert(buf);
|
||||
furi_assert(dest);
|
||||
furi_assert(bits_written);
|
||||
|
||||
size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);
|
||||
size_t buf_size_with_parity_bytes =
|
||||
(buf_size_bytes * (BITS_IN_BYTE + 1) + BITS_IN_BYTE) / BITS_IN_BYTE;
|
||||
furi_assert(buf_size_with_parity_bytes <= size_bytes);
|
||||
|
||||
uint8_t next_par_bit = 0;
|
||||
uint16_t curr_bit_pos = 0;
|
||||
uint8_t* bitstream = dest;
|
||||
|
||||
for(size_t i = 0; i < buf_size_bytes; i++) {
|
||||
next_par_bit = FURI_BIT(buf->parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE);
|
||||
if(curr_bit_pos % BITS_IN_BYTE == 0) {
|
||||
bitstream[curr_bit_pos / BITS_IN_BYTE] = buf->data[i];
|
||||
curr_bit_pos += BITS_IN_BYTE;
|
||||
bitstream[curr_bit_pos / BITS_IN_BYTE] = next_par_bit;
|
||||
curr_bit_pos++;
|
||||
} else {
|
||||
bitstream[curr_bit_pos / BITS_IN_BYTE] |= buf->data[i]
|
||||
<< (curr_bit_pos % BITS_IN_BYTE);
|
||||
bitstream[curr_bit_pos / BITS_IN_BYTE + 1] =
|
||||
buf->data[i] >> (BITS_IN_BYTE - curr_bit_pos % BITS_IN_BYTE);
|
||||
bitstream[curr_bit_pos / BITS_IN_BYTE + 1] |= next_par_bit
|
||||
<< (curr_bit_pos % BITS_IN_BYTE);
|
||||
curr_bit_pos += BITS_IN_BYTE + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*bits_written = curr_bit_pos;
|
||||
}
|
||||
|
||||
void bit_buffer_write_bytes_mid(
|
||||
const BitBuffer* buf,
|
||||
void* dest,
|
||||
size_t start_index,
|
||||
size_t size_bytes) {
|
||||
furi_assert(buf);
|
||||
furi_assert(dest);
|
||||
furi_assert(start_index + size_bytes <= bit_buffer_get_size_bytes(buf));
|
||||
|
||||
memcpy(dest, buf->data + start_index, size_bytes);
|
||||
}
|
||||
|
||||
bool bit_buffer_has_partial_byte(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return (buf->size_bits % BITS_IN_BYTE) != 0;
|
||||
}
|
||||
|
||||
bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte) {
|
||||
furi_assert(buf);
|
||||
|
||||
return bit_buffer_get_size_bytes(buf) && (buf->data[0] == byte);
|
||||
}
|
||||
|
||||
size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return buf->capacity_bytes;
|
||||
}
|
||||
|
||||
size_t bit_buffer_get_size(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return buf->size_bits;
|
||||
}
|
||||
|
||||
size_t bit_buffer_get_size_bytes(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return (buf->size_bits / BITS_IN_BYTE) + (buf->size_bits % BITS_IN_BYTE ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index) {
|
||||
furi_assert(buf);
|
||||
furi_assert(buf->capacity_bytes > index);
|
||||
|
||||
return buf->data[index];
|
||||
}
|
||||
|
||||
uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits) {
|
||||
furi_assert(buf);
|
||||
furi_assert(buf->capacity_bytes * BITS_IN_BYTE > index_bits);
|
||||
|
||||
const size_t byte_index = index_bits / BITS_IN_BYTE;
|
||||
const size_t bit_offset = index_bits % BITS_IN_BYTE;
|
||||
|
||||
const uint8_t lo = buf->data[byte_index] >> bit_offset;
|
||||
const uint8_t hi = buf->data[byte_index + 1] << (BITS_IN_BYTE - bit_offset);
|
||||
|
||||
return lo | hi;
|
||||
}
|
||||
|
||||
const uint8_t* bit_buffer_get_data(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
const uint8_t* bit_buffer_get_parity(const BitBuffer* buf) {
|
||||
furi_assert(buf);
|
||||
|
||||
return buf->parity;
|
||||
}
|
||||
|
||||
void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte) {
|
||||
furi_assert(buf);
|
||||
|
||||
const size_t size_bytes = bit_buffer_get_size_bytes(buf);
|
||||
furi_assert(size_bytes > index);
|
||||
|
||||
buf->data[index] = byte;
|
||||
}
|
||||
|
||||
void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity) {
|
||||
furi_assert(buff);
|
||||
furi_assert(buff->size_bits / BITS_IN_BYTE > index);
|
||||
|
||||
buff->data[index] = byte;
|
||||
if((index % BITS_IN_BYTE) == 0) {
|
||||
buff->parity[index / BITS_IN_BYTE] = parity;
|
||||
} else {
|
||||
buff->parity[index / BITS_IN_BYTE] |= parity << (index % BITS_IN_BYTE);
|
||||
}
|
||||
}
|
||||
|
||||
void bit_buffer_set_size(BitBuffer* buf, size_t new_size) {
|
||||
furi_assert(buf);
|
||||
furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= new_size);
|
||||
|
||||
buf->size_bits = new_size;
|
||||
}
|
||||
|
||||
void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes) {
|
||||
furi_assert(buf);
|
||||
furi_assert(buf->capacity_bytes >= new_size_bytes);
|
||||
|
||||
buf->size_bits = new_size_bytes * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_append(BitBuffer* buf, const BitBuffer* other) {
|
||||
bit_buffer_append_right(buf, other, 0);
|
||||
}
|
||||
|
||||
void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {
|
||||
furi_assert(buf);
|
||||
furi_assert(other);
|
||||
|
||||
const size_t size_bytes = bit_buffer_get_size_bytes(buf);
|
||||
const size_t other_size_bytes = bit_buffer_get_size_bytes(other) - start_index;
|
||||
|
||||
furi_assert(buf->capacity_bytes >= size_bytes + other_size_bytes);
|
||||
|
||||
memcpy(buf->data + size_bytes, other->data + start_index, other_size_bytes);
|
||||
buf->size_bits += other->size_bits - start_index * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte) {
|
||||
furi_assert(buf);
|
||||
|
||||
const size_t data_size_bytes = bit_buffer_get_size_bytes(buf);
|
||||
const size_t new_data_size_bytes = data_size_bytes + 1;
|
||||
furi_assert(new_data_size_bytes <= buf->capacity_bytes);
|
||||
|
||||
buf->data[data_size_bytes] = byte;
|
||||
buf->size_bits = new_data_size_bytes * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {
|
||||
furi_assert(buf);
|
||||
furi_assert(data);
|
||||
|
||||
const size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);
|
||||
furi_assert(buf->capacity_bytes >= buf_size_bytes + size_bytes);
|
||||
|
||||
memcpy(&buf->data[buf_size_bytes], data, size_bytes);
|
||||
buf->size_bits += size_bytes * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
void bit_buffer_append_bit(BitBuffer* buf, bool bit) {
|
||||
furi_assert(buf);
|
||||
furi_assert(
|
||||
bit_buffer_get_size_bytes(buf) <=
|
||||
(buf->capacity_bytes - (bit_buffer_has_partial_byte(buf) ? 0 : 1)));
|
||||
|
||||
if(bit) {
|
||||
const size_t byte_index = buf->size_bits / BITS_IN_BYTE;
|
||||
const size_t bit_offset = (buf->size_bits % BITS_IN_BYTE);
|
||||
buf->data[byte_index] |= 1U << bit_offset;
|
||||
}
|
||||
|
||||
buf->size_bits++;
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct BitBuffer BitBuffer;
|
||||
|
||||
// Construction, deletion, reset
|
||||
|
||||
/**
|
||||
* Allocate a BitBuffer instance.
|
||||
*
|
||||
* @param [in] capacity_bytes maximum buffer capacity, in bytes
|
||||
* @return pointer to the allocated BitBuffer instance
|
||||
*/
|
||||
BitBuffer* bit_buffer_alloc(size_t capacity_bytes);
|
||||
|
||||
/**
|
||||
* Delete a BitBuffer instance.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance
|
||||
*/
|
||||
void bit_buffer_free(BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Clear all data from a BitBuffer instance.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance
|
||||
*/
|
||||
void bit_buffer_reset(BitBuffer* buf);
|
||||
|
||||
// Copy and write
|
||||
|
||||
/**
|
||||
* Copy another BitBuffer instance's contents to this one, replacing
|
||||
* all of the original data.
|
||||
* The destination capacity must be no less than the source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] other pointer to a BitBuffer instance to copy from
|
||||
* @note
|
||||
*/
|
||||
void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other);
|
||||
|
||||
/**
|
||||
* Copy all BitBuffer instance's contents to this one, starting from start_index,
|
||||
* replacing all of the original data.
|
||||
* The destination capacity must be no less than the source data size
|
||||
* counting from start_index.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] other pointer to a BitBuffer instance to copy from
|
||||
* @param [in] start_index index to begin copying source data from
|
||||
*/
|
||||
void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index);
|
||||
|
||||
/**
|
||||
* Copy all BitBuffer instance's contents to this one, ending with end_index,
|
||||
* replacing all of the original data.
|
||||
* The destination capacity must be no less than the source data size
|
||||
* counting to end_index.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] other pointer to a BitBuffer instance to copy from
|
||||
* @param [in] end_index index to end copying source data at
|
||||
*/
|
||||
void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index);
|
||||
|
||||
/**
|
||||
* Copy a byte array to a BitBuffer instance, replacing all of the original data.
|
||||
* The destination capacity must be no less than the source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] data pointer to the byte array to be copied
|
||||
* @param [in] size_bytes size of the data to be copied, in bytes
|
||||
*/
|
||||
void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes);
|
||||
|
||||
/**
|
||||
* Copy a byte array to a BitBuffer instance, replacing all of the original data.
|
||||
* The destination capacity must be no less than the source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] data pointer to the byte array to be copied
|
||||
* @param [in] size_bits size of the data to be copied, in bits
|
||||
*/
|
||||
void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits);
|
||||
|
||||
/**
|
||||
* Copy a byte with parity array to a BitBuffer instance, replacing all of the original data.
|
||||
* The destination capacity must be no less than the source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to copy into
|
||||
* @param [in] data pointer to the byte array to be copied
|
||||
* @param [in] size_bitss size of the data to be copied, in bits
|
||||
*/
|
||||
void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits);
|
||||
|
||||
/**
|
||||
* Write a BitBuffer instance's entire contents to an arbitrary memory location.
|
||||
* The destination memory must be allocated. Additionally, the destination
|
||||
* capacity must be no less than the source data size.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to write from
|
||||
* @param [out] dest pointer to the destination memory location
|
||||
* @param [in] size_bytes maximum destination data size, in bytes
|
||||
*/
|
||||
void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes);
|
||||
|
||||
/**
|
||||
* Write a BitBuffer instance's entire contents to an arbitrary memory location.
|
||||
* Additionally, place a parity bit after each byte.
|
||||
* The destination memory must be allocated. Additionally, the destination
|
||||
* capacity must be no less than the source data size plus parity.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to write from
|
||||
* @param [out] dest pointer to the destination memory location
|
||||
* @param [in] size_bytes maximum destination data size, in bytes
|
||||
* @param [out] bits_written actual number of bits writen, in bits
|
||||
*/
|
||||
void bit_buffer_write_bytes_with_parity(
|
||||
const BitBuffer* buf,
|
||||
void* dest,
|
||||
size_t size_bytes,
|
||||
size_t* bits_written);
|
||||
|
||||
/**
|
||||
* Write a slice of BitBuffer instance's contents to an arbitrary memory location.
|
||||
* The destination memory must be allocated. Additionally, the destination
|
||||
* capacity must be no less than the requested slice size.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to write from
|
||||
* @param [out] dest pointer to the destination memory location
|
||||
* @param [in] start_index index to begin copying source data from
|
||||
* @param [in] size_bytes data slice size, in bytes
|
||||
*/
|
||||
void bit_buffer_write_bytes_mid(
|
||||
const BitBuffer* buf,
|
||||
void* dest,
|
||||
size_t start_index,
|
||||
size_t size_bytes);
|
||||
|
||||
// Checks
|
||||
|
||||
/**
|
||||
* Check whether a BitBuffer instance contains a partial byte (i.e. the bit count
|
||||
* is not divisible by 8).
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be checked
|
||||
* @return true if the instance contains a partial byte, false otherwise
|
||||
*/
|
||||
bool bit_buffer_has_partial_byte(const BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Check whether a BitBuffer instance's contents start with the designated byte.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be checked
|
||||
* @param [in] byte byte value to be checked against
|
||||
* @return true if data starts with designated byte, false otherwise
|
||||
*/
|
||||
bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte);
|
||||
|
||||
// Getters
|
||||
|
||||
/**
|
||||
* Get a BitBuffer instance's capacity (i.e. the maximum possible amount of data), in bytes.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @return capacity, in bytes
|
||||
*/
|
||||
size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Get a BitBuffer instance's data size (i.e. the amount of stored data), in bits.
|
||||
* Might be not divisible by 8 (see bit_buffer_is_partial_byte).
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @return data size, in bits.
|
||||
*/
|
||||
size_t bit_buffer_get_size(const BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Get a BitBuffer instance's data size (i.e. the amount of stored data), in bytes.
|
||||
* If a partial byte is present, it is also counted.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @return data size, in bytes.
|
||||
*/
|
||||
size_t bit_buffer_get_size_bytes(const BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Get a byte value at a specified index in a BitBuffer instance.
|
||||
* The index must be valid (i.e. less than the instance's data size in bytes).
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @param [in] index index of the byte in question
|
||||
*/
|
||||
uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index);
|
||||
|
||||
/**
|
||||
* Get a byte value starting from the specified bit index in a BitBuffer instance.
|
||||
* The resulting byte might correspond to a single byte (if the index is a multiple
|
||||
* of 8), or two overlapping bytes combined.
|
||||
* The index must be valid (i.e. less than the instance's data size in bits).
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @param [in] index bit index of the byte in question
|
||||
*/
|
||||
uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits);
|
||||
|
||||
/**
|
||||
* Get the pointer to a BitBuffer instance's underlying data.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @return pointer to the underlying data
|
||||
*/
|
||||
const uint8_t* bit_buffer_get_data(const BitBuffer* buf);
|
||||
|
||||
/**
|
||||
* Get the pointer to a BitBuffer instance's underlying data.
|
||||
*
|
||||
* @param [in] buf pointer to a BitBuffer instance to be queried
|
||||
* @return pointer to the underlying data
|
||||
*/
|
||||
const uint8_t* bit_buffer_get_parity(const BitBuffer* buf);
|
||||
|
||||
// Setters
|
||||
|
||||
/**
|
||||
* Set byte value at a specified index in a BitBuffer instance.
|
||||
* The index must be valid (i.e. less than the instance's data size in bytes).
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be modified
|
||||
* @param [in] index index of the byte in question
|
||||
* @param [in] byte byte value to be set at index
|
||||
*/
|
||||
void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte);
|
||||
|
||||
/**
|
||||
* Set byte and parity bit value at a specified index in a BitBuffer instance.
|
||||
* The index must be valid (i.e. less than the instance's data size in bytes).
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be modified
|
||||
* @param [in] index index of the byte in question
|
||||
* @param [in] byte byte value to be set at index
|
||||
* @param [in] parity parity bit value to be set at index
|
||||
*/
|
||||
void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity);
|
||||
|
||||
/**
|
||||
* Resize a BitBuffer instance to a new size, in bits.
|
||||
* @warning May cause bugs. Use only if absolutely necessary.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be resized
|
||||
* @param [in] new_size the new size of the buffer, in bits
|
||||
*/
|
||||
void bit_buffer_set_size(BitBuffer* buf, size_t new_size);
|
||||
|
||||
/**
|
||||
* Resize a BitBuffer instance to a new size, in bytes.
|
||||
* @warning May cause bugs. Use only if absolutely necessary.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be resized
|
||||
* @param [in] new_size_bytes the new size of the buffer, in bytes
|
||||
*/
|
||||
void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes);
|
||||
|
||||
// Modification
|
||||
|
||||
/**
|
||||
* Append all BitBuffer's instance contents to this one. The destination capacity
|
||||
* must be no less than its original data size plus source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be appended to
|
||||
* @param [in] other pointer to a BitBuffer instance to be appended
|
||||
*/
|
||||
void bit_buffer_append(BitBuffer* buf, const BitBuffer* other);
|
||||
|
||||
/**
|
||||
* Append a BitBuffer's instance contents to this one, starting from start_index.
|
||||
* The destination capacity must be no less than the source data size
|
||||
* counting from start_index.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be appended to
|
||||
* @param [in] other pointer to a BitBuffer instance to be appended
|
||||
* @param [in] start_index index to begin copying source data from
|
||||
*/
|
||||
void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index);
|
||||
|
||||
/**
|
||||
* Append a byte to a BitBuffer instance.
|
||||
* The destination capacity must be no less its original data size plus one.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be appended to
|
||||
* @param [in] byte byte value to be appended
|
||||
*/
|
||||
void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte);
|
||||
|
||||
/**
|
||||
* Append a byte array to a BitBuffer instance.
|
||||
* The destination capacity must be no less its original data size plus source data size.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be appended to
|
||||
* @param [in] data pointer to the byte array to be appended
|
||||
* @param [in] size_bytes size of the data to be appended, in bytes
|
||||
*/
|
||||
void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes);
|
||||
|
||||
/**
|
||||
* Append a bit to a BitBuffer instance.
|
||||
* The destination capacity must be sufficient to accomodate the additional bit.
|
||||
*
|
||||
* @param [in,out] buf pointer to a BitBuffer instance to be appended to
|
||||
* @param [in] bit bit value to be appended
|
||||
*/
|
||||
void bit_buffer_append_bit(BitBuffer* buf, bool bit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "simple_array.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
struct SimpleArray {
|
||||
const SimpleArrayConfig* config;
|
||||
SimpleArrayElement* data;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
SimpleArray* simple_array_alloc(const SimpleArrayConfig* config) {
|
||||
SimpleArray* instance = malloc(sizeof(SimpleArray));
|
||||
instance->config = config;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void simple_array_free(SimpleArray* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
simple_array_reset(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void simple_array_init(SimpleArray* instance, uint32_t count) {
|
||||
furi_assert(instance);
|
||||
furi_assert(count > 0);
|
||||
|
||||
simple_array_reset(instance);
|
||||
|
||||
instance->data = malloc(count * instance->config->type_size);
|
||||
instance->count = count;
|
||||
|
||||
SimpleArrayInit init = instance->config->init;
|
||||
if(init) {
|
||||
for(uint32_t i = 0; i < instance->count; ++i) {
|
||||
init(simple_array_get(instance, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simple_array_reset(SimpleArray* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->data) {
|
||||
SimpleArrayReset reset = instance->config->reset;
|
||||
|
||||
if(reset) {
|
||||
for(uint32_t i = 0; i < instance->count; ++i) {
|
||||
reset(simple_array_get(instance, i));
|
||||
}
|
||||
}
|
||||
|
||||
free(instance->data);
|
||||
|
||||
instance->count = 0;
|
||||
instance->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void simple_array_copy(SimpleArray* instance, const SimpleArray* other) {
|
||||
furi_assert(instance);
|
||||
furi_assert(other);
|
||||
furi_assert(instance->config == other->config);
|
||||
|
||||
simple_array_reset(instance);
|
||||
|
||||
if(other->count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
simple_array_init(instance, other->count);
|
||||
|
||||
SimpleArrayCopy copy = instance->config->copy;
|
||||
if(copy) {
|
||||
for(uint32_t i = 0; i < other->count; ++i) {
|
||||
copy(simple_array_get(instance, i), simple_array_cget(other, i));
|
||||
}
|
||||
} else {
|
||||
memcpy(instance->data, other->data, other->count * instance->config->type_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other) {
|
||||
furi_assert(instance);
|
||||
furi_assert(other);
|
||||
|
||||
// Equal if the same object
|
||||
if(instance == other) return true;
|
||||
|
||||
return (instance->config == other->config) && (instance->count == other->count) &&
|
||||
((instance->data == other->data) || (instance->data == NULL) || (other->data == NULL) ||
|
||||
(memcmp(instance->data, other->data, other->count) == 0));
|
||||
}
|
||||
|
||||
uint32_t simple_array_get_count(const SimpleArray* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->count;
|
||||
}
|
||||
|
||||
SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index) {
|
||||
furi_assert(instance);
|
||||
furi_assert(index < instance->count);
|
||||
|
||||
return instance->data + index * instance->config->type_size;
|
||||
}
|
||||
|
||||
const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index) {
|
||||
return simple_array_get((SimpleArrayElement*)instance, index);
|
||||
}
|
||||
|
||||
SimpleArrayData* simple_array_get_data(SimpleArray* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->data);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance) {
|
||||
return simple_array_get_data((SimpleArray*)instance);
|
||||
}
|
||||
|
||||
const SimpleArrayConfig simple_array_config_uint8_t = {
|
||||
.init = NULL,
|
||||
.copy = NULL,
|
||||
.reset = NULL,
|
||||
.type_size = sizeof(uint8_t),
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* @file simple_array.h
|
||||
*
|
||||
* @brief This file provides a simple (non-type safe) array for elements with
|
||||
* (optional) in-place construction support.
|
||||
*
|
||||
* No append, remove or take operations are supported.
|
||||
* Instead, the user must call simple_array_init() in order to reserve the memory for n elements.
|
||||
* In case if init() is specified in the configuration, then the elements are constructed in-place.
|
||||
*
|
||||
* To clear all elements from the array, call simple_array_reset(), which will also call reset()
|
||||
* for each element in case if it was specified in the configuration. This is useful if a custom
|
||||
* destructor is required for a particular type. Calling simple_array_free() will also result in a
|
||||
* simple_array_reset() call automatically.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SimpleArray SimpleArray;
|
||||
|
||||
typedef void SimpleArrayData;
|
||||
typedef void SimpleArrayElement;
|
||||
|
||||
typedef void (*SimpleArrayInit)(SimpleArrayElement* elem);
|
||||
typedef void (*SimpleArrayReset)(SimpleArrayElement* elem);
|
||||
typedef void (*SimpleArrayCopy)(SimpleArrayElement* elem, const SimpleArrayElement* other);
|
||||
|
||||
/** Simple Array configuration structure. Defined per type. */
|
||||
typedef struct {
|
||||
SimpleArrayInit init; /**< Initialisation (in-place constructor) method. */
|
||||
SimpleArrayReset reset; /**< Reset (custom destructor) method. */
|
||||
SimpleArrayCopy copy; /**< Copy (custom copy-constructor) method. */
|
||||
const size_t type_size; /** Type size, in bytes. */
|
||||
} SimpleArrayConfig;
|
||||
|
||||
/**
|
||||
* Allocate a SimpleArray instance with the given configuration.
|
||||
*
|
||||
* @param [in] config Pointer to the type-specific configuration
|
||||
* @return Pointer to the allocated SimpleArray instance
|
||||
*/
|
||||
SimpleArray* simple_array_alloc(const SimpleArrayConfig* config);
|
||||
|
||||
/**
|
||||
* Free a SimpleArray instance and release its contents.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to be freed
|
||||
*/
|
||||
void simple_array_free(SimpleArray* instance);
|
||||
|
||||
/**
|
||||
* Initialise a SimpleArray instance by allocating additional space to contain
|
||||
* the requested number of elements.
|
||||
* If init() is specified in the config, then it is called for each element,
|
||||
* otherwise the data is filled with zeroes.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to be init'd
|
||||
* @param [in] count Number of elements to be allocated and init'd
|
||||
*/
|
||||
void simple_array_init(SimpleArray* instance, uint32_t count);
|
||||
|
||||
/**
|
||||
* Reset a SimpleArray instance and delete all of its elements.
|
||||
* If reset() is specified in the config, then it is called for each element,
|
||||
* otherwise the data is simply free()'d.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to be reset
|
||||
*/
|
||||
void simple_array_reset(SimpleArray* instance);
|
||||
|
||||
/**
|
||||
* Copy (duplicate) another SimpleArray instance to this one.
|
||||
* If copy() is specified in the config, then it is called for each element,
|
||||
* otherwise the data is simply memcpy()'d.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to copy to
|
||||
* @param [in] other Pointer to the SimpleArray instance to copy from
|
||||
*/
|
||||
void simple_array_copy(SimpleArray* instance, const SimpleArray* other);
|
||||
|
||||
/**
|
||||
* Check if another SimpleArray instance is equal (the same object or holds the
|
||||
* same data) to this one.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to be compared
|
||||
* @param [in] other Pointer to the SimpleArray instance to be compared
|
||||
* @return True if instances are considered equal, false otherwise
|
||||
*/
|
||||
bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other);
|
||||
|
||||
/**
|
||||
* Get the count of elements currently contained in a SimpleArray instance.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to query the count from
|
||||
* @return Count of elements contained in the instance
|
||||
*/
|
||||
uint32_t simple_array_get_count(const SimpleArray* instance);
|
||||
|
||||
/**
|
||||
* Get a pointer to an element contained in a SimpleArray instance.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to get an element from
|
||||
* @param [in] index Index of the element in question. MUST be less than total element count
|
||||
* @return Pointer to the element specified by index
|
||||
*/
|
||||
SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index);
|
||||
|
||||
/**
|
||||
* Get a const pointer to an element contained in a SimpleArray instance.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to get an element from
|
||||
* @param [in] index Index of the element in question. MUST be less than total element count
|
||||
* @return Const pointer to the element specified by index
|
||||
*/
|
||||
const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index);
|
||||
|
||||
/**
|
||||
* Get a pointer to the internal data of a SimpleArray instance.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to get the data of
|
||||
* @return Pointer to the instance's internal data
|
||||
*/
|
||||
SimpleArrayData* simple_array_get_data(SimpleArray* instance);
|
||||
|
||||
/**
|
||||
* Get a constant pointer to the internal data of a SimpleArray instance.
|
||||
*
|
||||
* @param [in] instance Pointer to the SimpleArray instance to get the data of
|
||||
* @return Constant pointer to the instance's internal data
|
||||
*/
|
||||
const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance);
|
||||
|
||||
// Standard preset configurations
|
||||
|
||||
// Preset configuration for a byte(uint8_t) array.
|
||||
extern const SimpleArrayConfig simple_array_config_uint8_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user