Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View 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)

View 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

View 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);

View 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);

View 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);

View 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)

View 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);

View 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);

View 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);

View 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);

View 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);

View 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)

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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)

View 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);

View 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);

View 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))

View 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

View 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);