mirror of
https://github.com/google/pebble.git
synced 2025-05-19 09:54:55 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
31
src/libutil/build_id.c
Normal file
31
src/libutil/build_id.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/build_id.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
bool build_id_contains_gnu_build_id(const ElfExternalNote *note) {
|
||||
const uint32_t NT_GNU_BUILD_ID = 3;
|
||||
const char *expected_name = "GNU";
|
||||
const uint32_t expected_name_length = strlen(expected_name) + 1;
|
||||
return note->type == NT_GNU_BUILD_ID &&
|
||||
note->name_length == expected_name_length &&
|
||||
note->data_length == BUILD_ID_EXPECTED_LEN &&
|
||||
(strncmp((const char *) note->data, expected_name, expected_name_length) == 0);
|
||||
}
|
186
src/libutil/circular_buffer.c
Normal file
186
src/libutil/circular_buffer.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static uint16_t get_write_length_available(CircularBuffer* buffer) {
|
||||
return buffer->buffer_size - buffer->data_length;
|
||||
}
|
||||
|
||||
static uint16_t get_write_index(CircularBuffer* buffer) {
|
||||
return (buffer->read_index + buffer->data_length) % buffer->buffer_size;
|
||||
}
|
||||
|
||||
void circular_buffer_init(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size) {
|
||||
buffer->buffer = storage;
|
||||
buffer->buffer_size = storage_size;
|
||||
buffer->read_index = 0;
|
||||
buffer->data_length = 0;
|
||||
buffer->write_in_progress = false;
|
||||
buffer->auto_reset = true;
|
||||
}
|
||||
|
||||
void circular_buffer_init_ex(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size,
|
||||
bool auto_reset) {
|
||||
circular_buffer_init(buffer, storage, storage_size);
|
||||
buffer->auto_reset = auto_reset;
|
||||
}
|
||||
|
||||
bool circular_buffer_write(CircularBuffer* buffer, const void* data, uint16_t length) {
|
||||
if (get_write_length_available(buffer) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we know the message will fit, so no more checking against buffer_read_index required.
|
||||
|
||||
uint16_t write_index = get_write_index(buffer);
|
||||
|
||||
// Update the data_length member now beforce we muck with the length parameter
|
||||
buffer->data_length += length;
|
||||
|
||||
const uint16_t remaining_length = buffer->buffer_size - write_index;
|
||||
if (remaining_length < length) {
|
||||
// Need to write the message in two chunks around the end of the buffer. Write the first chunk
|
||||
memcpy(&buffer->buffer[write_index], data, remaining_length);
|
||||
|
||||
write_index = 0;
|
||||
data = ((uint8_t*) data) + remaining_length;
|
||||
length -= remaining_length;
|
||||
}
|
||||
|
||||
// Write the last chunk
|
||||
memcpy(&buffer->buffer[write_index], data, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t circular_buffer_write_prepare(CircularBuffer *buffer, uint8_t **data_out) {
|
||||
if (!get_write_length_available(buffer) || buffer->write_in_progress) {
|
||||
*data_out = NULL;
|
||||
return 0;
|
||||
}
|
||||
buffer->write_in_progress = true;
|
||||
|
||||
const uint16_t write_index = get_write_index(buffer);
|
||||
*data_out = buffer->buffer + write_index;
|
||||
if (buffer->read_index > write_index) {
|
||||
return buffer->read_index - write_index;
|
||||
}
|
||||
return buffer->buffer_size - write_index;
|
||||
}
|
||||
|
||||
void circular_buffer_write_finish(CircularBuffer *buffer, uint16_t written_length) {
|
||||
UTIL_ASSERT(buffer->data_length + written_length <= buffer->buffer_size);
|
||||
buffer->data_length += written_length;
|
||||
buffer->write_in_progress = false;
|
||||
}
|
||||
|
||||
bool circular_buffer_read(const CircularBuffer* buffer, uint16_t length, const uint8_t** data_out, uint16_t* length_out) {
|
||||
if (buffer->data_length < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*data_out = &buffer->buffer[buffer->read_index];
|
||||
|
||||
const uint16_t remaining_length = buffer->buffer_size - buffer->read_index;
|
||||
*length_out = MIN(remaining_length, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t circular_buffer_copy(const CircularBuffer* buffer,
|
||||
void *data_out,
|
||||
uint16_t length_to_copy) {
|
||||
return circular_buffer_copy_offset(buffer, 0, data_out, length_to_copy);
|
||||
}
|
||||
|
||||
uint16_t circular_buffer_copy_offset(const CircularBuffer* buffer, uint16_t start_offset,
|
||||
uint8_t *data_out, uint16_t length_to_copy) {
|
||||
if (buffer->data_length <= start_offset) {
|
||||
return 0;
|
||||
}
|
||||
const uint16_t read_index = (buffer->read_index + start_offset) % buffer->buffer_size;
|
||||
const uint16_t data_length = buffer->data_length - start_offset;
|
||||
|
||||
length_to_copy = MIN(length_to_copy, data_length);
|
||||
|
||||
// Number of total bytes after the read index to end of buffer:
|
||||
const uint16_t total_length_to_end = buffer->buffer_size - read_index;
|
||||
// Number of bytes of after the read index that need to be copied:
|
||||
const uint16_t end_copy_length = MIN(total_length_to_end, length_to_copy);
|
||||
memcpy(data_out, &buffer->buffer[read_index], end_copy_length);
|
||||
|
||||
// If length_to_copy > total_length_to_end, there is more data at the
|
||||
// beginning of the circular buffer (wrapped around):
|
||||
const uint16_t wrapped_length = length_to_copy - end_copy_length;
|
||||
if (wrapped_length) {
|
||||
memcpy(data_out + total_length_to_end, buffer->buffer, wrapped_length);
|
||||
}
|
||||
|
||||
return length_to_copy;
|
||||
}
|
||||
|
||||
bool circular_buffer_read_or_copy(const CircularBuffer* buffer, uint8_t **data_out, size_t length,
|
||||
void *(*malloc_imp)(size_t), bool *caller_should_free) {
|
||||
UTIL_ASSERT(buffer && malloc_imp && data_out && caller_should_free);
|
||||
if (buffer->data_length < length) {
|
||||
return false;
|
||||
}
|
||||
const uint16_t continguous_length = (buffer->buffer_size - buffer->read_index);
|
||||
const bool should_malloc_and_copy = (length > continguous_length);
|
||||
*caller_should_free = should_malloc_and_copy;
|
||||
if (should_malloc_and_copy) {
|
||||
*data_out = (uint8_t *) malloc_imp(length);
|
||||
if (*data_out) {
|
||||
circular_buffer_copy(buffer, *data_out, length);
|
||||
} else {
|
||||
*caller_should_free = false;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
uint16_t length_out;
|
||||
circular_buffer_read(buffer, length, (const uint8_t **)data_out, &length_out);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool circular_buffer_consume(CircularBuffer* buffer, uint16_t length) {
|
||||
if (buffer->data_length < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer->read_index = (buffer->read_index + length) % buffer->buffer_size;
|
||||
buffer->data_length -= length;
|
||||
|
||||
// Reset read_index if there's no more data, so any newly written data won't wrap
|
||||
if (buffer->auto_reset && buffer->data_length == 0 && !buffer->write_in_progress) {
|
||||
buffer->read_index = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t circular_buffer_get_write_space_remaining(const CircularBuffer* buffer) {
|
||||
return buffer->buffer_size - buffer->data_length;
|
||||
}
|
||||
|
||||
uint16_t circular_buffer_get_read_space_remaining(const CircularBuffer* buffer) {
|
||||
return buffer->data_length;
|
||||
}
|
93
src/libutil/circular_cache.c
Normal file
93
src/libutil/circular_cache.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/circular_cache.h"
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void circular_cache_init(CircularCache* c, uint8_t* buffer, size_t item_size,
|
||||
int total_items, Comparator compare_cb) {
|
||||
UTIL_ASSERT(c);
|
||||
UTIL_ASSERT(buffer);
|
||||
UTIL_ASSERT(item_size);
|
||||
UTIL_ASSERT(compare_cb);
|
||||
|
||||
*c = (CircularCache) {
|
||||
.cache = buffer,
|
||||
.next_erased_item_idx = 0,
|
||||
.item_size = item_size,
|
||||
.total_items = total_items,
|
||||
.compare_cb = compare_cb
|
||||
};
|
||||
}
|
||||
|
||||
void circular_cache_set_item_destructor(CircularCache *c, CircularCacheItemDestructor destructor) {
|
||||
c->item_destructor = destructor;
|
||||
}
|
||||
|
||||
static uint8_t* prv_get_item_at_index(CircularCache* c, int index) {
|
||||
return c->cache + index * c->item_size;
|
||||
}
|
||||
|
||||
bool circular_cache_contains(CircularCache* c, void* theirs) {
|
||||
return (circular_cache_get(c, theirs) != NULL);
|
||||
}
|
||||
|
||||
void *circular_cache_get(CircularCache* c, void* theirs) {
|
||||
for (int i = 0; i < c->total_items; ++i) {
|
||||
uint8_t* ours = prv_get_item_at_index(c, i);
|
||||
if (c->compare_cb(ours, theirs) == 0) {
|
||||
return ours;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void circular_cache_push(CircularCache* c, void* new_item) {
|
||||
uint8_t* old_item = prv_get_item_at_index(c, c->next_erased_item_idx);
|
||||
if (c->item_destructor) {
|
||||
c->item_destructor(old_item);
|
||||
}
|
||||
memcpy(old_item, new_item, c->item_size);
|
||||
++c->next_erased_item_idx;
|
||||
UTIL_ASSERT(c->next_erased_item_idx <= c->total_items);
|
||||
if (c->next_erased_item_idx == c->total_items) {
|
||||
c->next_erased_item_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void circular_cache_fill(CircularCache *c, uint8_t *item) {
|
||||
// If you need item_destructor and fill, add an index pointing to the oldest item for destruction
|
||||
UTIL_ASSERT(c->item_destructor == NULL);
|
||||
for (int i = 0; i < c->total_items; ++i) {
|
||||
memcpy(c->cache + i * c->item_size, item, c->item_size);
|
||||
}
|
||||
}
|
||||
|
||||
void circular_cache_flush(CircularCache *c) {
|
||||
// This assumes that the user of this library can differentiate empty entries in the cache from
|
||||
// valid ones.
|
||||
for (int i = 0; i < c->total_items; i++) {
|
||||
uint8_t* item = prv_get_item_at_index(c, i);
|
||||
if (c->item_destructor) {
|
||||
c->item_destructor(item);
|
||||
}
|
||||
}
|
||||
c->next_erased_item_idx = 0;
|
||||
}
|
51
src/libutil/crc32.c
Normal file
51
src/libutil/crc32.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/crc32.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// Nybble-wide table driven CRC-32 algorithm
|
||||
//
|
||||
// A compromise between speed and size, this algorithm uses a lookup table to
|
||||
// calculate the CRC four bits at a time with a size cost of only 64 bytes. By
|
||||
// contrast, a byte-wide algorithm requires a lookup table sixteen times larger!
|
||||
//
|
||||
// The lookup table is generated by the crc32_lut.py
|
||||
|
||||
static const uint32_t s_lookup_table[] = {
|
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
||||
};
|
||||
|
||||
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) {
|
||||
if (data == 0) {
|
||||
return 0;
|
||||
}
|
||||
const uint8_t * restrict bytes = data;
|
||||
|
||||
crc ^= 0xffffffff;
|
||||
while (length--) {
|
||||
crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf];
|
||||
crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf];
|
||||
bytes++;
|
||||
}
|
||||
crc ^= 0xffffffff;
|
||||
return crc;
|
||||
}
|
37
src/libutil/hash.c
Normal file
37
src/libutil/hash.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Based on DJB2 Hash
|
||||
uint32_t hash(const uint8_t *bytes, const uint32_t length) {
|
||||
uint32_t hash = 5381;
|
||||
|
||||
if (length == 0) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
const uint8_t *last_byte = bytes + length;
|
||||
while (bytes != last_byte) {
|
||||
c = *bytes;
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
bytes++;
|
||||
}
|
||||
return hash;
|
||||
}
|
634
src/libutil/heap.c
Normal file
634
src/libutil/heap.c
Normal file
|
@ -0,0 +1,634 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/heap.h"
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/math.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* The following defines a type that is the size in bytes of the */
|
||||
/* desired alignment of each datr fragment. */
|
||||
typedef unsigned long Alignment_t;
|
||||
|
||||
/* The following defines the byte boundary size that has been */
|
||||
/* specified size if the alignment data. */
|
||||
#define ALIGNMENT_SIZE sizeof(Alignment_t)
|
||||
|
||||
/* The following structure is used to allign data fragments on a */
|
||||
/* specified memory boundary. */
|
||||
typedef union _tagAlignmentStruct_t
|
||||
{
|
||||
Alignment_t AlignmentValue;
|
||||
unsigned char ByteValue;
|
||||
} AlignmentStruct_t;
|
||||
|
||||
/* The following defines the size in bytes of a data fragment that is*/
|
||||
/* considered a large value. Allocations that are equal to and */
|
||||
/* larger than this value will be allocated from the end of the */
|
||||
/* buffer. */
|
||||
#define LARGE_SIZE (256/ALIGNMENT_SIZE)
|
||||
|
||||
//! The maximum size of the heap as a number of ALIGNMENT_SIZE
|
||||
#define SEGMENT_SIZE_MAX (0x7FFF)
|
||||
|
||||
/* The following defines the minimum size (in alignment units) of a */
|
||||
/* fragment that is considered useful. The value is used when trying*/
|
||||
/* to determine if a fragment that is larger than the requested size */
|
||||
/* can be broken into 2 framents leaving a fragment that is of the */
|
||||
/* requested size and one that is at least as larger as the */
|
||||
/* MINIMUM_MEMORY_SIZE. */
|
||||
#define MINIMUM_MEMORY_SIZE 1
|
||||
|
||||
/* The following defines the structure that describes a memory */
|
||||
/* fragment. */
|
||||
typedef struct _tagHeapInfo_t
|
||||
{
|
||||
//! Size of the preceding segment, measured in units of ALIGNMENT_SIZE, including the size of this beginer
|
||||
uint16_t PrevSize;
|
||||
|
||||
//! Whether or not this block is currently allocated (vs being free).
|
||||
bool is_allocated:1;
|
||||
|
||||
//! Size of this segment, measured in units of ALIGNMENT_SIZE, including this size of this beginer
|
||||
uint16_t Size:15;
|
||||
|
||||
#ifdef MALLOC_INSTRUMENTATION
|
||||
uintptr_t pc; //<! The address that called malloc.
|
||||
#endif
|
||||
|
||||
//! This is the actual buffer that's returned to the caller. We use this struct to make
|
||||
//! sure the buffer we return is appropriately aligned.
|
||||
AlignmentStruct_t Data;
|
||||
} HeapInfo_t;
|
||||
|
||||
|
||||
//! The size of a block in units of Alignment_t, including the beginer and including _x words of data.
|
||||
#define HEAP_INFO_BLOCK_SIZE(_x) ((offsetof(HeapInfo_t, Data) / ALIGNMENT_SIZE) + (_x))
|
||||
|
||||
//! Convert a pointer to the Data member to a pointer to the HeapInfo_t beginer
|
||||
#define HEAP_INFO_FOR_PTR(ptr) (HeapInfo_t *)(((Alignment_t *)ptr) - HEAP_INFO_BLOCK_SIZE(0))
|
||||
|
||||
_Static_assert((offsetof(HeapInfo_t, Data) % ALIGNMENT_SIZE) == 0, "Heap not properly aligned.");
|
||||
|
||||
//! Heap is assumed corrupt if expr does not evaluate true
|
||||
#define HEAP_ASSERT_SANE(heap, expr, log_addr) \
|
||||
if (!(expr)) { prv_handle_corruption(heap, log_addr); }
|
||||
|
||||
//! Lock the heap, using whatever behaviour the heap has configured using heap_set_lock_impl
|
||||
static void heap_lock(Heap* heap) {
|
||||
if (heap->lock_impl.lock_function) {
|
||||
heap->lock_impl.lock_function(heap->lock_impl.lock_context);
|
||||
}
|
||||
}
|
||||
|
||||
//! Unlock the heap, using whatever behaviour the heap has configured using heap_set_lock_impl
|
||||
static void heap_unlock(Heap* heap) {
|
||||
if (heap->lock_impl.unlock_function) {
|
||||
heap->lock_impl.unlock_function(heap->lock_impl.lock_context);
|
||||
}
|
||||
|
||||
// Handle any heap corruption that may have been detected while the heap was locked.
|
||||
if (heap->corrupt_block != NULL) {
|
||||
heap->corruption_handler(heap->corrupt_block);
|
||||
heap->corrupt_block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_handle_corruption(Heap * const heap, void *ptr) {
|
||||
if (heap->corruption_handler) {
|
||||
heap->corrupt_block = ptr;
|
||||
return;
|
||||
}
|
||||
UTIL_ASSERT(0); // Error: Heap corrupt around <ptr>
|
||||
}
|
||||
|
||||
static HeapInfo_t *find_segment(Heap* const heap, unsigned long n_units);
|
||||
static HeapInfo_t *allocate_block(Heap* const heap, unsigned long n_units, HeapInfo_t* heap_info_ptr);
|
||||
|
||||
//! Advance the block pointer to the next block.
|
||||
static HeapInfo_t* get_next_block(Heap * const heap, HeapInfo_t* block) {
|
||||
HEAP_ASSERT_SANE(heap, block->Size != 0, block);
|
||||
return (HeapInfo_t *)(((Alignment_t *)block) + block->Size);
|
||||
}
|
||||
|
||||
//! Move the block pointer back to the previous block.
|
||||
static HeapInfo_t* get_previous_block(Heap * const heap, HeapInfo_t* block) {
|
||||
HEAP_ASSERT_SANE(heap, block->PrevSize != 0, block);
|
||||
return (HeapInfo_t *)(((Alignment_t *)block) - block->PrevSize);
|
||||
}
|
||||
|
||||
static void prv_calc_totals(Heap* const heap, unsigned int *used, unsigned int *free, unsigned int *max_free) {
|
||||
HeapInfo_t *heap_info_ptr;
|
||||
uint16_t free_segments;
|
||||
uint16_t alloc_segments;
|
||||
|
||||
/* Initialize the return values. */
|
||||
*used = 0;
|
||||
*free = 0;
|
||||
*max_free = 0;
|
||||
free_segments = 0;
|
||||
alloc_segments = 0;
|
||||
heap_info_ptr = heap->begin;
|
||||
|
||||
do {
|
||||
/* Check to see if the current fragment is marked as free. */
|
||||
if(heap_info_ptr->is_allocated) {
|
||||
alloc_segments++;
|
||||
|
||||
*used += heap_info_ptr->Size * ALIGNMENT_SIZE;
|
||||
} else {
|
||||
free_segments++;
|
||||
|
||||
/* Accumulate the total number of free Bytes. */
|
||||
*free += heap_info_ptr->Size * ALIGNMENT_SIZE;
|
||||
|
||||
/* Check to see if the current fragment is larger that any */
|
||||
/* we have seen and update the Max Value if it is larger. */
|
||||
if(heap_info_ptr->Size > *max_free) {
|
||||
*max_free = heap_info_ptr->Size * ALIGNMENT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the pointer to the next entry. */
|
||||
heap_info_ptr = get_next_block(heap, heap_info_ptr);
|
||||
|
||||
} while (heap_info_ptr < heap->end);
|
||||
|
||||
UTIL_ASSERT(heap_info_ptr == heap->end);
|
||||
|
||||
char format_str[80];
|
||||
snprintf(format_str, sizeof(format_str), "alloc: %u (%u bytes), free: %u (%u bytes)",
|
||||
alloc_segments, *used, free_segments, *free);
|
||||
util_dbgserial_str(format_str);
|
||||
}
|
||||
|
||||
void heap_calc_totals(Heap* const heap, unsigned int *used, unsigned int *free, unsigned int *max_free) {
|
||||
UTIL_ASSERT(heap);
|
||||
|
||||
/* Verify that the parameters that were passed in appear valid. */
|
||||
if((used) && (free) && (max_free) && (heap->begin)) {
|
||||
heap_lock(heap);
|
||||
prv_calc_totals(heap, used, free, max_free);
|
||||
heap_unlock(heap);
|
||||
}
|
||||
}
|
||||
|
||||
void heap_init(Heap* const heap, void* start, void* end, bool fuzz_on_free) {
|
||||
UTIL_ASSERT(start && end);
|
||||
|
||||
// Align the pointer by advancing it to the next boundary.
|
||||
start = (void*)((((uintptr_t) start) + (sizeof(Alignment_t) - 1)) & ~(sizeof(Alignment_t) - 1));
|
||||
end = (void*) (((uintptr_t) end) & ~(sizeof(Alignment_t) - 1));
|
||||
|
||||
// Calculate the size of the heap in alignment units.
|
||||
uint32_t heap_size = ((uintptr_t)end - (uintptr_t)start) / ALIGNMENT_SIZE;
|
||||
// If we have more space than we can use, just limit it to the usable space. This limit is caused
|
||||
// by the width of .Size and .PrevSize in HeapInfo_t
|
||||
heap_size = MIN(SEGMENT_SIZE_MAX, heap_size);
|
||||
end = ((char*) start) + (heap_size * ALIGNMENT_SIZE);
|
||||
|
||||
memset(start, 0, heap_size * ALIGNMENT_SIZE);
|
||||
|
||||
*heap = (Heap) {
|
||||
.begin = start,
|
||||
.end = end,
|
||||
.fuzz_on_free = fuzz_on_free
|
||||
};
|
||||
|
||||
*(heap->begin) = (HeapInfo_t) {
|
||||
.PrevSize = heap_size,
|
||||
.is_allocated = false,
|
||||
.Size = heap_size
|
||||
};
|
||||
}
|
||||
|
||||
void heap_set_lock_impl(Heap *heap, HeapLockImpl lock_impl) {
|
||||
heap->lock_impl = lock_impl;
|
||||
}
|
||||
|
||||
void heap_set_double_free_handler(Heap *heap, DoubleFreeHandler double_free_handler) {
|
||||
heap->double_free_handler = double_free_handler;
|
||||
}
|
||||
|
||||
void heap_set_corruption_handler(Heap *heap, CorruptionHandler corruption_handler) {
|
||||
heap->corruption_handler = corruption_handler;
|
||||
}
|
||||
|
||||
void *heap_malloc(Heap* const heap, unsigned long nbytes, uintptr_t client_pc) {
|
||||
// Check to make sure the heap we have is initialized.
|
||||
UTIL_ASSERT(heap->begin);
|
||||
|
||||
/* Convert the requested memory allocation in bytes to alignment */
|
||||
/* size. */
|
||||
unsigned long allocation_size = (nbytes + (ALIGNMENT_SIZE - 1)) / ALIGNMENT_SIZE;
|
||||
|
||||
/* Add the beginer size to the requested size. */
|
||||
allocation_size += HEAP_INFO_BLOCK_SIZE(0);
|
||||
|
||||
/* Verify that the requested size is valid */
|
||||
if ((allocation_size < HEAP_INFO_BLOCK_SIZE(1)) || (allocation_size >= SEGMENT_SIZE_MAX)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HeapInfo_t* allocated_block;
|
||||
|
||||
heap_lock(heap);
|
||||
{
|
||||
HeapInfo_t* free_block = find_segment(heap, allocation_size);
|
||||
allocated_block = allocate_block(heap, allocation_size, free_block);
|
||||
|
||||
if (allocated_block != NULL) {
|
||||
// We've allocated a new block, update our metrics
|
||||
|
||||
#ifdef MALLOC_INSTRUMENTATION
|
||||
allocated_block->pc = client_pc;
|
||||
#endif
|
||||
|
||||
heap->current_size += allocated_block->Size * ALIGNMENT_SIZE;
|
||||
if (heap->current_size > heap->high_water_mark) {
|
||||
heap->high_water_mark = heap->current_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
heap_unlock(heap);
|
||||
|
||||
if (allocated_block) {
|
||||
return &allocated_block->Data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void heap_free(Heap* const heap, void *ptr, uintptr_t client_pc) {
|
||||
UTIL_ASSERT(heap->begin);
|
||||
|
||||
if (!ptr) {
|
||||
// free(0) is a no-op, just bail
|
||||
return;
|
||||
}
|
||||
|
||||
UTIL_ASSERT(heap_contains_address(heap, ptr)); // <ptr> not in range (heap->begin, heap->end)
|
||||
|
||||
heap_lock(heap);
|
||||
{
|
||||
/* Get a pointer to the Heap Info. */
|
||||
HeapInfo_t *heap_info_ptr = HEAP_INFO_FOR_PTR(ptr);
|
||||
|
||||
/* Verify that this segment is allocated. */
|
||||
if (!heap_info_ptr->is_allocated) {
|
||||
// If not, try to the call the handler if present. If there's no handler, just explode.
|
||||
// If there is a handler, let them know this happened and then just no-op and fast return.
|
||||
if (heap->double_free_handler) {
|
||||
heap_unlock(heap);
|
||||
|
||||
heap->double_free_handler(ptr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UTIL_ASSERT(0); // heap_free on invalid pointer <ptr>
|
||||
}
|
||||
|
||||
/* Mask out the allocation bit of the segment to be freed. */
|
||||
/* This will make calculations in this block easier. */
|
||||
heap_info_ptr->is_allocated = false;
|
||||
|
||||
#ifndef RELEASE
|
||||
if (heap->fuzz_on_free) {
|
||||
memset(ptr, 0xBD,
|
||||
(heap_info_ptr->Size - HEAP_INFO_BLOCK_SIZE(0)) * ALIGNMENT_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update metrics
|
||||
#ifdef MALLOC_INSTRUMENTATION
|
||||
heap_info_ptr->pc = client_pc;
|
||||
#endif
|
||||
heap->current_size -= heap_info_ptr->Size * ALIGNMENT_SIZE;
|
||||
|
||||
/* If the segment to be freed is at the begin of the heap, */
|
||||
/* then we do not have to merge or update any sizes of the */
|
||||
/* previous segment. This will also handle the case where */
|
||||
/* the entire heap has been allocated to one segment. */
|
||||
if(heap_info_ptr != heap->begin) {
|
||||
/* Calculate the pointer to the previous segment. */
|
||||
HeapInfo_t *previous_block = get_previous_block(heap, heap_info_ptr);
|
||||
|
||||
HEAP_ASSERT_SANE(heap, previous_block->Size == heap_info_ptr->PrevSize, heap_info_ptr);
|
||||
|
||||
/* Check to see if the previous segment can be combined. */
|
||||
if(!previous_block->is_allocated) {
|
||||
/* Add the segment to be freed to the new beginer. */
|
||||
previous_block->Size += heap_info_ptr->Size;
|
||||
|
||||
/* Set the pointer to the beginning of the previous */
|
||||
/* free segment. */
|
||||
heap_info_ptr = previous_block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the pointer to the next segment. */
|
||||
HeapInfo_t *next_block = get_next_block(heap, heap_info_ptr);
|
||||
|
||||
/* If we are pointing at the end of the heap, then use the */
|
||||
/* begin as the next segment. */
|
||||
if(next_block == heap->end) {
|
||||
/* We can't combine the begin with the end, so just */
|
||||
/* update the PrevSize field. */
|
||||
heap->begin->PrevSize = heap_info_ptr->Size;
|
||||
}
|
||||
else {
|
||||
HEAP_ASSERT_SANE(heap, next_block->PrevSize == (HEAP_INFO_FOR_PTR(ptr))->Size, next_block);
|
||||
|
||||
/* We are not pointing to the end of the heap, so if the */
|
||||
/* next segment is allocated then update the PrevSize */
|
||||
/* field. */
|
||||
if (next_block->is_allocated) {
|
||||
next_block->PrevSize = heap_info_ptr->Size;
|
||||
} else {
|
||||
/* The next segment is free, so merge it with the */
|
||||
/* current segment. */
|
||||
heap_info_ptr->Size += next_block->Size;
|
||||
|
||||
/* Since we merged the next segment, we have to update*/
|
||||
/* the next next segment's PrevSize field. */
|
||||
HeapInfo_t *next_next_block = get_next_block(heap, heap_info_ptr);
|
||||
|
||||
/* If we are pointing at the end of the heap, then use*/
|
||||
/* the begin as the next next segment. */
|
||||
if(next_next_block == heap->end) {
|
||||
heap->begin->PrevSize = heap_info_ptr->Size;
|
||||
}
|
||||
else {
|
||||
next_next_block->PrevSize = heap_info_ptr->Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
heap_unlock(heap);
|
||||
}
|
||||
|
||||
bool heap_is_allocated(Heap* const heap, void* ptr) {
|
||||
bool rc = false;
|
||||
if (!heap_contains_address(heap, ptr)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
HeapInfo_t *heap_info_ptr = heap->begin;
|
||||
|
||||
// Iterate through the heap to see if this pointer is still allocated.
|
||||
heap_lock(heap);
|
||||
while (heap_info_ptr < heap->end) {
|
||||
if (heap_info_ptr == HEAP_INFO_FOR_PTR(ptr)) {
|
||||
rc = heap_info_ptr->is_allocated;
|
||||
break;
|
||||
}
|
||||
heap_info_ptr = get_next_block(heap, heap_info_ptr);
|
||||
}
|
||||
heap_unlock(heap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool heap_contains_address(Heap* const heap, void* ptr) {
|
||||
return (ptr >= (void*) heap->begin && ptr < (void*) heap->end);
|
||||
}
|
||||
|
||||
size_t heap_size(const Heap *heap) {
|
||||
return ((char*) heap->end) - ((char*) heap->begin);
|
||||
}
|
||||
|
||||
static void prv_sanity_check_block(Heap * const heap, HeapInfo_t *block) {
|
||||
HeapInfo_t* prev_block = get_previous_block(heap, block);
|
||||
HEAP_ASSERT_SANE(heap,
|
||||
prev_block <= heap->begin || prev_block->Size == block->PrevSize, block);
|
||||
HeapInfo_t* next_block = get_next_block(heap, block);
|
||||
HEAP_ASSERT_SANE(heap, next_block >= heap->end || next_block->PrevSize == block->Size, block);
|
||||
}
|
||||
|
||||
//! Finds a segment where data of the size n_units will fit.
|
||||
//! @param n_units number of ALIGNMENT_SIZE units this segment requires.
|
||||
static HeapInfo_t *find_segment(Heap* const heap, unsigned long n_units) {
|
||||
HeapInfo_t *heap_info_ptr = NULL;
|
||||
/* If we are allocating a large segment, then start at the */
|
||||
/* end of the heap. Otherwise, start at the beginning of */
|
||||
/* the heap. If there is only one segment in the heap, then*/
|
||||
/* heap->begin will be used either way. */
|
||||
if (n_units >= LARGE_SIZE) {
|
||||
heap_info_ptr = (HeapInfo_t *)(((Alignment_t *)heap->end) - heap->begin->PrevSize);
|
||||
} else {
|
||||
heap_info_ptr = heap->begin;
|
||||
}
|
||||
|
||||
/* Loop until we have walked the entire list. */
|
||||
while (((n_units < LARGE_SIZE) || (heap_info_ptr > heap->begin)) && (heap_info_ptr < heap->end)) {
|
||||
|
||||
prv_sanity_check_block(heap, heap_info_ptr);
|
||||
|
||||
/* Check to see if the current entry is free and is large*/
|
||||
/* enough to hold the data being requested. */
|
||||
if (heap_info_ptr->is_allocated || (heap_info_ptr->Size < n_units)) {
|
||||
/* If the requested size is larger than the limit then*/
|
||||
/* search backwards for an available buffer, else go */
|
||||
/* forward. This will hopefully help to reduce */
|
||||
/* fragmentataion problems. */
|
||||
if (n_units >= LARGE_SIZE) {
|
||||
heap_info_ptr = get_previous_block(heap, heap_info_ptr);
|
||||
} else {
|
||||
heap_info_ptr = get_next_block(heap, heap_info_ptr);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the space we found is within the bounds of the heap
|
||||
UTIL_ASSERT((heap_info_ptr >= heap->begin) &&
|
||||
(heap_info_ptr <= heap->end));
|
||||
|
||||
return heap_info_ptr;
|
||||
}
|
||||
|
||||
//! Split a block into two smaller blocks, returning a pointer to the new second block.
|
||||
//! The first block will be available at the same location as before, but with a smaller size.
|
||||
//! Assumes the block is big enough to be split and is unallocated.
|
||||
//! @param first_part_size the size of the new block, in ALIGNMENT_SIZE units, including the beginer
|
||||
static HeapInfo_t* split_block(Heap *heap, HeapInfo_t* block, size_t first_part_size) {
|
||||
HeapInfo_t* second_block = (HeapInfo_t*) (((Alignment_t*) block) + first_part_size);
|
||||
|
||||
second_block->PrevSize = first_part_size;
|
||||
second_block->is_allocated = false;
|
||||
second_block->Size = block->Size - first_part_size;
|
||||
|
||||
block->Size = first_part_size;
|
||||
|
||||
/* Calculate the pointer to the next segment. */
|
||||
HeapInfo_t* next_next_block = get_next_block(heap, second_block);
|
||||
|
||||
/* Check for a wrap condition and update the next */
|
||||
/* segment's PrevSize field. */
|
||||
if (next_next_block == heap->end) {
|
||||
heap->begin->PrevSize = second_block->Size;
|
||||
} else {
|
||||
next_next_block->PrevSize = second_block->Size;
|
||||
}
|
||||
|
||||
return second_block;
|
||||
}
|
||||
|
||||
//! Allocated the block in the given HeapInfo_t segment.
|
||||
//! @param n_units number of ALIGNMENT_SIZE units this segment requires (including space for the beginer).
|
||||
//! @param heap_info_ptr the segment where the block should be allocated.
|
||||
static HeapInfo_t *allocate_block(Heap* const heap, unsigned long n_units, HeapInfo_t* heap_info_ptr) {
|
||||
// Make sure we can use all or part of this block for this allocation.
|
||||
if (heap_info_ptr == heap->end || heap_info_ptr->is_allocated || heap_info_ptr->Size < n_units) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check to see if we need to split this into two */
|
||||
/* entries. */
|
||||
/* * NOTE * If there is not enough room to make another */
|
||||
/* entry then we will not adjust the size of */
|
||||
/* this entry to match the amount requested. */
|
||||
if (heap_info_ptr->Size < (n_units + HEAP_INFO_BLOCK_SIZE(MINIMUM_MEMORY_SIZE))) {
|
||||
// Nope! Just use the whole block.
|
||||
heap_info_ptr->is_allocated = true;
|
||||
return heap_info_ptr;
|
||||
}
|
||||
|
||||
/* If this is a large segment allocation, then split */
|
||||
/* the segment so that the free segment is at the */
|
||||
/* beginning. */
|
||||
if (n_units >= LARGE_SIZE) {
|
||||
HeapInfo_t *second_block = split_block(heap, heap_info_ptr, heap_info_ptr->Size - n_units);
|
||||
second_block->is_allocated = true;
|
||||
return second_block;
|
||||
}
|
||||
|
||||
split_block(heap, heap_info_ptr, n_units);
|
||||
heap_info_ptr->is_allocated = true;
|
||||
return heap_info_ptr;
|
||||
}
|
||||
|
||||
// A very naive realloc implementation
|
||||
void* heap_realloc(Heap* const heap, void *ptr, unsigned long nbytes, uintptr_t client_pc) {
|
||||
#if !defined(MALLOC_INSTRUMENTATION)
|
||||
client_pc = 0;
|
||||
#endif
|
||||
// Get a pointer to the Heap Info.
|
||||
void *new_ptr = heap_malloc(heap, nbytes, client_pc);
|
||||
if (new_ptr && ptr) {
|
||||
// Copy over old data.
|
||||
const HeapInfo_t *heap_info_ptr = HEAP_INFO_FOR_PTR(ptr);
|
||||
const uint16_t original_size = heap_info_ptr->Size * ALIGNMENT_SIZE;
|
||||
memcpy(new_ptr, ptr, MIN(nbytes, original_size));
|
||||
heap_free(heap, ptr, client_pc);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
void* heap_zalloc(Heap* const heap, size_t size, uintptr_t client_pc) {
|
||||
void *ptr = heap_malloc(heap, size, client_pc);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* heap_calloc(Heap* const heap, size_t count, size_t size, uintptr_t client_pc) {
|
||||
return heap_zalloc(heap, count * size, client_pc);
|
||||
}
|
||||
|
||||
uint32_t heap_get_minimum_headroom(Heap *heap) {
|
||||
return (heap_size(heap) - heap->high_water_mark);
|
||||
}
|
||||
|
||||
// Serial Commands
|
||||
///////////////////////////////////////////////////////////
|
||||
#ifdef MALLOC_INSTRUMENTATION
|
||||
void heap_dump_malloc_instrumentation_to_dbgserial(Heap *heap) {
|
||||
char buffer[80];
|
||||
unsigned long num_free_blocks = 0;
|
||||
unsigned long num_free_bytes = 0;
|
||||
unsigned long num_alloc_blocks = 0;
|
||||
unsigned long num_alloc_bytes = 0;
|
||||
unsigned long largest_free = 0;
|
||||
|
||||
// The output in this function is parsed by tools/parse_dump_malloc.py, so don't change it
|
||||
// without updating that file as well.
|
||||
|
||||
HeapInfo_t* heap_iter = heap->begin;
|
||||
|
||||
heap_lock(heap);
|
||||
void* pc;
|
||||
char* type;
|
||||
while (heap_iter < heap->end) {
|
||||
unsigned long block_size = (long)(heap_iter->Size) * ALIGNMENT_SIZE;
|
||||
|
||||
if (heap_iter->is_allocated) {
|
||||
pc = (void *)heap_iter->pc;
|
||||
type = "";
|
||||
num_alloc_blocks++;
|
||||
num_alloc_bytes += block_size;
|
||||
} else {
|
||||
pc = NULL;
|
||||
type = "FREE";
|
||||
num_free_blocks++;
|
||||
num_free_bytes += block_size;
|
||||
largest_free = MAX(largest_free, block_size);
|
||||
}
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "PC:0x%08lX Addr:0x%08lX Bytes:%-8lu %s",
|
||||
(long)pc, (long)&heap_iter->Data, block_size, type);
|
||||
util_dbgserial_str(buffer);
|
||||
heap_iter = get_next_block(heap, heap_iter);
|
||||
}
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap start %p", heap->begin);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap end %p", heap->end);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap total size %zu", heap_size(heap));
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap allocated %u", heap->current_size);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap high water mark %u", heap->high_water_mark);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap free blocks: %lu bytes, %lu blocks", num_free_bytes,
|
||||
num_free_blocks);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap alloc blocks: %lu bytes, %lu blocks", num_alloc_bytes,
|
||||
num_alloc_blocks);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Heap largest free block: %lu", largest_free);
|
||||
util_dbgserial_str(buffer);
|
||||
|
||||
heap_unlock(heap);
|
||||
|
||||
}
|
||||
#endif
|
90
src/libutil/hexdump.c
Normal file
90
src/libutil/hexdump.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/hexdump.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// offset + gap + 8 hex bytes + gap + 8 hex bytes + gap + 8 ascii bytes
|
||||
// + mini gap + 8 ascii bytes + null:
|
||||
#define LINE_BUFFER_LENGTH (4 + 2 + (3 * 8) + 2 + (3 * 8) + 2 + 8 + 1 + 8 + 1)
|
||||
|
||||
void hexdump(const char *src_filename, int src_line_number, int level,
|
||||
const uint8_t *data, size_t length, HexdumpLineCallback write_line_cb) {
|
||||
char line_buffer[LINE_BUFFER_LENGTH];
|
||||
unsigned int offset = 0;
|
||||
|
||||
while (offset < length) {
|
||||
unsigned int buffer_offset = 0;
|
||||
|
||||
// Print data line offset
|
||||
snprintf(line_buffer, LINE_BUFFER_LENGTH, "%04x ", offset);
|
||||
buffer_offset += 6;
|
||||
|
||||
// Print the hex bytes
|
||||
const unsigned int num_line_bytes = ((length - offset) > 16) ? 16 : (length - offset);
|
||||
for (unsigned int i = 0; i < num_line_bytes; ++i) {
|
||||
if (i == 8) {
|
||||
line_buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
snprintf(line_buffer + buffer_offset, LINE_BUFFER_LENGTH - buffer_offset, "%02x ",
|
||||
data[offset + i]);
|
||||
buffer_offset += 3;
|
||||
}
|
||||
|
||||
// Calculate and apply padding between the hex dump and the ascii dump.
|
||||
unsigned int required_padding = 2;
|
||||
if (num_line_bytes < 16) {
|
||||
// If we're printing a partial line, pad out the rest so the
|
||||
// ascii lines up.
|
||||
required_padding += (16 - num_line_bytes) * 3;
|
||||
if (num_line_bytes <= 8)
|
||||
{
|
||||
// Account for the gap between the 8 byte hex blocks.
|
||||
required_padding += 1;
|
||||
}
|
||||
}
|
||||
|
||||
memset(line_buffer + buffer_offset, ' ', required_padding);
|
||||
buffer_offset += required_padding;
|
||||
|
||||
// Print the ASCII bytes
|
||||
for (unsigned int i = 0; i < num_line_bytes; ++i) {
|
||||
if (i == 8)
|
||||
{
|
||||
line_buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
char c = data[offset + i];
|
||||
if (c < 32 || c > 126 || c == '`') // ` is used for log hash string delimiting
|
||||
{
|
||||
c = '.';
|
||||
}
|
||||
line_buffer[buffer_offset++] = c;
|
||||
}
|
||||
|
||||
// No need to pad after a partial ascii line, since we don't line up
|
||||
// anything after it.
|
||||
|
||||
// Null terminate and print.
|
||||
line_buffer[buffer_offset] = 0;
|
||||
write_line_cb(level, src_filename, src_line_number, line_buffer);
|
||||
|
||||
offset += 16;
|
||||
}
|
||||
}
|
29
src/libutil/includes/util/assert.h
Normal file
29
src/libutil/includes/util/assert.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/attributes.h"
|
||||
#include "util/likely.h"
|
||||
|
||||
NORETURN util_assertion_failed(const char *filename, int line);
|
||||
|
||||
#define UTIL_ASSERT(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
util_assertion_failed(__FILE_NAME__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
91
src/libutil/includes/util/attributes.h
Normal file
91
src/libutil/includes/util/attributes.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__clang__)
|
||||
#define GCC_ONLY(x)
|
||||
#else
|
||||
#define GCC_ONLY(x) x
|
||||
#endif
|
||||
|
||||
// Function attributes
|
||||
#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST)))
|
||||
|
||||
#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST)
|
||||
|
||||
#define ALWAYS_INLINE __attribute__((__always_inline__)) inline
|
||||
#define DEPRECATED __attribute__((deprecated))
|
||||
#define NOINLINE __attribute__((__noinline__))
|
||||
#define NORETURN __attribute__((__noreturn__)) void
|
||||
#define NAKED_FUNC __attribute__((__naked__))
|
||||
#define OPTIMIZE_FUNC(LVL) GCC_ONLY(__attribute__((__optimize__(LVL))))
|
||||
#define CONST_FUNC __attribute__((__const__))
|
||||
#define PURE_FUNC __attribute__((__pure__))
|
||||
|
||||
// Variable attributes
|
||||
#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC)))
|
||||
|
||||
// Structure attributes
|
||||
#define PACKED __attribute__((__packed__))
|
||||
|
||||
// General attributes
|
||||
#define USED __attribute__((__used__))
|
||||
#define UNUSED __attribute__((__unused__))
|
||||
#define WEAK __attribute__((__weak__))
|
||||
#define ALIAS(sym) __attribute__((__weak__, __alias__(sym)))
|
||||
#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__)))
|
||||
#define ALIGN(bytes) __attribute__((__aligned__(bytes)))
|
||||
|
||||
// Unit tests break if variables go in custom sections
|
||||
#if !UNITTEST
|
||||
# define SECTION(SEC) __attribute__((__section__(SEC)))
|
||||
#else
|
||||
# define SECTION(SEC)
|
||||
#endif
|
||||
|
||||
// Only present on STM32F7
|
||||
#define DTCM_BSS SECTION(".dtcm_bss")
|
||||
|
||||
// DMA_BSS: Section attribute for DMA buffers
|
||||
#if MICRO_FAMILY_STM32F7
|
||||
# define DMA_BSS DTCM_BSS
|
||||
// There is an erratum present in STM32F7xx which causes DMA reads from DTCM
|
||||
// (but not writes to DTCM) to be corrupted if the MCU enters sleep mode during
|
||||
// the transfer. Source DMA buffers must be placed in SRAM on these platforms.
|
||||
// The DMA driver enforces this. Also, alignment to the start of a cache line
|
||||
// seems to be required, though it's unclear why.
|
||||
# define DMA_READ_BSS ALIGN(32)
|
||||
#else
|
||||
# define DMA_BSS
|
||||
# define DMA_READ_BSS
|
||||
#endif
|
||||
|
||||
// Use this macro to allow overriding of private functions in order to test them within unit tests.
|
||||
#if !UNITTEST
|
||||
# define T_STATIC static
|
||||
#else
|
||||
# define T_STATIC WEAK
|
||||
#endif
|
||||
|
||||
// Use this macro to allow overriding of non-static (i.e. global) functions in order to test them
|
||||
// within unit tests. For lack of a better name, we have named this a MOCKABLE (i.e. can be
|
||||
// mocked or overridden in unit tests but not in normal firmware)
|
||||
#if !UNITTEST
|
||||
# define MOCKABLE
|
||||
#else
|
||||
# define MOCKABLE WEAK
|
||||
#endif
|
41
src/libutil/includes/util/build_id.h
Normal file
41
src/libutil/includes/util/build_id.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
//! The linker inserts the build id as an "elf external note" structure:
|
||||
typedef struct PACKED {
|
||||
uint32_t name_length;
|
||||
uint32_t data_length;
|
||||
uint32_t type; // NT_GNU_BUILD_ID = 3
|
||||
uint8_t data[]; // concatenated name ('GNU') + data (build id)
|
||||
} ElfExternalNote;
|
||||
|
||||
// the build id is a unique identification for the built files. The default algo uses SHA1
|
||||
// to produce a 160 bit (20 byte sequence)
|
||||
#define BUILD_ID_EXPECTED_LEN (20)
|
||||
|
||||
#define BUILD_ID_NAME_EXPECTED_LEN (4)
|
||||
|
||||
#define BUILD_ID_TOTAL_EXPECTED_LEN (sizeof(ElfExternalNote) + \
|
||||
BUILD_ID_NAME_EXPECTED_LEN + BUILD_ID_EXPECTED_LEN)
|
||||
|
||||
bool build_id_contains_gnu_build_id(const ElfExternalNote *note);
|
124
src/libutil/includes/util/circular_buffer.h
Normal file
124
src/libutil/includes/util/circular_buffer.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
//! The circular buffer is a contiguous buffer of a fixed length where data is written and consumed
|
||||
//! in a circular fashion. When the write ends up writing past the end of the buffer, it wraps
|
||||
//! around to use the first part of the buffer again, assuming that someone else has consumed some
|
||||
//! data to free it up.
|
||||
typedef struct CircularBuffer {
|
||||
uint8_t* buffer;
|
||||
bool write_in_progress;
|
||||
bool auto_reset;
|
||||
uint16_t buffer_size;
|
||||
uint16_t read_index; //! What offset in buffer that we should read from next.
|
||||
uint16_t data_length; //! How many bytes after read_index contain valid data.
|
||||
} CircularBuffer;
|
||||
|
||||
void circular_buffer_init(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size);
|
||||
|
||||
//! Extended _init() -- provides access to auto_reset flag.
|
||||
//! If auto_reset is true (default), on _consume(), read/write pointers will be reset in an attempt
|
||||
//! to reduce buffer wraps. If false, the buffer will always wrap, leaving the previous data
|
||||
//! in the buffer. This is handy for post mortem evaluation of debug logs, etc.
|
||||
void circular_buffer_init_ex(CircularBuffer* buffer, uint8_t* storage, uint16_t storage_size,
|
||||
bool auto_reset);
|
||||
|
||||
//! Copies data from a given buffer and length into the circular buffer.
|
||||
//! @return false if there's insufficient space.
|
||||
bool circular_buffer_write(CircularBuffer* buffer, const void* data, uint16_t length);
|
||||
|
||||
//! Gets a pointer into the circular buffer where the caller itself can write data to.
|
||||
//! @note After the client is done writing data, it _must_ call circular_buffer_write_finish()
|
||||
//! so that the CircularBuffer can update the length of the data it contains and update its internal
|
||||
//! bookkeeping
|
||||
//! @param[out] data_out Pointer to storage for the pointer that is set the the start of the
|
||||
//! writable area when the function returns, or NULL if there is no space available.
|
||||
//! @return The maximum number of bytes that can be written starting at the returned the pointer.
|
||||
//! Zero is returned when there is no space available.
|
||||
uint16_t circular_buffer_write_prepare(CircularBuffer *buffer, uint8_t **data_out);
|
||||
|
||||
//! To be used after circular_buffer_write_prepare(), to make the CircularBuffer update the length
|
||||
//! of the data it contains.
|
||||
//! @param written_length The length that has just been writted at the pointer provided by
|
||||
//! circular_buffer_write_prepare().
|
||||
void circular_buffer_write_finish(CircularBuffer *buffer, uint16_t written_length);
|
||||
|
||||
//! Read a contiguous chunk of memory from the circular buffer. The data remains on the buffer until
|
||||
//! circular_buffer_consume is called.
|
||||
//!
|
||||
//! If the circular buffer wraps in the middle of the requested data, this function call will return true but will
|
||||
//! provide fewer bytes that requested. When this happens, the length_out parameter will be set to a value smaller
|
||||
//! than length. A second read call can be made with the remaining smaller length to retreive the rest.
|
||||
//!
|
||||
//! The reason this read doesn't consume is to avoid having to copy out the data. The data_out pointer should be
|
||||
//! stable until you explicitely ask for it to be consumed with circular_buffer_consume.
|
||||
//!
|
||||
//! @param buffer The buffer to read from
|
||||
//! @param length How many bytes to read
|
||||
//! @param[out] data_out The bytes that were read.
|
||||
//! @param[out] length_out How many bytes were read.
|
||||
//! @return false if there's less than length bytes in the buffer.
|
||||
bool circular_buffer_read(const CircularBuffer* buffer, uint16_t length, const uint8_t** data_out, uint16_t* length_out);
|
||||
|
||||
//! Same as circular_buffer_copy_offset(), but with a starting offset of zero.
|
||||
uint16_t circular_buffer_copy(const CircularBuffer* buffer, void *data_out, uint16_t length);
|
||||
|
||||
//! Copy a number of bytes from the circular buffer into another (contiguous)
|
||||
//! buffer. The function takes care of any wrapping of the circular buffer.
|
||||
//! The data remains on the circular buffer until circular_buffer_consume is
|
||||
//! called.
|
||||
//! @param buffer The circular buffer to copy from.
|
||||
//! @param start_offset Number of bytes of the source content to skip.
|
||||
//! @param[out] data_out The buffer to copy the data to.
|
||||
//! @param length The number of bytes to copy.
|
||||
//! @return The number of bytes copied.
|
||||
uint16_t circular_buffer_copy_offset(const CircularBuffer* buffer, uint16_t start_offset,
|
||||
uint8_t *data_out, uint16_t length);
|
||||
|
||||
//! Gets a pointer to a continuous byte array of the requested length from the circular buffer.
|
||||
//! In case the requested length wraps around the edges of the circular buffer, a heap-allocated
|
||||
//! copy is made.
|
||||
//! @param buffer The buffer to read or copy from.
|
||||
//! @param[out] data_ptr_out After returning, it will point to the byte array with the data. NOTE:
|
||||
//! This can be NULL in the case the malloc_imp wasn't able to allocate the buffer.
|
||||
//! @param[in] length The length of the data to read. If this is longer than what
|
||||
//! circular_buffer_get_read_space_remaining() returns, the function will return false.
|
||||
//! @param[in] malloc_imp The malloc() implementation to allocate a new buffer, in case if the
|
||||
//! requested length is non-contiguously stored.
|
||||
//! @param[out] caller_should_free Will be true after returning if the caller must call
|
||||
//! free(*data_ptr_out) at some point, because the data had been copied into a temporary
|
||||
//! heap-allocated buffer.
|
||||
//! @return false if there's less than length bytes in the buffer or if the copy failed because
|
||||
//! there was not enough memory.
|
||||
bool circular_buffer_read_or_copy(const CircularBuffer* buffer, uint8_t **data_out, size_t length,
|
||||
void *(*malloc_imp)(size_t), bool *caller_should_free);
|
||||
|
||||
//! Removes length bytes of the oldest data from the buffer.
|
||||
bool circular_buffer_consume(CircularBuffer* buffer, uint16_t length);
|
||||
|
||||
//! @return The number of bytes we can write before circular_buffer_write will return false.
|
||||
uint16_t circular_buffer_get_write_space_remaining(const CircularBuffer* buffer);
|
||||
|
||||
//! @return The number of bytes we can read before circular_buffer_read will return false.
|
||||
uint16_t circular_buffer_get_read_space_remaining(const CircularBuffer* buffer);
|
||||
|
64
src/libutil/includes/util/circular_cache.h
Normal file
64
src/libutil/includes/util/circular_cache.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/order.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! @note Needs to handle NULL items gracefully
|
||||
typedef void (*CircularCacheItemDestructor)(void *item);
|
||||
|
||||
//! Array-backed circular cache
|
||||
typedef struct {
|
||||
uint8_t* cache; //<! Pointer to the array
|
||||
size_t item_size; //<! Size of the array element in bytes
|
||||
int next_erased_item_idx; //<! Next array element to be deleted
|
||||
int total_items;
|
||||
Comparator compare_cb;
|
||||
CircularCacheItemDestructor item_destructor;
|
||||
} CircularCache;
|
||||
|
||||
void circular_cache_init(CircularCache* c, uint8_t* buffer, size_t item_size,
|
||||
int total_items, Comparator compare_cb);
|
||||
|
||||
//! Add a destructor to be called when an item is evicted from the circular cache.
|
||||
//! @note the destructor needs to handle NULL items gracefully
|
||||
void circular_cache_set_item_destructor(CircularCache *c, CircularCacheItemDestructor destructor);
|
||||
|
||||
//! @return True if the cache contains the data
|
||||
//! @note Item must be of size item_size
|
||||
bool circular_cache_contains(CircularCache* c, void* item);
|
||||
|
||||
//! @return Pointer to buffer of entry in cache that contains the data
|
||||
//! @note Item must be of size item_size
|
||||
void *circular_cache_get(CircularCache* c, void* theirs);
|
||||
|
||||
//! Push data of size item_size into the circular cache
|
||||
//! Overwrites the item at next_erased_item_idx
|
||||
void circular_cache_push(CircularCache* c, void* item);
|
||||
|
||||
//! Fills a circular cache with the representation an item, useful for non-zero clearing a cache
|
||||
//! @note this will assert if an item destructor is set
|
||||
void circular_cache_fill(CircularCache *c, uint8_t *item);
|
||||
|
||||
//! Flushes the buffer, calling destructors for each item in the cache. The calling module must
|
||||
//! be able to differentiate between a valid and invalid entry in the cache (e.g. the cache is not
|
||||
//! yet filled, so it has entries with zeroed out data).
|
||||
void circular_cache_flush(CircularCache *c);
|
67
src/libutil/includes/util/crc32.h
Normal file
67
src/libutil/includes/util/crc32.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! \file
|
||||
//! Calculate the CRC-32 checksum of data.
|
||||
//!
|
||||
//! The checksum is the standard CRC-32 used by zlib, PNG and others.
|
||||
//! The model parameters for the algorithm, as described in A Painless Guide to
|
||||
//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are:
|
||||
//! Name: "CRC-32"
|
||||
//! Width: 32
|
||||
//! Poly: 04C11DB7
|
||||
//! Init: FFFFFFFF
|
||||
//! RefIn: True
|
||||
//! RefOut: True
|
||||
//! XorOut: FFFFFFFF
|
||||
//! Check: CBF43926
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
//! Update a running CRC-32 checksum with the bytes of data and return the
|
||||
//! updated CRC-32. If data is NULL, the function returns the required initial
|
||||
//! value for the CRC.
|
||||
//!
|
||||
//! This function is drop-in compatible with zlib's crc32 function.
|
||||
//!
|
||||
//! \par Usage
|
||||
//! \code
|
||||
//! uint32_t crc = crc32(0, NULL, 0);
|
||||
//! while (read_buffer(data, length)) {
|
||||
//! crc = crc32(crc, data, length);
|
||||
//! }
|
||||
//! \endcode
|
||||
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length);
|
||||
|
||||
//! The initial CRC register value for a standard CRC-32 checksum.
|
||||
//!
|
||||
//! It is the same value as is returned by the `crc32` function when data is
|
||||
//! NULL.
|
||||
//!
|
||||
//! \code
|
||||
//! assert(CRC32_INIT == crc32(0, NULL, 0));
|
||||
//! \endcode
|
||||
#define CRC32_INIT (0)
|
||||
|
||||
//! The residue constant of the CRC-32 algorithm.
|
||||
//!
|
||||
//! If the CRC-32 value of a message is appended (little-endian) onto the
|
||||
//! end of the message, the CRC-32 of the concatenated message and CRC will be
|
||||
//! equal to CRC32_RESIDUE if the message has not been corrupted in transit.
|
||||
#define CRC32_RESIDUE (0x2144DF1C)
|
21
src/libutil/includes/util/hash.h
Normal file
21
src/libutil/includes/util/hash.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t hash(const uint8_t *bytes, const uint32_t length);
|
139
src/libutil/includes/util/heap.h
Normal file
139
src/libutil/includes/util/heap.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct _tagHeapInfo_t;
|
||||
typedef struct _tagHeapInfo_t HeapInfo_t;
|
||||
|
||||
typedef void (*LockFunction)(void*);
|
||||
typedef void (*UnlockFunction)(void*);
|
||||
|
||||
typedef struct HeapLockImpl {
|
||||
LockFunction lock_function;
|
||||
UnlockFunction unlock_function;
|
||||
void* lock_context;
|
||||
} HeapLockImpl;
|
||||
|
||||
typedef void (*DoubleFreeHandler)(void*);
|
||||
typedef void (*CorruptionHandler)(void*);
|
||||
|
||||
typedef struct Heap {
|
||||
// These HeapInfo_t structure pointers are initialized to the start and the end of the heap area.
|
||||
// The begin will point to the first block that's in the heap area, where the end is actually a
|
||||
// pointer to the first block *after* the heap area. This means the last block in the heap isn't
|
||||
// actually at end, but at end - begin->PrevSize.
|
||||
HeapInfo_t *begin;
|
||||
HeapInfo_t *end;
|
||||
|
||||
//! Number of allocated bytes, including beginers
|
||||
unsigned int current_size;
|
||||
//! Peak number of allocated bytes, including beginers
|
||||
unsigned int high_water_mark;
|
||||
|
||||
HeapLockImpl lock_impl;
|
||||
|
||||
DoubleFreeHandler double_free_handler;
|
||||
bool fuzz_on_free;
|
||||
|
||||
void *corrupt_block;
|
||||
CorruptionHandler corruption_handler;
|
||||
} Heap;
|
||||
|
||||
//! Initialize the heap inside the specified boundaries, zero-ing out the free
|
||||
//! list data structure.
|
||||
//! @note Assumes 0 is not a valid address for allocation.
|
||||
//! @param start The start of the heap will be the first int-aligned address >= start
|
||||
//! @param end The end of the heap will be the last sizeof(HeaderBlock) aligned
|
||||
//! address that is < end
|
||||
//! @param fuzz_on_free if true, memsets memory contents to junk values upon free in order to
|
||||
//! catch bad accesses more quickly
|
||||
void heap_init(Heap* const heap, void* start, void* end, bool fuzz_on_free);
|
||||
|
||||
//! Configure this heap for thread safety using the given locking implementation
|
||||
void heap_set_lock_impl(Heap *heap, HeapLockImpl lock_impl);
|
||||
|
||||
//! Configure the heap with a pointer that gets called when a double free is detected.
|
||||
//! If this isn't configured on a heap, the default behaviour is to trigger a PBL_CROAK.
|
||||
void heap_set_double_free_handler(Heap *heap, DoubleFreeHandler double_free_handler);
|
||||
|
||||
//! Configure the heap with a pointer that gets called when (and if) corruption is detected.
|
||||
//! If this isn't configured on a heap, the default behaviour is to trigger a PBL_CROAK.
|
||||
void heap_set_corruption_handler(Heap *heap, CorruptionHandler corruption_handler);
|
||||
|
||||
//! Allocate a fragment of memory on the given heap. Tries to avoid
|
||||
//! fragmentation by obtaining memory requests larger than LARGE_SIZE from the
|
||||
//! endo of the buffer, while small fragments are taken from the start of the
|
||||
//! buffer.
|
||||
//! @note heap_init() must be called prior to using heap_malloc().
|
||||
//! @param nbytes Number of bytes to be allocated. Must be > 0.
|
||||
//! @param client_pc The PC register of the client who caused this malloc. Only used when
|
||||
//! MALLOC_INSTRUMENTATION is defined.
|
||||
//! @return A pointer to the start of the allocated memory
|
||||
void* heap_malloc(Heap* const heap, unsigned long nbytes, uintptr_t client_pc);
|
||||
|
||||
//! Return memory to free list. Where possible, make contiguous blocks of free
|
||||
//! memory. The function tries to verify that the structure is a valid fragment
|
||||
//! structure before the memory is freed. When a fragment is freed, adjacent
|
||||
//! free fragments may be combined.
|
||||
//! @note heap_init() must be called prior to using heap_free().
|
||||
//! otherwise, the free list will be NULL.)
|
||||
//! @note Assumes that 0 is not a valid address for allocation.
|
||||
void heap_free(Heap* const heap, void* ptr, uintptr_t client_pc);
|
||||
|
||||
//! Allocate a new block of the given size, and copy over the data at ptr into
|
||||
//! the new block. If the new size is smaller than the old size, will only copy
|
||||
//! over as much data as possible. Frees ptr.
|
||||
//! @param ptr Points to the memory region to re-allocate.
|
||||
//! @param nbytes The total number of bytes to allocate.
|
||||
//! @param client_pc The PC register of the client who caused this malloc. Only used when
|
||||
//! MALLOC_INSTRUMENTATION is defined.
|
||||
void* heap_realloc(Heap* const heap, void *ptr, unsigned long nbytes, uintptr_t client_pc);
|
||||
|
||||
//! Allocate a buffer to hold anything. The initial contents of the buffer
|
||||
//! are zero'd.
|
||||
void* heap_zalloc(Heap* const heap, size_t size, uintptr_t client_pc);
|
||||
|
||||
//! Allocate a buffer to hold an array of count elements, each of size size (in bytes)
|
||||
//! and initializes all bits to zero.
|
||||
void* heap_calloc(Heap* const heap, size_t count, size_t size, uintptr_t client_pc);
|
||||
|
||||
//! @return True if ptr is on the given heap, false otherwise.
|
||||
bool heap_contains_address(Heap* const heap, void* ptr);
|
||||
|
||||
//! @return True if ptr is allocated on the given heap, false otherwise.
|
||||
bool heap_is_allocated(Heap* const heap, void* ptr);
|
||||
|
||||
//! @return The size of the heap in bytes
|
||||
size_t heap_size(const Heap *heap);
|
||||
|
||||
//! @return the fewest amount of bytes that the given heap had free
|
||||
uint32_t heap_get_minimum_headroom(Heap *heap);
|
||||
|
||||
//! Used for debugging.
|
||||
//! Calculates and outputs the current memory usage on the given heap.
|
||||
//! @param used Output, will contain the number of bytes currently
|
||||
//! allocated and in use.
|
||||
//! @param free Output, will contain the number of unallocated bytes.
|
||||
//! @param max_free Output, will contain size of the largest unallocated
|
||||
//! fragment.
|
||||
void heap_calc_totals(Heap* const heap, unsigned int *used, unsigned int *free, unsigned int *max_free);
|
||||
|
||||
void heap_dump_malloc_instrumentation_to_dbgserial(Heap *heap);
|
29
src/libutil/includes/util/hexdump.h
Normal file
29
src/libutil/includes/util/hexdump.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*HexdumpLineCallback)(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer);
|
||||
|
||||
//! Hexdumps data in xxd-style formatting, by repeatedly calling write_line_cb for each line.
|
||||
//! @note The line_buffer that is passed does not end with any newline characters.
|
||||
void hexdump(const char *src_filename, int src_line_number, int level,
|
||||
const uint8_t *data, size_t length, HexdumpLineCallback write_line_cb);
|
44
src/libutil/includes/util/iterator.h
Normal file
44
src/libutil/includes/util/iterator.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//! Simple utility for enforcing consistent use of the iterator pattern
|
||||
//! and facilitate unit testing.
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void* IteratorState;
|
||||
|
||||
typedef bool (*IteratorCallback)(IteratorState state);
|
||||
|
||||
typedef struct {
|
||||
IteratorCallback next;
|
||||
IteratorCallback prev;
|
||||
IteratorState state;
|
||||
} Iterator;
|
||||
|
||||
#define ITERATOR_EMPTY ((Iterator){ 0, 0, 0 })
|
||||
|
||||
void iter_init(Iterator* iter, IteratorCallback next, IteratorCallback prev, IteratorState state);
|
||||
|
||||
//! @return true if successfully moved to next node
|
||||
bool iter_next(Iterator* iter);
|
||||
|
||||
//! @return true if successfully moved to previous node
|
||||
bool iter_prev(Iterator* iter);
|
||||
|
||||
IteratorState iter_get_state(Iterator* iter);
|
||||
|
46
src/libutil/includes/util/keyed_circular_cache.h
Normal file
46
src/libutil/includes/util/keyed_circular_cache.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/order.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t KeyedCircularCacheKey;
|
||||
|
||||
//! Array-backed circular cache, optimized for data cache efficiency
|
||||
typedef struct {
|
||||
KeyedCircularCacheKey *cache_keys;
|
||||
uint8_t *cache_data; //<! Pointer to the array
|
||||
size_t item_size; //<! Size of the array element in bytes
|
||||
size_t next_item_to_erase_idx; //<! Next array element to be deleted
|
||||
size_t total_items;
|
||||
} KeyedCircularCache;
|
||||
|
||||
void keyed_circular_cache_init(KeyedCircularCache *c, KeyedCircularCacheKey *key_buffer,
|
||||
void *data_buffer, size_t item_size, size_t total_items);
|
||||
|
||||
//! @return Pointer to buffer of entry in cache that contains the data
|
||||
//! @note Item must be of size item_size
|
||||
void *keyed_circular_cache_get(KeyedCircularCache *c, KeyedCircularCacheKey key);
|
||||
|
||||
//! Push data of size item_size into the circular cache
|
||||
//! Overwrites the item at next_item_to_erase_idx
|
||||
void keyed_circular_cache_push(KeyedCircularCache *c, KeyedCircularCacheKey key,
|
||||
const void *item);
|
20
src/libutil/includes/util/likely.h
Normal file
20
src/libutil/includes/util/likely.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define LIKELY(x) __builtin_expect((x),1)
|
||||
#define UNLIKELY(x) __builtin_expect((x),0)
|
156
src/libutil/includes/util/list.h
Normal file
156
src/libutil/includes/util/list.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "order.h"
|
||||
|
||||
typedef struct ListNode {
|
||||
struct ListNode* next;
|
||||
struct ListNode* prev;
|
||||
} ListNode;
|
||||
|
||||
typedef bool (*ListFilterCallback)(ListNode *found_node, void *data);
|
||||
|
||||
//! - If a callback returns true, the iteration continues
|
||||
//! - If a callback returns false, the ieration stops.
|
||||
typedef bool (*ListForEachCallback)(ListNode *node, void *context);
|
||||
|
||||
#define LIST_NODE_NULL { .next = NULL, .prev = NULL }
|
||||
|
||||
//! Initializes the node.
|
||||
void list_init(ListNode *head);
|
||||
|
||||
//! Inserts new_node after node in the list.
|
||||
//! Always returns one of the two nodes that is closest to, or is the tail of the list.
|
||||
ListNode* list_insert_after(ListNode *node, ListNode *new_node);
|
||||
|
||||
//! Inserts new_node before node in the list.
|
||||
//! Always returns one of the two nodes that is closest to, or is the head of the list.
|
||||
//! Warning: Returns new_node, rather than the new head of the list
|
||||
//! as you might expect.
|
||||
ListNode* list_insert_before(ListNode *node, ListNode *new_node);
|
||||
|
||||
//! Removes the head of the list and returns the new head.
|
||||
ListNode* list_pop_head(ListNode *node);
|
||||
|
||||
//! Removes the tail of the list and returns the new tail.
|
||||
ListNode* list_pop_tail(ListNode *node);
|
||||
|
||||
//! Removed the node from the list.
|
||||
//! @param node the ListNode to remove.
|
||||
//! @param[in,out] *head will be updated if the removed node happens to be the head
|
||||
//! @param[in,out] *tail will be updated if the removed node happens to be the tail.
|
||||
//! @note head and tail parameters are optional. Pass in NULL if not used.
|
||||
void list_remove(ListNode *node, ListNode **head, ListNode **tail);
|
||||
|
||||
//! Appends new_node to the tail of the list that node is part of.
|
||||
//! @param node Any node in the list, can be NULL (will result in a list containing only new_node)
|
||||
//! Always returns the tail of the list.
|
||||
ListNode* list_append(ListNode *node, ListNode *new_node);
|
||||
|
||||
//! Appends new_node to the head of the list that node is part of.
|
||||
//! @param node Any node in the list, can be NULL (will result in a list containing only new_node)
|
||||
//! Always returns the head of the list.
|
||||
ListNode* list_prepend(ListNode *node, ListNode *new_node);
|
||||
|
||||
//! Gets the next node
|
||||
ListNode* list_get_next(ListNode *node);
|
||||
|
||||
//! Gets the previous node
|
||||
ListNode* list_get_prev(ListNode *node);
|
||||
|
||||
//! Gets the last node in the list
|
||||
ListNode* list_get_tail(ListNode *node);
|
||||
|
||||
//! Gets the first node in the list
|
||||
ListNode* list_get_head(ListNode *node);
|
||||
|
||||
//! @return true if the passed in node is the head of a list.
|
||||
bool list_is_head(const ListNode *node);
|
||||
|
||||
//! @return true if the passed in node is the tail of a list.
|
||||
bool list_is_tail(const ListNode *node);
|
||||
|
||||
//! Counts the number of nodes from node to the tail of the list, including said node
|
||||
uint32_t list_count_to_tail_from(ListNode *node);
|
||||
|
||||
//! Counts the number of nodes from node to the head of the list, including said node
|
||||
uint32_t list_count_to_head_from(ListNode *node);
|
||||
|
||||
//! Counts the number of nodes from head to tail
|
||||
uint32_t list_count(ListNode *node);
|
||||
|
||||
//! Gets the node at <index> away, where positive index is towards the tail
|
||||
ListNode* list_get_at(ListNode *node, int32_t index);
|
||||
|
||||
//! Adds a node to a list ordered by given comparator.
|
||||
//! @param[in] head The head of the list that we want to add to.
|
||||
//! @param[in] new_node The node being added.
|
||||
//! @param[in] comparator The comparison function to use
|
||||
//! @param[in] ascending True to maintain the list ordered ascending from head to tail.
|
||||
//! @returns The (new) head of the list.
|
||||
//! @note This function will not sort existing nodes in the list.
|
||||
ListNode* list_sorted_add(ListNode *head, ListNode *new_node, Comparator comparator, bool ascending);
|
||||
|
||||
//! @param[in] head The head of the list to search.
|
||||
//! @param[in] node The node to search for.
|
||||
//! @returns True if the list contains node
|
||||
bool list_contains(const ListNode *head, const ListNode *node);
|
||||
|
||||
//! Gets the first node that conforms to the given filter callback
|
||||
//! @param node The list node from which to depart the search
|
||||
//! @param filter_callback A function returning true in case the node that is passed in matches the
|
||||
//! filter criteria, and false if it doesn't and should be skipped.
|
||||
//! @param found_node The node to be evaluated by the filter callback
|
||||
//! @param data Optional callback data
|
||||
ListNode* list_find(ListNode *node, ListFilterCallback filter_callback, void *data);
|
||||
|
||||
//! Gets the next node that conforms to the given filter callback
|
||||
//! @param node The list node from which to depart the search
|
||||
//! @param filter_callback A function returning true in case the node that is passed in matches the
|
||||
//! filter criteria, and false if it doesn't and should be skipped.
|
||||
//! @param found_node The node to be evaluated by the filter callback
|
||||
//! @param wrap_around True if the search should continue from the head if the tail has been reached
|
||||
//! @param data Optional callback data
|
||||
ListNode* list_find_next(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data);
|
||||
|
||||
//! Gets the previous node that conforms to the given filter callback
|
||||
//! @param node The list node from which to depart the search
|
||||
//! @param filter_callback A function returning true in case the node that is passed in matches the
|
||||
//! filter criteria, and false if it doesn't and should be skipped.
|
||||
//! @param found_node The node to be evaluated by the filter callback
|
||||
//! @param wrap_around True if the search should continue from the tail if the head has been reached
|
||||
//! @param data Optional callback data
|
||||
ListNode* list_find_prev(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data);
|
||||
|
||||
//! Concatenate two lists.
|
||||
//! @param list_a list onto which to concatenate list_b
|
||||
//! @param list_b list to concatenate onto list_a
|
||||
//! @return head of the new list
|
||||
ListNode* list_concatenate(ListNode *list_a, ListNode *list_b);
|
||||
|
||||
//! Iterates over each node and passes it into callback given
|
||||
//! @param[in] head The head of the list that we want to iterate over.
|
||||
//! @param[in] each_cb The callback function to pass each node into
|
||||
//! @param[in] data Optional callback data
|
||||
void list_foreach(ListNode *head, ListForEachCallback each_cb, void *context);
|
||||
|
||||
//! Dump a list to PBL_LOG
|
||||
void list_debug_dump(ListNode *head);
|
||||
|
26
src/libutil/includes/util/logging.h
Normal file
26
src/libutil/includes/util/logging.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void util_log(const char *filename, int line, const char *string);
|
||||
|
||||
#define UTIL_LOG(string) \
|
||||
do { \
|
||||
util_log(__FILE_NAME__, __LINE__, string); \
|
||||
} while (0)
|
||||
|
||||
void util_dbgserial_str(const char *string);
|
101
src/libutil/includes/util/math.h
Normal file
101
src/libutil/includes/util/math.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define ABS(a) (((a) > 0) ? (a) : -1 * (a))
|
||||
#define CLIP(n, min, max) ((n)<(min)?(min):((n)>(max)?(max):(n)))
|
||||
#define ROUND(num, denom) (((num) + ((denom) / 2))/(denom))
|
||||
#define WITHIN(n, min, max) ((n) >= (min) && (n) <= (max))
|
||||
#define RANGE_WITHIN(n_min, n_max, min, max) ((n_min) >= (min) && (n_max) <= (max))
|
||||
|
||||
// Divide num by denom, rounding up (ceil(0.5) is 1.0, and ceil(-0.5) is 0.0)
|
||||
// ex. 3, 4 (ie. 3/4) : returns 1
|
||||
// ex. -3, 4 : returns 0
|
||||
#define DIVIDE_CEIL(num, denom) (((num) + ((denom) - 1)) / (denom))
|
||||
|
||||
// Round value up to the next increment of modulus
|
||||
// ex. val = 152 mod = 32 : returns 160
|
||||
// val = -32 mod = 90 : returns -90
|
||||
#define ROUND_TO_MOD_CEIL(val, mod) \
|
||||
((val >= 0) ? \
|
||||
((((val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)) : \
|
||||
-((((-val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)))
|
||||
|
||||
int32_t sign_extend(uint32_t a, int bits);
|
||||
|
||||
//! Calculates the distance (end - start), taking a roll-over into account as good as it can get.
|
||||
int32_t serial_distance32(uint32_t start, uint32_t end);
|
||||
|
||||
//! Calculates the distance (end - start), taking a roll-over into account as good as it can get.
|
||||
//! @param bits the number of bits that are valid in start and end.
|
||||
int32_t serial_distance(uint32_t start, uint32_t end, int bits);
|
||||
|
||||
/*
|
||||
* find the log base two of a number rounded up
|
||||
*/
|
||||
int ceil_log_two(uint32_t n);
|
||||
|
||||
//! newton's method for floor(sqrt(x)) -> should always converge
|
||||
int32_t integer_sqrt(int64_t x);
|
||||
|
||||
/*
|
||||
* The -Wtype-limits flag generated an error with the previous IS_SIGNED maco.
|
||||
* If an unsigned number was passed in the macro would check if the unsigned number was less than 0.
|
||||
*/
|
||||
//! Determine whether a variable is signed or not.
|
||||
//! @param var The variable to evaluate.
|
||||
//! @return true if the variable is signed.
|
||||
#define IS_SIGNED(var) (__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned char), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned short), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned int), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned long), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \
|
||||
)
|
||||
|
||||
// http://stackoverflow.com/questions/14997165/fastest-way-to-get-a-positive-modulo-in-c-c
|
||||
static inline int positive_modulo(int i, int n) {
|
||||
return (i % n + n) % n;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/1878907/the-smallest-difference-between-2-angles
|
||||
static inline int distance_to_mod_boundary(int32_t i, uint16_t n) {
|
||||
const int mod = positive_modulo(i, n);
|
||||
const int half = n / 2;
|
||||
return ABS((mod + half) % n - half);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the next backoff interval using a bounded binary expoential backoff formula.
|
||||
*
|
||||
* @param[in,out] attempt The number of retries performed so far. This count will be incremented by the function.
|
||||
* @param[in] initial_value The inital backoff interval. Subsequent backoff attempts will be this number multiplied by a power of 2.
|
||||
* @param[in] max_value The maximum backoff interval that returned by the function.
|
||||
* @return The next backoff interval.
|
||||
*/
|
||||
uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value);
|
||||
|
||||
//! Find the greatest common divisor of two numbers.
|
||||
uint32_t gcd(uint32_t a, uint32_t b);
|
188
src/libutil/includes/util/math_fixed.h
Normal file
188
src/libutil/includes/util/math_fixed.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Fixed_S16_3 = 1 bit sign, 12 bits integer, 3 bits fraction
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Note the fraction is unsigned and represents a positive addition
|
||||
// to the integer. So for example:
|
||||
// The value -1.125 will be stored as (-2 + 7*0.125) ==> integer = -2, fraction = 7
|
||||
// The value 1.125 will be stored as (1 + 1*0.125) ==> integer = 1, fraction = 1
|
||||
// This representation allows for direct addition/multiplication between numbers to happen
|
||||
// without any complicated logic.
|
||||
// The same representation for negative numbers applies for all fixed point representations
|
||||
// in this file (i.e. fraction component is a positive addition to the integer).
|
||||
typedef union __attribute__ ((__packed__)) Fixed_S16_3 {
|
||||
int16_t raw_value;
|
||||
struct {
|
||||
uint16_t fraction:3;
|
||||
int16_t integer:13;
|
||||
};
|
||||
} Fixed_S16_3;
|
||||
|
||||
#define Fixed_S16_3(raw) ((Fixed_S16_3){ .raw_value = (raw) })
|
||||
#define FIXED_S16_3_PRECISION 3
|
||||
#define FIXED_S16_3_FACTOR (1 << FIXED_S16_3_PRECISION)
|
||||
|
||||
#define FIXED_S16_3_ZERO ((Fixed_S16_3){ .integer = 0, .fraction = 0 })
|
||||
#define FIXED_S16_3_ONE ((Fixed_S16_3){ .integer = 1, .fraction = 0 })
|
||||
#define FIXED_S16_3_HALF ((Fixed_S16_3){ .raw_value = FIXED_S16_3_ONE.raw_value / 2 })
|
||||
|
||||
static __inline__ Fixed_S16_3 Fixed_S16_3_mul(Fixed_S16_3 a, Fixed_S16_3 b) {
|
||||
return Fixed_S16_3(((int32_t) a.raw_value * b.raw_value) >> FIXED_S16_3_PRECISION);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S16_3 Fixed_S16_3_add(Fixed_S16_3 a, Fixed_S16_3 b) {
|
||||
return Fixed_S16_3(a.raw_value + b.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S16_3 Fixed_S16_3_sub(Fixed_S16_3 a, Fixed_S16_3 b) {
|
||||
return Fixed_S16_3(a.raw_value - b.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S16_3 Fixed_S16_3_add3(Fixed_S16_3 a, Fixed_S16_3 b, Fixed_S16_3 c) {
|
||||
return Fixed_S16_3(a.raw_value + b.raw_value + c.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ bool Fixed_S16_3_equal(Fixed_S16_3 a, Fixed_S16_3 b) {
|
||||
return (a.raw_value == b.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ int16_t Fixed_S16_3_rounded_int(Fixed_S16_3 a) {
|
||||
const int16_t delta = a.raw_value >= 0 ? FIXED_S16_3_HALF.raw_value : -FIXED_S16_3_HALF.raw_value;
|
||||
return (a.raw_value + delta) / FIXED_S16_3_FACTOR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Fixed_S32_16 = 1 bit sign, 15 bits integer, 16 bits fraction
|
||||
////////////////////////////////////////////////////////////////
|
||||
typedef union __attribute__ ((__packed__)) Fixed_S32_16 {
|
||||
int32_t raw_value;
|
||||
struct {
|
||||
uint16_t fraction:16;
|
||||
int16_t integer:16;
|
||||
};
|
||||
} Fixed_S32_16;
|
||||
|
||||
//! Work-around for function pointer return type Fixed_S32_16 to avoid
|
||||
//! tripping the pre-processor to use the equally named Fixed_S32_16 define
|
||||
typedef Fixed_S32_16 Fixed_S32_16Return;
|
||||
|
||||
#define Fixed_S32_16(raw) ((Fixed_S32_16){ .raw_value = (raw) })
|
||||
#define FIXED_S32_16_PRECISION 16
|
||||
|
||||
#define FIXED_S32_16_ONE ((Fixed_S32_16){ .integer = 1, .fraction = 0 })
|
||||
#define FIXED_S32_16_ZERO ((Fixed_S32_16){ .integer = 0, .fraction = 0 })
|
||||
|
||||
static __inline__ Fixed_S32_16 Fixed_S32_16_mul(Fixed_S32_16 a, Fixed_S32_16 b) {
|
||||
Fixed_S32_16 x;
|
||||
|
||||
x.raw_value = (int32_t)((((int64_t) a.raw_value * (int64_t) b.raw_value)) >>
|
||||
FIXED_S32_16_PRECISION);
|
||||
return x;
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S32_16 Fixed_S32_16_add(Fixed_S32_16 a, Fixed_S32_16 b) {
|
||||
return Fixed_S32_16(a.raw_value + b.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S32_16 Fixed_S32_16_add3(Fixed_S32_16 a, Fixed_S32_16 b, Fixed_S32_16 c) {
|
||||
return Fixed_S32_16(a.raw_value + b.raw_value + c.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S32_16 Fixed_S32_16_sub(Fixed_S32_16 a, Fixed_S32_16 b) {
|
||||
return Fixed_S32_16(a.raw_value - b.raw_value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Fixed_S64_32 = 1 bit sign, 31 bits integer, 32 bits fraction
|
||||
////////////////////////////////////////////////////////////////
|
||||
typedef union __attribute__ ((__packed__)) Fixed_S64_32 {
|
||||
int64_t raw_value;
|
||||
struct {
|
||||
uint32_t fraction:32;
|
||||
int32_t integer:32;
|
||||
};
|
||||
} Fixed_S64_32;
|
||||
|
||||
#define FIXED_S64_32_PRECISION 32
|
||||
|
||||
#define FIXED_S64_32_ONE ((Fixed_S64_32){ .integer = 1, .fraction = 0 })
|
||||
#define FIXED_S64_32_ZERO ((Fixed_S64_32){ .integer = 0, .fraction = 0 })
|
||||
|
||||
#define FIXED_S64_32_FROM_RAW(raw) ((Fixed_S64_32){ .raw_value = (raw) })
|
||||
#define FIXED_S64_32_FROM_INT(x) ((Fixed_S64_32){ .integer = x, .fraction = 0 })
|
||||
#define FIXED_S64_32_TO_INT(x) (x.integer)
|
||||
|
||||
static __inline__ Fixed_S64_32 Fixed_S64_32_mul(Fixed_S64_32 a, Fixed_S64_32 b) {
|
||||
Fixed_S64_32 result;
|
||||
result.raw_value = (((uint64_t)(a.integer * b.integer)) << 32)
|
||||
+ ((((uint64_t)a.fraction) * ((uint64_t)b.fraction)) >> 32)
|
||||
+ ((a.integer) * ((uint64_t)b.fraction))
|
||||
+ (((uint64_t)a.fraction) * (b.integer));
|
||||
return result;
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S64_32 Fixed_S64_32_add(Fixed_S64_32 a, Fixed_S64_32 b) {
|
||||
return FIXED_S64_32_FROM_RAW(a.raw_value + b.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S64_32 Fixed_S64_32_add3(Fixed_S64_32 a, Fixed_S64_32 b, Fixed_S64_32 c) {
|
||||
return FIXED_S64_32_FROM_RAW(a.raw_value + b.raw_value + c.raw_value);
|
||||
}
|
||||
|
||||
static __inline__ Fixed_S64_32 Fixed_S64_32_sub(Fixed_S64_32 a, Fixed_S64_32 b) {
|
||||
return FIXED_S64_32_FROM_RAW(a.raw_value - b.raw_value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// Mixed operations
|
||||
////////////////////////////////////////////////////////////////
|
||||
// This function muliples a Fixed_S16_3 and Fixed_S32_16 and returns result in Fixed_S16_3 format
|
||||
static __inline__ Fixed_S16_3 Fixed_S16_3_S32_16_mul(Fixed_S16_3 a, Fixed_S32_16 b) {
|
||||
return Fixed_S16_3( a.raw_value * b.raw_value >> FIXED_S32_16_PRECISION );
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
/// High level math functions and filters
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Run x through a linear recursive filter. See https://en.wikipedia.org/wiki/Digital_biquad_filter
|
||||
//! for example of a 2nd order recursive filter. This function implements a generic Nth order one.
|
||||
//! @param[in] x the next input value, x[n]
|
||||
//! @param[in] num_input_coefficients the number of taps on the input side
|
||||
//! @param[in] num_output_coefficients the number of taps on the output side
|
||||
//! @param[in] cb pointer to array of input side coefficients. Must be an array of size
|
||||
//! num_input_coefficients
|
||||
//! @param[in] ca pointer to array of output side coefficients. Must be an array of size
|
||||
//! num_output_coefficients
|
||||
//! @param[in|out] state_x pointer to array to hold the history of x. Must be an array
|
||||
//! of size num_input_coefficients
|
||||
//! @param[in|out] state_y pointer to array to hold the history of y. Must be an array
|
||||
//! of size num_output_coefficients
|
||||
//! @return the filtered output value, y[n]
|
||||
Fixed_S64_32 math_fixed_recursive_filter(Fixed_S64_32 x,
|
||||
int num_input_coefficients, int num_output_coefficients,
|
||||
const Fixed_S64_32 *cb, const Fixed_S64_32 *ca,
|
||||
Fixed_S64_32 *state_x, Fixed_S64_32 *state_y);
|
25
src/libutil/includes/util/order.h
Normal file
25
src/libutil/includes/util/order.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
//! A Comparator returns the Order in which (a, b) occurs
|
||||
//! @return negative int for a descending value (a > b), positive for an ascending value (b > a), 0 for equal
|
||||
typedef int (*Comparator)(void *a, void *b);
|
||||
|
||||
int uint32_comparator(void *a, void *b);
|
21
src/libutil/includes/util/rand32.h
Normal file
21
src/libutil/includes/util/rand32.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
extern uint32_t rand32(void);
|
31
src/libutil/includes/util/size.h
Normal file
31
src/libutil/includes/util/size.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! Calculate the length of an array, based on the size of the element type.
|
||||
//! @param array The array to be evaluated.
|
||||
//! @return The length of the array.
|
||||
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
|
||||
|
||||
//! Calculate the length of a literal array based on the size of the given type
|
||||
//! This is usable in contexts that require compile time constants
|
||||
//! @param type Type of the elements
|
||||
//! @param array Literal definition of the array
|
||||
//! @return Length of the array in bytes
|
||||
#define STATIC_ARRAY_LENGTH(type, array) (sizeof((type[]) array) / sizeof(type))
|
||||
|
||||
#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
|
30
src/libutil/includes/util/sort.h
Normal file
30
src/libutil/includes/util/sort.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//! Standard sort comparator function
|
||||
typedef int (*SortComparator)(const void *, const void *);
|
||||
|
||||
//! Bubble sorts an array
|
||||
//! @param[in] array The array that should be sorted
|
||||
//! @param[in] num_elem Number of elements in the array
|
||||
//! @param[in] elem_size Size of each element in the array
|
||||
//! @param[in] comp SortComparator comparator function
|
||||
void sort_bubble(void *array, size_t num_elem, size_t elem_size, SortComparator comp);
|
72
src/libutil/includes/util/string.h
Normal file
72
src/libutil/includes/util/string.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
// For some reason the string.h that newlib generates doesn't have this definition,
|
||||
// but if you just extern this the libc still has the right symbol. Weirdddd.
|
||||
size_t strnlen(const char *, size_t);
|
||||
|
||||
const char *string_strip_leading_whitespace(const char * string);
|
||||
|
||||
void string_strip_trailing_whitespace(const char *string, char *string_out);
|
||||
|
||||
#define IS_EMPTY_STRING(s) (s[0] == '\0')
|
||||
|
||||
// Stolen from http://stackoverflow.com/a/8488201
|
||||
#define GET_FILE_NAME(file) (strrchr(file, '/') ? (strrchr(file, '/') + 1) : (file))
|
||||
|
||||
//! Converts an unsigned integer value to a null-terminated hex-value string and stores the result
|
||||
//! in buffer.
|
||||
void itoa(uint32_t num, char *buffer, int buffer_length);
|
||||
|
||||
//! Converts a signed integer value to a null-terminated string and stores the result in buffer.
|
||||
//! NOTE: Buffer must be long enough to fit a string 12 bytes long.
|
||||
void itoa_int(int n, char *str, int base);
|
||||
|
||||
//! Reverses a string in place
|
||||
void string_reverse(char *str);
|
||||
|
||||
uintptr_t str_to_address(const char *address_str);
|
||||
|
||||
const char *bool_to_str(bool b);
|
||||
|
||||
//! @param hex 12-digit hex string representing a BT address
|
||||
//! @param addr Points to a SS1 BD_ADDR_t as defined in BTBTypes.h
|
||||
//! @return True on success
|
||||
bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size);
|
||||
|
||||
//! Concatenates a simple string and a number.
|
||||
//! NOTE: Buffer must be long enough to fit the largest number value (12 bytes) and the string, plus
|
||||
//! A null-terminated character
|
||||
void concat_str_int(const char *str, uint32_t num, char *buf, uint8_t buf_len);
|
||||
|
||||
//! Convert an ASCII string to uppercase
|
||||
void toupper_str(char *str);
|
||||
|
||||
//! Converts a byte stream to a hex string, i.e ({0xaa, 0xbb, 0xcc} -> "aabbcc")
|
||||
void byte_stream_to_hex_string(char *out_buf, size_t out_buf_len,
|
||||
const uint8_t *byte_stream, size_t byte_stream_len, bool stream_backward);
|
||||
|
||||
//! Appends the src string to dst, taking the overall size of the dst buffer into account
|
||||
//! @param dst Destination string to append to
|
||||
//! @param src String to append
|
||||
//! @param dst_space Total size of dst buffer
|
||||
void safe_strcat(char* dst, const char* src, int dst_space);
|
21
src/libutil/includes/util/struct.h
Normal file
21
src/libutil/includes/util/struct.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! Returns `struct_ptr->field_name` if `struct_ptr` isn't NULL, otherwise returns default_value
|
||||
#define NULL_SAFE_FIELD_ACCESS(struct_ptr, field_name, default_value) \
|
||||
((struct_ptr) ? ((struct_ptr)->field_name) : (default_value))
|
73
src/libutil/includes/util/trig.h
Normal file
73
src/libutil/includes/util/trig.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
//! @file trig.h
|
||||
//!
|
||||
//! @addtogroup Foundation
|
||||
//! @{
|
||||
//! @addtogroup Math
|
||||
//! @{
|
||||
|
||||
//! The largest value that can result from a call to \ref sin_lookup or \ref cos_lookup.
|
||||
//! For a code example, see the detailed description at the top of this chapter: \ref Math
|
||||
#define TRIG_MAX_RATIO 0xffff
|
||||
|
||||
//! Angle value that corresponds to 360 degrees or 2 PI radians
|
||||
//! @see \ref sin_lookup
|
||||
//! @see \ref cos_lookup
|
||||
#define TRIG_MAX_ANGLE 0x10000
|
||||
|
||||
//! Angle value that corresponds to 180 degrees or PI radians
|
||||
//! @see \ref sin_lookup
|
||||
//! @see \ref cos_lookup
|
||||
#define TRIG_PI 0x8000
|
||||
|
||||
#define TRIG_FP 16
|
||||
|
||||
//! Converts from a fixed point value representation to the equivalent value in degrees
|
||||
//! @see DEG_TO_TRIGANGLE
|
||||
//! @see TRIG_MAX_ANGLE
|
||||
#define TRIGANGLE_TO_DEG(trig_angle) (((trig_angle) * 360) / TRIG_MAX_ANGLE)
|
||||
|
||||
//! Converts from an angle in degrees to the equivalent fixed point value representation
|
||||
//! @see TRIGANGLE_TO_DEG
|
||||
//! @see TRIG_MAX_ANGLE
|
||||
#define DEG_TO_TRIGANGLE(angle) (((angle) * TRIG_MAX_ANGLE) / 360)
|
||||
|
||||
//! Look-up the sine of the given angle from a pre-computed table.
|
||||
//! @param angle The angle for which to compute the cosine.
|
||||
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
|
||||
int32_t sin_lookup(int32_t angle);
|
||||
|
||||
//! Look-up the cosine of the given angle from a pre-computed table.
|
||||
//! This is equivalent to calling `sin_lookup(angle + TRIG_MAX_ANGLE / 4)`.
|
||||
//! @param angle The angle for which to compute the cosine.
|
||||
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
|
||||
int32_t cos_lookup(int32_t angle);
|
||||
|
||||
//! Look-up the arctangent of a given x, y pair
|
||||
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
|
||||
int32_t atan2_lookup(int16_t y, int16_t x);
|
||||
|
||||
//! @internal
|
||||
//! Normalize an angle to the range [0, TRIG_MAX_ANGLE]
|
||||
uint32_t normalize_angle(int32_t angle);
|
||||
|
||||
//! @} // group Math
|
||||
//! @} // group Foundation
|
91
src/libutil/includes/util/uuid.h
Normal file
91
src/libutil/includes/util/uuid.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/list.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define UUID_SIZE 16
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t byte0;
|
||||
uint8_t byte1;
|
||||
uint8_t byte2;
|
||||
uint8_t byte3;
|
||||
uint8_t byte4;
|
||||
uint8_t byte5;
|
||||
uint8_t byte6;
|
||||
uint8_t byte7;
|
||||
uint8_t byte8;
|
||||
uint8_t byte9;
|
||||
uint8_t byte10;
|
||||
uint8_t byte11;
|
||||
uint8_t byte12;
|
||||
uint8_t byte13;
|
||||
uint8_t byte14;
|
||||
uint8_t byte15;
|
||||
} Uuid;
|
||||
|
||||
#define UUID_SYSTEM {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define UUID_INVALID_INIT {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
#define UUID_INVALID ((const Uuid) UUID_INVALID_INIT)
|
||||
|
||||
//! Make a Uuid object from sixteen bytes.
|
||||
//! @return A Uuid structure representing the bytes p0 to p15.
|
||||
#define UuidMake(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) ((Uuid) {p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15})
|
||||
|
||||
//! Creates a Uuid from an array of bytes with 16 bytes in Big Endian order.
|
||||
//! @return The created Uuid
|
||||
#define UuidMakeFromBEBytes(b) ((Uuid) { b[0], b[1], b[2], b[3], \
|
||||
b[4], b[5], b[6], b[7], \
|
||||
b[8], b[9], b[10], b[11], \
|
||||
b[12], b[13], b[14], b[15] })
|
||||
|
||||
//! Creates a Uuid from an array of bytes with 16 bytes in Little Endian order.
|
||||
//! @return The created Uuid
|
||||
#define UuidMakeFromLEBytes(b) ((Uuid) { b[15], b[14], b[13], b[12], \
|
||||
b[11], b[10], b[9], b[8], \
|
||||
b[7], b[6], b[5], b[4], \
|
||||
b[3], b[2], b[1], b[0] })
|
||||
|
||||
//! Compares two UUIDs.
|
||||
//! @return True if the two UUIDs are equal, false if they are not.
|
||||
bool uuid_equal(const Uuid *uu1, const Uuid *uu2);
|
||||
|
||||
//! Generate a v4 compliant UUID
|
||||
void uuid_generate(Uuid *uuid_out);
|
||||
|
||||
//! Check if a UUID is the system UUID
|
||||
//! @return True if UUID is UUID_SYSTEM, false otherwise
|
||||
bool uuid_is_system(const Uuid *uuid);
|
||||
|
||||
//! Tests whether a UUID is equal to UUID_INVALID
|
||||
//! @param uuid The UUID to test
|
||||
//! @return True uuid is equal to UUID_INVALID, false if not
|
||||
bool uuid_is_invalid(const Uuid *uuid);
|
||||
|
||||
//! The minimum required length of a string used to hold a uuid (including null).
|
||||
#define UUID_STRING_BUFFER_LENGTH (32 + 4 + 2 + 1) // numbers + dashes + brackets + trailing null
|
||||
|
||||
//! Writes UUID in a string form into buffer that looks like the following...
|
||||
//! {12345678-1234-5678-1234-567812345678} or {NULL UUID} if NULL was passed.
|
||||
//! @param uuid The Uuid to write into the buffer as human-readable string
|
||||
//! @param buffer Memory to write the string to. Must be at least \ref UUID_STRING_BUFFER_LENGTH bytes long.
|
||||
void uuid_to_string(const Uuid *uuid, char *buffer);
|
||||
|
40
src/libutil/iterator.c
Normal file
40
src/libutil/iterator.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/iterator.h"
|
||||
|
||||
#include "util/assert.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void iter_init(Iterator* iter, IteratorCallback next, IteratorCallback prev, IteratorState state) {
|
||||
*iter = (Iterator) {
|
||||
.next = next,
|
||||
.prev = prev,
|
||||
.state = state
|
||||
};
|
||||
}
|
||||
|
||||
bool iter_next(Iterator* iter) {
|
||||
UTIL_ASSERT(iter->next);
|
||||
return iter->next(iter->state);
|
||||
}
|
||||
|
||||
bool iter_prev(Iterator* iter) {
|
||||
UTIL_ASSERT(iter->prev);
|
||||
return iter->prev(iter->state);
|
||||
}
|
||||
|
69
src/libutil/keyed_circular_cache.c
Normal file
69
src/libutil/keyed_circular_cache.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/keyed_circular_cache.h"
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void keyed_circular_cache_init(KeyedCircularCache *c, KeyedCircularCacheKey *key_buffer,
|
||||
void *data_buffer, size_t item_size, size_t total_items) {
|
||||
UTIL_ASSERT(c);
|
||||
UTIL_ASSERT(key_buffer);
|
||||
UTIL_ASSERT(data_buffer);
|
||||
UTIL_ASSERT(item_size);
|
||||
|
||||
*c = (KeyedCircularCache) {
|
||||
.cache_keys = key_buffer,
|
||||
.cache_data = (uint8_t *)data_buffer,
|
||||
.item_size = item_size,
|
||||
.total_items = total_items,
|
||||
};
|
||||
}
|
||||
|
||||
static uint8_t *prv_get_item_at_index(KeyedCircularCache *c, int index) {
|
||||
return c->cache_data + (index * c->item_size);
|
||||
}
|
||||
|
||||
void *keyed_circular_cache_get(KeyedCircularCache *c, KeyedCircularCacheKey key) {
|
||||
// Optimize for accessing most recently pushed elements.
|
||||
int idx = c->next_item_to_erase_idx;
|
||||
for (unsigned int i = 0; i < c->total_items; i++) {
|
||||
idx--;
|
||||
if (idx < 0) {
|
||||
idx += c->total_items;
|
||||
}
|
||||
if (c->cache_keys[idx] == key) {
|
||||
return prv_get_item_at_index(c, idx);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void keyed_circular_cache_push(KeyedCircularCache *c, KeyedCircularCacheKey key,
|
||||
const void *new_item) {
|
||||
uint8_t *old_item = prv_get_item_at_index(c, c->next_item_to_erase_idx);
|
||||
memcpy(old_item, new_item, c->item_size);
|
||||
c->cache_keys[c->next_item_to_erase_idx] = key;
|
||||
|
||||
c->next_item_to_erase_idx++;
|
||||
UTIL_ASSERT(c->next_item_to_erase_idx <= c->total_items);
|
||||
if (c->next_item_to_erase_idx == c->total_items) {
|
||||
c->next_item_to_erase_idx = 0;
|
||||
}
|
||||
}
|
356
src/libutil/list.c
Normal file
356
src/libutil/list.c
Normal file
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/list.h"
|
||||
#include "util/assert.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void list_init(ListNode* node) {
|
||||
node->next = NULL;
|
||||
node->prev = NULL;
|
||||
}
|
||||
|
||||
ListNode* list_insert_after(ListNode* node, ListNode* new_node) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
new_node->next = node->next;
|
||||
new_node->prev = node;
|
||||
|
||||
if (node->next) {
|
||||
node->next->prev = new_node;
|
||||
}
|
||||
node->next = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ListNode* list_insert_before(ListNode* node, ListNode* new_node) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
new_node->next = node;
|
||||
new_node->prev = node->prev;
|
||||
|
||||
if (node->prev) {
|
||||
node->prev->next = new_node;
|
||||
}
|
||||
node->prev = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
ListNode* list_pop_head(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *head = list_get_head(node);
|
||||
ListNode *new_head = head->next;
|
||||
list_remove(head, NULL, NULL);
|
||||
return new_head;
|
||||
}
|
||||
|
||||
ListNode* list_pop_tail(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode* tail = list_get_tail(node);
|
||||
ListNode* new_tail = tail->prev;
|
||||
list_remove(tail, NULL, NULL);
|
||||
return new_tail;
|
||||
}
|
||||
|
||||
void list_remove(ListNode* node, ListNode **head, ListNode **tail) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
if (head && *head == node) {
|
||||
*head = node->next;
|
||||
}
|
||||
if (tail && *tail == node) {
|
||||
*tail = node->prev;
|
||||
}
|
||||
if (node->next) {
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
if (node->prev) {
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
ListNode* list_append(ListNode* node, ListNode* new_node) {
|
||||
return list_insert_after(list_get_tail(node), new_node);
|
||||
}
|
||||
|
||||
ListNode* list_prepend(ListNode* node, ListNode* new_node) {
|
||||
return list_insert_before(list_get_head(node), new_node);
|
||||
}
|
||||
|
||||
ListNode* list_get_next(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return node->next;
|
||||
}
|
||||
|
||||
ListNode* list_get_prev(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return node->prev;
|
||||
}
|
||||
|
||||
ListNode* list_get_tail(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
while (node->next != NULL) {
|
||||
node = node->next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ListNode* list_get_head(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
while (node->prev != NULL) {
|
||||
node = node->prev;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
bool list_is_head(const ListNode *node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return !node->prev;
|
||||
}
|
||||
|
||||
bool list_is_tail(const ListNode *node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return !node->next;
|
||||
}
|
||||
|
||||
uint32_t list_count_to_tail_from(ListNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t count = 1;
|
||||
while ((node = node->next) != NULL) {
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t list_count_to_head_from(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t count = 1;
|
||||
while ((node = node->prev) != NULL) {
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t list_count(ListNode* node) {
|
||||
return list_count_to_tail_from(list_get_head(node));
|
||||
}
|
||||
|
||||
ListNode* list_get_at(ListNode *node, int32_t index) {
|
||||
while (node != NULL && index != 0) {
|
||||
if (index > 0) {
|
||||
node = node->next;
|
||||
index--;
|
||||
} else {
|
||||
node = node->prev;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ListNode* list_sorted_add(ListNode *node, ListNode *new_node, Comparator comparator, bool ascending) {
|
||||
if (node == NULL) {
|
||||
return new_node;
|
||||
}
|
||||
if (new_node == NULL) {
|
||||
return node;
|
||||
}
|
||||
ListNode * const head = node;
|
||||
for(;;) {
|
||||
int order = comparator(node, new_node);
|
||||
if (!ascending) {
|
||||
order = -order;
|
||||
}
|
||||
|
||||
if (order < 0) {
|
||||
list_insert_before(node, new_node);
|
||||
if (node == head) {
|
||||
return new_node;
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
ListNode *next = node->next;
|
||||
if (next == NULL) {
|
||||
list_insert_after(node, new_node);
|
||||
return head;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool list_contains(const ListNode *node, const ListNode *node_to_search) {
|
||||
if (node == NULL || node_to_search == NULL) {
|
||||
return false;
|
||||
}
|
||||
while (node) {
|
||||
if (node == node_to_search) {
|
||||
return true;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ListNode* list_find(ListNode *node, ListFilterCallback filter_callback, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
do {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
} while ((cursor = cursor->next));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode* list_find_next(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
while ((cursor = cursor->next)) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
if (wrap_around == false) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = list_get_head(node);
|
||||
while (cursor) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
// We're back at where we started and even <node> itself doesn't match the filter
|
||||
if (cursor == node) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = cursor->next;
|
||||
}
|
||||
UTIL_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode* list_find_prev(ListNode *node, ListFilterCallback filter_callback, bool wrap_around, void *data) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ListNode *cursor = node;
|
||||
while ((cursor = cursor->prev)) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
if (wrap_around == false) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = list_get_tail(node);
|
||||
while (cursor) {
|
||||
if (filter_callback(cursor, data)) {
|
||||
return cursor;
|
||||
}
|
||||
// We're back at where we started and even <node> itself doesn't match the filter
|
||||
if (cursor == node) {
|
||||
return NULL;
|
||||
}
|
||||
cursor = cursor->prev;
|
||||
}
|
||||
UTIL_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode *list_concatenate(ListNode *restrict list_a, ListNode *restrict list_b) {
|
||||
ListNode *head_a = list_get_head(list_a);
|
||||
if (list_b == NULL) {
|
||||
return head_a;
|
||||
}
|
||||
|
||||
ListNode *head_b = list_get_head(list_b);
|
||||
if (list_a == NULL) {
|
||||
return head_b;
|
||||
}
|
||||
|
||||
if (head_a == head_b) {
|
||||
// list b is already in list a!
|
||||
return head_a;
|
||||
}
|
||||
|
||||
ListNode *tail_a = list_get_tail(list_a);
|
||||
|
||||
head_b->prev = tail_a;
|
||||
tail_a->next = head_b;
|
||||
|
||||
return head_a;
|
||||
}
|
||||
|
||||
void list_foreach(ListNode *head, ListForEachCallback each_cb, void *context) {
|
||||
if (!each_cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
ListNode *iter = head;
|
||||
while (iter) {
|
||||
// Save off a pointer so the client to this function can destroy the node (useful for deinits)
|
||||
ListNode *next = iter->next;
|
||||
if (!each_cb(iter, context)) {
|
||||
return;
|
||||
}
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
void list_debug_dump(ListNode *head) {
|
||||
ListNode *iter = head;
|
||||
char buffer[30];
|
||||
while (iter) {
|
||||
snprintf(buffer, sizeof(buffer), "node %p (%p, %p)", iter, iter->prev, iter->next);
|
||||
UTIL_LOG(buffer);
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
95
src/libutil/math.c
Normal file
95
src/libutil/math.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/math.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int32_t sign_extend(uint32_t a, int bits) {
|
||||
if (bits == 32) {
|
||||
return a;
|
||||
}
|
||||
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
|
||||
int const m = 1U << (bits - 1); // mask can be pre-computed if b is fixed
|
||||
|
||||
a = a & ((1U << bits) - 1); // (Skip this if bits in x above position b are already zero.)
|
||||
return (a ^ m) - m;
|
||||
}
|
||||
|
||||
int32_t serial_distance32(uint32_t a, uint32_t b) {
|
||||
return serial_distance(a, b, 32);
|
||||
}
|
||||
|
||||
int32_t serial_distance(uint32_t a, uint32_t b, int bits) {
|
||||
// See https://en.wikipedia.org/wiki/Serial_Number_Arithmetic
|
||||
const int64_t a_minus_b = a - b;
|
||||
const int64_t b_minus_a = b - a;
|
||||
const bool a_is_earlier_than_b = (a < b && b_minus_a < (1 << (bits - 1))) || (a > b && a_minus_b > (1 << (bits - 1)));
|
||||
return sign_extend(a_is_earlier_than_b ? -a_minus_b : b_minus_a, bits);
|
||||
}
|
||||
|
||||
int ceil_log_two(uint32_t n) {
|
||||
// clz stands for Count Leading Zeroes. We use it to find the MSB
|
||||
int msb = 31 - __builtin_clz(n);
|
||||
// popcount counts the number of set bits in a word (1's)
|
||||
bool power_of_two = __builtin_popcount(n) == 1;
|
||||
// if not exact power of two, use the next power of two
|
||||
// we want to err on the side of caution and want to
|
||||
// always round up
|
||||
return ((power_of_two) ? msb : (msb + 1));
|
||||
}
|
||||
|
||||
//! newton's method for floor(sqrt(x)) -> should always converge
|
||||
int32_t integer_sqrt(int64_t x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
int64_t last_res = 0x3fff;
|
||||
uint16_t iterations = 0;
|
||||
while ((last_res > 0) && (iterations < 15)) {
|
||||
last_res = ((x / last_res) + last_res)/2;
|
||||
iterations++;
|
||||
}
|
||||
return (last_res);
|
||||
}
|
||||
|
||||
uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value) {
|
||||
if (*attempt > 31) {
|
||||
return max_value;
|
||||
}
|
||||
uint32_t backoff_multiplier = 0x1 << (*attempt)++;
|
||||
uint32_t next_value = initial_value * backoff_multiplier;
|
||||
return MIN(next_value, max_value);
|
||||
}
|
||||
|
||||
uint32_t gcd(uint32_t a, uint32_t b) {
|
||||
// Infinite loops are bad
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Euclidean algorithm, yay!
|
||||
while (a != b) {
|
||||
if (a > b) {
|
||||
a -= b;
|
||||
} else {
|
||||
b -= a;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
50
src/libutil/math_fixed.c
Normal file
50
src/libutil/math_fixed.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/math_fixed.h"
|
||||
|
||||
Fixed_S64_32 math_fixed_recursive_filter(Fixed_S64_32 x,
|
||||
int num_input_coefficients, int num_output_coefficients,
|
||||
const Fixed_S64_32 *cb, const Fixed_S64_32 *ca,
|
||||
Fixed_S64_32 *state_x, Fixed_S64_32 *state_y) {
|
||||
UTIL_ASSERT(num_input_coefficients >= 1);
|
||||
|
||||
// shift the input over by one
|
||||
for (int k = num_input_coefficients - 1; k > 0; k--) {
|
||||
state_x[k] = state_x[k - 1];
|
||||
}
|
||||
state_x[0] = x;
|
||||
|
||||
// Factor in the x * b elements
|
||||
Fixed_S64_32 ytmp = Fixed_S64_32_mul(cb[0], state_x[0]);
|
||||
for (int i = 1; i < num_input_coefficients; i++) {
|
||||
ytmp = Fixed_S64_32_add(ytmp, Fixed_S64_32_mul(cb[i], state_x[i]));
|
||||
}
|
||||
|
||||
// Factor in the y * a coeficients
|
||||
for (int i = 0; i < num_output_coefficients; i++) {
|
||||
ytmp = Fixed_S64_32_sub(ytmp, Fixed_S64_32_mul(ca[i], state_y[i]));
|
||||
}
|
||||
|
||||
// shift the y output elements
|
||||
for (int k = num_output_coefficients - 1; k > 0; k--) {
|
||||
state_y[k] = state_y[k-1];
|
||||
}
|
||||
state_y[0] = ytmp;
|
||||
|
||||
return ytmp;
|
||||
}
|
30
src/libutil/order.c
Normal file
30
src/libutil/order.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/order.h"
|
||||
|
||||
int uint32_comparator(void *a, void *b) {
|
||||
uint32_t A = *(uint32_t *)a;
|
||||
uint32_t B = *(uint32_t *)b;
|
||||
|
||||
if (B > A) {
|
||||
return 1;
|
||||
} else if (B < A) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
49
src/libutil/platform.c
Normal file
49
src/libutil/platform.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/assert.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/rand32.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
// Below are some default implementations for system-specific functions required by libutil.
|
||||
// These functions assume a working C standard library is linked into the program.
|
||||
// For programs where this isn't the case (e.g. the Pebble FW),
|
||||
// alternate implementations need to be provided.
|
||||
// The functions are defined as WEAK so they may be easily overridden.
|
||||
|
||||
// If you are getting link errors due to printf not being defined, you probably
|
||||
// need to provide your own implementation of the functions below.
|
||||
|
||||
WEAK void util_log(const char *filename, int line, const char *string) {
|
||||
printf("%s:%d %s\n", filename, line, string);
|
||||
}
|
||||
|
||||
WEAK void util_dbgserial_str(const char *string) {
|
||||
printf("%s\n", string);
|
||||
}
|
||||
|
||||
WEAK NORETURN util_assertion_failed(const char *filename, int line) {
|
||||
util_log(filename, line, "*** UTIL ASSERT FAILED");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
WEAK uint32_t rand32(void) {
|
||||
return ((uint32_t)rand() << 1) + (uint32_t)rand;
|
||||
}
|
44
src/libutil/sort.c
Normal file
44
src/libutil/sort.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <util/sort.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static void prv_swap(void *a, void *b, size_t elem_size) {
|
||||
uint8_t *a_ptr = (uint8_t *)a;
|
||||
uint8_t *b_ptr = (uint8_t *)b;
|
||||
for (size_t i = 0; i < elem_size; i++) {
|
||||
uint8_t tmp = *a_ptr;
|
||||
*a_ptr++ = *b_ptr;
|
||||
*b_ptr++ = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void sort_bubble(void *array, size_t num_elem, size_t elem_size, SortComparator comp) {
|
||||
// as the number of sessions is expected to be small (<=16), and we don't seem to have a generic
|
||||
// sort implementation, we do a simple bubble sort here
|
||||
for (uint32_t i = 0; i + 1 < num_elem; i++) {
|
||||
for (uint32_t j = i + 1; j < num_elem; j++) {
|
||||
uint8_t *val1 = ((uint8_t *)array) + (i * elem_size);
|
||||
uint8_t *val2 = ((uint8_t *)array) + (j * elem_size);
|
||||
if (comp(val1, val2) > 0) {
|
||||
prv_swap(val1, val2, elem_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
188
src/libutil/string.c
Normal file
188
src/libutil/string.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char *string_strip_leading_whitespace(const char *string) {
|
||||
const char *result_string = string;
|
||||
while (*result_string != '\0') {
|
||||
if (*result_string != ' ' &&
|
||||
*result_string != '\n') {
|
||||
break;
|
||||
}
|
||||
result_string++;
|
||||
}
|
||||
|
||||
return result_string;
|
||||
}
|
||||
|
||||
void string_strip_trailing_whitespace(const char *string, char *string_out) {
|
||||
int string_len = strlen(string);
|
||||
bool trim = true;
|
||||
for (int i = string_len; i >= 0; i--) {
|
||||
if (trim &&
|
||||
(string[i] == ' ' || string[i] == '\n' || string[i] == '\0')) {
|
||||
string_out[i] = '\0';
|
||||
} else {
|
||||
trim = false;
|
||||
string_out[i] = string[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *bool_to_str(bool b) {
|
||||
if (b) {
|
||||
return "yes";
|
||||
} else {
|
||||
return "no";
|
||||
}
|
||||
}
|
||||
|
||||
void itoa(uint32_t num, char *buffer, int buffer_length) {
|
||||
if (buffer_length < 11) {
|
||||
return;
|
||||
}
|
||||
*buffer++ = '0';
|
||||
*buffer++ = 'x';
|
||||
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4);
|
||||
|
||||
char c;
|
||||
if (digit < 0xa) {
|
||||
c = '0' + digit;
|
||||
} else if (digit < 0x10) {
|
||||
c = 'a' + (digit - 0xa);
|
||||
} else {
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
*buffer++ = c;
|
||||
}
|
||||
*buffer = '\0';
|
||||
}
|
||||
|
||||
void string_reverse(char *str) {
|
||||
uint8_t i = 0;
|
||||
int8_t j = strlen(str) - 1;
|
||||
for (i = 0; i < j; i++, j--) {
|
||||
char c = str[i];
|
||||
str[i] = str[j];
|
||||
str[j] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/* itoa: convert n to characters in s */
|
||||
void itoa_int(int n, char *str, int base) {
|
||||
bool neg;
|
||||
if ((neg = (n < 0))) { /* record sign */
|
||||
n = -n; /* make n positive */
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
do { /* generate digits in reverse order */
|
||||
str[i++] = (n % base) + '0'; /* get next digit */
|
||||
} while ((n /= base) > 0); /* delete it */
|
||||
|
||||
if (neg) {
|
||||
str[i++] = '-'; /* append sign */
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
string_reverse(str);
|
||||
}
|
||||
|
||||
static int8_t ascii_hex_to_int(const uint8_t c) {
|
||||
if (isdigit(c)) return c - '0';
|
||||
if (isupper(c)) return (c - 'A') + 10;
|
||||
if (islower(c)) return (c - 'a') + 10;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint8_t ascii_hex_to_uint(const uint8_t msb, const uint8_t lsb) {
|
||||
return 16 * ascii_hex_to_int(msb) + ascii_hex_to_int(lsb);
|
||||
}
|
||||
|
||||
uintptr_t str_to_address(const char *address_str) {
|
||||
char *endptr;
|
||||
uintptr_t address = strtoul(address_str, &endptr, 0);
|
||||
if (*endptr != '\0') { // A non-address character encountered
|
||||
return -1;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size) {
|
||||
const int len = strlen(hex_str);
|
||||
if (len != 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* src = (uint8_t*) hex_str;
|
||||
uint8_t* dest = bd_addr + bd_addr_size - 1;
|
||||
|
||||
for (unsigned int i = 0; i < bd_addr_size; ++i, src += 2, --dest) {
|
||||
*dest = ascii_hex_to_uint(src[0], src[1]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void concat_str_int(const char *str, uint32_t num, char *buf, uint8_t buf_len) {
|
||||
uint8_t str_len = strlen(str);
|
||||
strncpy(buf, str, str_len);
|
||||
itoa_int(num, buf + str_len, 10);
|
||||
}
|
||||
|
||||
void toupper_str(char *str) {
|
||||
int len = strlen(str);
|
||||
for (int i = 0; i < len; i++) {
|
||||
str[i] = toupper((unsigned char)str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void byte_stream_to_hex_string(char *out_buf, size_t out_buf_len,
|
||||
const uint8_t *byte_stream, size_t byte_stream_len, bool print_backward) {
|
||||
size_t bytes_left = byte_stream_len;
|
||||
if (print_backward) {
|
||||
byte_stream += (byte_stream_len - 1); // addr of the last element
|
||||
}
|
||||
|
||||
while (out_buf_len >= 3 /* 2 hex digits, plus '\0' */
|
||||
&& bytes_left > 0) {
|
||||
snprintf(out_buf, out_buf_len, "%02x", *byte_stream);
|
||||
|
||||
out_buf += 2;
|
||||
out_buf_len -= 2;
|
||||
|
||||
byte_stream += (print_backward) ? -1 : 1;
|
||||
bytes_left -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void safe_strcat(char* dst, const char* src, int dst_space) {
|
||||
int remaining = dst_space - strlen(dst);
|
||||
if (dst_space > 0) {
|
||||
strncat(dst, src, remaining);
|
||||
}
|
||||
dst[dst_space-1] = 0;
|
||||
}
|
623
src/libutil/trig.c
Normal file
623
src/libutil/trig.c
Normal file
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "util/math.h"
|
||||
|
||||
|
||||
// lookup points on the first quarter sin wave
|
||||
// generated by tintin/tools/trigTable.py
|
||||
static const uint16_t SIN_LOOKUP[] = {
|
||||
0,
|
||||
401,
|
||||
801,
|
||||
1202,
|
||||
1602,
|
||||
2002,
|
||||
2403,
|
||||
2803,
|
||||
3203,
|
||||
3603,
|
||||
4003,
|
||||
4403,
|
||||
4802,
|
||||
5202,
|
||||
5601,
|
||||
6000,
|
||||
6399,
|
||||
6797,
|
||||
7195,
|
||||
7593,
|
||||
7991,
|
||||
8389,
|
||||
8786,
|
||||
9182,
|
||||
9579,
|
||||
9975,
|
||||
10371,
|
||||
10766,
|
||||
11161,
|
||||
11555,
|
||||
11949,
|
||||
12343,
|
||||
12736,
|
||||
13129,
|
||||
13521,
|
||||
13913,
|
||||
14304,
|
||||
14694,
|
||||
15085,
|
||||
15474,
|
||||
15863,
|
||||
16251,
|
||||
16639,
|
||||
17026,
|
||||
17413,
|
||||
17798,
|
||||
18184,
|
||||
18568,
|
||||
18952,
|
||||
19335,
|
||||
19717,
|
||||
20099,
|
||||
20480,
|
||||
20860,
|
||||
21239,
|
||||
21618,
|
||||
21996,
|
||||
22372,
|
||||
22749,
|
||||
23124,
|
||||
23498,
|
||||
23872,
|
||||
24244,
|
||||
24616,
|
||||
24987,
|
||||
25356,
|
||||
25725,
|
||||
26093,
|
||||
26460,
|
||||
26826,
|
||||
27191,
|
||||
27555,
|
||||
27918,
|
||||
28280,
|
||||
28641,
|
||||
29000,
|
||||
29359,
|
||||
29717,
|
||||
30073,
|
||||
30428,
|
||||
30783,
|
||||
31136,
|
||||
31487,
|
||||
31838,
|
||||
32188,
|
||||
32536,
|
||||
32883,
|
||||
33229,
|
||||
33574,
|
||||
33917,
|
||||
34259,
|
||||
34600,
|
||||
34939,
|
||||
35278,
|
||||
35614,
|
||||
35950,
|
||||
36284,
|
||||
36617,
|
||||
36949,
|
||||
37279,
|
||||
37608,
|
||||
37935,
|
||||
38261,
|
||||
38585,
|
||||
38908,
|
||||
39230,
|
||||
39550,
|
||||
39869,
|
||||
40186,
|
||||
40502,
|
||||
40816,
|
||||
41128,
|
||||
41439,
|
||||
41749,
|
||||
42057,
|
||||
42363,
|
||||
42668,
|
||||
42971,
|
||||
43273,
|
||||
43573,
|
||||
43871,
|
||||
44168,
|
||||
44463,
|
||||
44757,
|
||||
45048,
|
||||
45338,
|
||||
45627,
|
||||
45913,
|
||||
46198,
|
||||
46482,
|
||||
46763,
|
||||
47043,
|
||||
47321,
|
||||
47597,
|
||||
47872,
|
||||
48144,
|
||||
48415,
|
||||
48684,
|
||||
48951,
|
||||
49217,
|
||||
49480,
|
||||
49742,
|
||||
50002,
|
||||
50260,
|
||||
50516,
|
||||
50770,
|
||||
51023,
|
||||
51273,
|
||||
51521,
|
||||
51768,
|
||||
52013,
|
||||
52255,
|
||||
52496,
|
||||
52735,
|
||||
52972,
|
||||
53207,
|
||||
53440,
|
||||
53670,
|
||||
53899,
|
||||
54126,
|
||||
54351,
|
||||
54574,
|
||||
54794,
|
||||
55013,
|
||||
55230,
|
||||
55444,
|
||||
55657,
|
||||
55867,
|
||||
56076,
|
||||
56282,
|
||||
56486,
|
||||
56688,
|
||||
56888,
|
||||
57086,
|
||||
57281,
|
||||
57475,
|
||||
57666,
|
||||
57856,
|
||||
58043,
|
||||
58228,
|
||||
58410,
|
||||
58591,
|
||||
58769,
|
||||
58945,
|
||||
59119,
|
||||
59291,
|
||||
59461,
|
||||
59628,
|
||||
59793,
|
||||
59956,
|
||||
60116,
|
||||
60275,
|
||||
60431,
|
||||
60585,
|
||||
60736,
|
||||
60886,
|
||||
61033,
|
||||
61177,
|
||||
61320,
|
||||
61460,
|
||||
61598,
|
||||
61734,
|
||||
61867,
|
||||
61998,
|
||||
62126,
|
||||
62253,
|
||||
62377,
|
||||
62498,
|
||||
62618,
|
||||
62735,
|
||||
62850,
|
||||
62962,
|
||||
63072,
|
||||
63179,
|
||||
63285,
|
||||
63388,
|
||||
63488,
|
||||
63586,
|
||||
63682,
|
||||
63775,
|
||||
63866,
|
||||
63955,
|
||||
64041,
|
||||
64125,
|
||||
64206,
|
||||
64286,
|
||||
64362,
|
||||
64436,
|
||||
64508,
|
||||
64578,
|
||||
64645,
|
||||
64709,
|
||||
64771,
|
||||
64831,
|
||||
64889,
|
||||
64943,
|
||||
64996,
|
||||
65046,
|
||||
65094,
|
||||
65139,
|
||||
65182,
|
||||
65222,
|
||||
65260,
|
||||
65295,
|
||||
65328,
|
||||
65359,
|
||||
65387,
|
||||
65413,
|
||||
65436,
|
||||
65457,
|
||||
65475,
|
||||
65491,
|
||||
65504,
|
||||
65515,
|
||||
65524,
|
||||
65530,
|
||||
65534,
|
||||
65535
|
||||
};
|
||||
|
||||
// lookup points on the first quarter atan wave
|
||||
// generated by tintin/tools/trigTable.py
|
||||
static const uint16_t ATAN_LOOKUP[] = {
|
||||
0,
|
||||
328,
|
||||
648,
|
||||
976,
|
||||
1296,
|
||||
1624,
|
||||
1944,
|
||||
2272,
|
||||
2600,
|
||||
2920,
|
||||
3248,
|
||||
3568,
|
||||
3896,
|
||||
4216,
|
||||
4544,
|
||||
4864,
|
||||
5192,
|
||||
5512,
|
||||
5832,
|
||||
6160,
|
||||
6480,
|
||||
6800,
|
||||
7128,
|
||||
7448,
|
||||
7768,
|
||||
8088,
|
||||
8416,
|
||||
8736,
|
||||
9056,
|
||||
9376,
|
||||
9696,
|
||||
10016,
|
||||
10336,
|
||||
10656,
|
||||
10976,
|
||||
11296,
|
||||
11616,
|
||||
11928,
|
||||
12248,
|
||||
12568,
|
||||
12880,
|
||||
13200,
|
||||
13520,
|
||||
13832,
|
||||
14152,
|
||||
14464,
|
||||
14776,
|
||||
15096,
|
||||
15408,
|
||||
15720,
|
||||
16032,
|
||||
16344,
|
||||
16656,
|
||||
16968,
|
||||
17280,
|
||||
17592,
|
||||
17904,
|
||||
18216,
|
||||
18520,
|
||||
18832,
|
||||
19136,
|
||||
19448,
|
||||
19752,
|
||||
20056,
|
||||
20368,
|
||||
20672,
|
||||
20976,
|
||||
21280,
|
||||
21584,
|
||||
21888,
|
||||
22192,
|
||||
22488,
|
||||
22792,
|
||||
23096,
|
||||
23392,
|
||||
23696,
|
||||
23992,
|
||||
24288,
|
||||
24584,
|
||||
24888,
|
||||
25184,
|
||||
25480,
|
||||
25768,
|
||||
26064,
|
||||
26360,
|
||||
26656,
|
||||
26944,
|
||||
27240,
|
||||
27528,
|
||||
27816,
|
||||
28104,
|
||||
28400,
|
||||
28688,
|
||||
28976,
|
||||
29256,
|
||||
29544,
|
||||
29832,
|
||||
30112,
|
||||
30400,
|
||||
30680,
|
||||
30960,
|
||||
31248,
|
||||
31528,
|
||||
31808,
|
||||
32088,
|
||||
32368,
|
||||
32640,
|
||||
32920,
|
||||
33192,
|
||||
33472,
|
||||
33744,
|
||||
34024,
|
||||
34296,
|
||||
34568,
|
||||
34840,
|
||||
35112,
|
||||
35376,
|
||||
35648,
|
||||
35920,
|
||||
36184,
|
||||
36448,
|
||||
36720,
|
||||
36984,
|
||||
37248,
|
||||
37512,
|
||||
37776,
|
||||
38040,
|
||||
38296,
|
||||
38560,
|
||||
38816,
|
||||
39080,
|
||||
39336,
|
||||
39592,
|
||||
39848,
|
||||
40104,
|
||||
40360,
|
||||
40616,
|
||||
40864,
|
||||
41120,
|
||||
41368,
|
||||
41624,
|
||||
41872,
|
||||
42120,
|
||||
42368,
|
||||
42616,
|
||||
42864,
|
||||
43112,
|
||||
43352,
|
||||
43600,
|
||||
43840,
|
||||
44088,
|
||||
44328,
|
||||
44568,
|
||||
44808,
|
||||
45048,
|
||||
45288,
|
||||
45520,
|
||||
45760,
|
||||
45992,
|
||||
46232,
|
||||
46464,
|
||||
46696,
|
||||
46928,
|
||||
47160,
|
||||
47392,
|
||||
47624,
|
||||
47856,
|
||||
48080,
|
||||
48312,
|
||||
48536,
|
||||
48768,
|
||||
48992,
|
||||
49216,
|
||||
49440,
|
||||
49664,
|
||||
49880,
|
||||
50104,
|
||||
50328,
|
||||
50544,
|
||||
50768,
|
||||
50984,
|
||||
51200,
|
||||
51416,
|
||||
51632,
|
||||
51848,
|
||||
52064,
|
||||
52272,
|
||||
52488,
|
||||
52696,
|
||||
52912,
|
||||
53120,
|
||||
53328,
|
||||
53536,
|
||||
53744,
|
||||
53952,
|
||||
54160,
|
||||
54368,
|
||||
54568,
|
||||
54776,
|
||||
54976,
|
||||
55184,
|
||||
55384,
|
||||
55584,
|
||||
55784,
|
||||
55984,
|
||||
56184,
|
||||
56384,
|
||||
56576,
|
||||
56776,
|
||||
56968,
|
||||
57168,
|
||||
57360,
|
||||
57552,
|
||||
57744,
|
||||
57936,
|
||||
58128,
|
||||
58320,
|
||||
58512,
|
||||
58704,
|
||||
58888,
|
||||
59080,
|
||||
59264,
|
||||
59448,
|
||||
59632,
|
||||
59824,
|
||||
60008,
|
||||
60184,
|
||||
60368,
|
||||
60552,
|
||||
60736,
|
||||
60912,
|
||||
61096,
|
||||
61272,
|
||||
61456,
|
||||
61632,
|
||||
61808,
|
||||
61984,
|
||||
62160,
|
||||
62336,
|
||||
62512,
|
||||
62680,
|
||||
62856,
|
||||
63032,
|
||||
63200,
|
||||
63368,
|
||||
63544,
|
||||
63712,
|
||||
63880,
|
||||
64048,
|
||||
64216,
|
||||
64384,
|
||||
64552,
|
||||
64720,
|
||||
64880,
|
||||
65048,
|
||||
65208,
|
||||
65376,
|
||||
65535,
|
||||
};
|
||||
|
||||
int32_t sin_lookup(int32_t angle) {
|
||||
int32_t mult = 1;
|
||||
|
||||
// modify the input angle and output multiplier for use in a first quadrent sine lookup
|
||||
if (angle < 0) {
|
||||
angle = -angle;
|
||||
mult = -mult;
|
||||
}
|
||||
if (angle >= TRIG_MAX_ANGLE) {
|
||||
angle %= TRIG_MAX_ANGLE;
|
||||
}
|
||||
if (angle >= TRIG_MAX_ANGLE / 2) {
|
||||
mult = -mult;
|
||||
angle -= TRIG_MAX_ANGLE / 2;
|
||||
}
|
||||
if (angle >= TRIG_MAX_ANGLE / 4 && angle <= TRIG_MAX_ANGLE / 2) {
|
||||
angle = TRIG_MAX_ANGLE / 2 - angle;
|
||||
}
|
||||
|
||||
// if I can interpolate linearly
|
||||
int32_t lookup_angle = angle * 4 / 0xff;
|
||||
if ((uint32_t)(lookup_angle + 1) < sizeof(SIN_LOOKUP) / sizeof(int32_t)) {
|
||||
return mult * (SIN_LOOKUP[lookup_angle] + ((angle * 4) % 0xff) * (SIN_LOOKUP[lookup_angle + 1] - SIN_LOOKUP[lookup_angle]) / 0xff);
|
||||
}
|
||||
|
||||
return mult * SIN_LOOKUP[lookup_angle];
|
||||
}
|
||||
|
||||
int32_t cos_lookup(int32_t angle) {
|
||||
return sin_lookup(angle + TRIG_MAX_ANGLE / 4);
|
||||
}
|
||||
|
||||
#define ATAN_LUT_STRIDE 0xff
|
||||
|
||||
int32_t atan2_lookup(int16_t y, int16_t x) {
|
||||
// inspired by http://www.coranac.com/documents/arctangent/
|
||||
if (y == 0) {
|
||||
return (x >= 0 ? 0 : TRIG_PI);
|
||||
}
|
||||
|
||||
// moving x and y values into bigger containers to avoid overflows
|
||||
int32_t x_wide = x;
|
||||
int32_t y_wide = y;
|
||||
int32_t angle = 0;
|
||||
// Find octant
|
||||
if (y_wide < 0) {
|
||||
x_wide = -x_wide;
|
||||
y_wide = -y_wide;
|
||||
angle += 4;
|
||||
}
|
||||
if (x_wide <= 0) {
|
||||
uint32_t t = x_wide;
|
||||
x_wide = y_wide;
|
||||
y_wide = -t;
|
||||
angle += 2;
|
||||
}
|
||||
if (x_wide <= y_wide) {
|
||||
uint32_t t = y_wide - x_wide;
|
||||
x_wide = x_wide + y_wide;
|
||||
y_wide = t;
|
||||
angle += 1;
|
||||
}
|
||||
|
||||
angle *= TRIG_PI / 4;
|
||||
uint32_t ratio = (y_wide << TRIG_FP) / x_wide;
|
||||
|
||||
return (angle + ATAN_LOOKUP[ratio / ATAN_LUT_STRIDE] / 8);
|
||||
}
|
||||
|
||||
uint32_t normalize_angle(int32_t angle) {
|
||||
uint32_t normalized_angle = ABS(angle) % TRIG_MAX_ANGLE;
|
||||
if (angle < 0) {
|
||||
normalized_angle = TRIG_MAX_ANGLE - normalized_angle;
|
||||
}
|
||||
return normalized_angle;
|
||||
}
|
77
src/libutil/uuid.c
Normal file
77
src/libutil/uuid.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/uuid.h"
|
||||
#include "util/rand32.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const Uuid system_uuid = UUID_SYSTEM;
|
||||
static const Uuid invalid_uuid = UUID_INVALID_INIT;
|
||||
|
||||
void uuid_generate(Uuid *uuid_out) {
|
||||
uint8_t uuid_bytes[UUID_SIZE];
|
||||
uint32_t *uuid_words = (uint32_t*)uuid_bytes;
|
||||
for (size_t i = 0; i < UUID_SIZE / sizeof(uint32_t); i++) {
|
||||
uuid_words[i] = rand32();
|
||||
}
|
||||
// set the version bits
|
||||
uuid_bytes[6] = (uuid_bytes[6] & ~0xF0) | 0x40;
|
||||
// set the reserved bits
|
||||
uuid_bytes[8] = (uuid_bytes[8] & ~0xC0) | 0x80;
|
||||
|
||||
// Use BE so that the bytes for version and reserved are correctly placed.
|
||||
*uuid_out = UuidMakeFromBEBytes(uuid_bytes);
|
||||
}
|
||||
|
||||
bool uuid_equal(const Uuid *uu1, const Uuid *uu2) {
|
||||
if (uu1 == NULL || uu2 == NULL) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(uu1, uu2, sizeof(Uuid)) == 0;
|
||||
}
|
||||
|
||||
bool uuid_is_system(const Uuid *uuid) {
|
||||
return uuid_equal(uuid, &system_uuid);
|
||||
}
|
||||
|
||||
bool uuid_is_invalid(const Uuid *uuid) {
|
||||
return !uuid || uuid_equal(uuid, &invalid_uuid);
|
||||
}
|
||||
|
||||
void uuid_to_string(const Uuid *uuid, char *buffer) {
|
||||
if (!uuid) {
|
||||
strcpy(buffer, "{NULL UUID}");
|
||||
return;
|
||||
}
|
||||
|
||||
// For reference...
|
||||
// {12345678-1234-5678-1234-567812345678}
|
||||
*buffer++ = '{';
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(Uuid); i++) {
|
||||
if ((i >= 4) && (i <= 10) && ((i % 2) == 0)) {
|
||||
*buffer++ = '-';
|
||||
}
|
||||
|
||||
buffer += snprintf(buffer, 3, "%02"PRIx8, ((uint8_t *)uuid)[i]);
|
||||
}
|
||||
*buffer++ = '}';
|
||||
*buffer = '\0';
|
||||
}
|
24
src/libutil/wscript
Normal file
24
src/libutil/wscript
Normal file
|
@ -0,0 +1,24 @@
|
|||
import waftools
|
||||
|
||||
|
||||
def build(bld):
|
||||
sources = bld.path.ant_glob('**/*.c')
|
||||
|
||||
def build_libutil(target, env):
|
||||
# Build the libutil directory using firmware environment
|
||||
bld.stlib(source=sources,
|
||||
target=target,
|
||||
includes=['.', 'includes'],
|
||||
use='pblibc_includes',
|
||||
env=env.derive())
|
||||
|
||||
bld(export_includes=['includes'], name='libutil_includes')
|
||||
|
||||
if (bld.variant not in ('test', 'test_rocky_emx')):
|
||||
build_libutil('libutil-cm0', bld.all_envs['cortex-m0'])
|
||||
|
||||
build_libutil('libutil', bld.env)
|
||||
build_libutil('libutil-32bit', bld.all_envs['32bit'])
|
||||
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue