Skip to content

Library configuration

This section describes functions and settings that control overall TrulyNatural SDK library behavior.

These are most useful on small platforms with limited memory, and relevant to the native C API only. Library configuration is not available in other language bindings.

Configuration

Use the config function to replace the heap memory allocator, specify a real-time clock function, or register an out-of-memory handler.

config

SNSR_API SnsrRC
snsrConfig(SnsrConfig config, ...);

Parameters and return value

config
Config configuration type. This determines the rest of the argument list.
OK for success, any other value indicates failure.

Global library configuration changes.

Use this variadic function to change the overall behavior of the TrulyNatural SDK. The required config parameter specifies which aspect to change.

Warning

  • Unless noted otherwise in the description of the config setting, you must call this function only when no library handles exist: Before any other API function at start-up, or after a call to tearDown.
  • This function is not thread-safe.
  • The number of additional parameters expected depend entirely on the config parameter. Failure to provide all the expected arguments with the expected types will lead to undefined behavior.

Config

chipComms

typedef uint32_t *(*SnsrChipComms)(uint32_t *in);

Security and licensing chip function.

Warning

Do not use this or CONFIG_SECURITY_CHIP unless recommended by Sensory.

config, CONFIG_SECURITY_CHIP

clock

typedef uint64_t (*SnsrClock)(void);

Custom clock function.

This should return the number of real-time monotonically-increasing clock ticks since an arbitrary start time.

Note

You'll typically only need to provide this clock function on small platforms that run on bare metal or very lightweight operating systems. The TrulyNatural SDK provides clock implementations on Linux, macOS, iOS, Android, and Windows.

config, CONFIG_CLOCK_FUNC

panic

typedef void (*SnsrPanic)(const char *format, va_list a);

Parameters and return value

format
printf() format string.
a
List of arguments that match the format specifiers in format.

Fatal error function.

When set with CONFIG_PANIC_FUNC, a function matching this signature will be called if the TrulyNatural SDK fails to allocate memory.

config, CONFIG_PANIC_FUNC

Config

typedef enum {
    SNSR_CONFIG_{name},
    ...
} SnsrConfig;

// Where {name} is from the table below, e.g.: SNSR_CONFIG_ALLOC

Values in this enum are used as the first argument to config and specify which additional arguments snsrConfig() expects.

Name Description
ALLOC

Replace the heap allocator used in the TrulyNatural SDK.

This replaces the standard C heap allocator with a custom library-specific allocator.

This function will fail if any previous TrulyNatural heap allocations have been made. Best practice is to configure the heap manager before calling any other library functions.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_ALLOC,
  SnsrAlloc_Vmt *vmt
);

vmt
Pointer to alloc_Vmt. This struct must remain valid at least until tearDown is called.

Example

#define SIZE_IN_BYTES 512000
static size_t segment[SIZE_IN_BYTES / sizeof(size_t)];

// in main()
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocTLSF(segment, sizeof(segment)));
config, alloc_Vmt, CONFIG_ALLOC_ADD_POOL
ALLOC_ADD_POOL

Add a memory pool to a custom heap allocator.

This adds an additional memory segment to use as backing store for the custom heap allocator configured with CONFIG_ALLOC.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_ALLOC_ADD_POOL,
  void *poolStart,
  size_t poolSizeInBytes
);

poolStart
Pointer to start of a read-write memory segment to add to the allocator store. This must be aligned to the words size of the CPU.
poolSizeInBytes
The number of bytes available at poolStart.

Example

Example:
#define POOL_SIZE 128000
static size_t pool[POOL_SIZE / sizeof(size_t)];
snsrConfig(SNSR_CONFIG_ALLOC_ADD_POOL, pool, sizeof(pool));
config, CONFIG_ALLOC
PANIC_FUNC

Set the fatal error function.

This panic function is called in the event of an unrecoverable error, such as the inability to allocate sufficient RAM.

The default behavior if panicFunc is NULL is to print an error message to stderr and then abort the process. This is undesirable on small embedded platforms with either no OS, or a very lightweight RTOS, that run from a single memory image.

If panicFunc is called, all TrulyNatural SDK handles are invalid and must be discarded. An effective way to do this is to use a custom heap allocator, which allows all open handles to be abandoned by reclaiming the heap segment.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_PANIC_FUNC,
  SnsrPanic panic
);

panic
panic callback function.

Example

Example:
#include <snsr.h>
#include <setjmp.h>
#include <stdio.h>

static jmp_buf PanicJmp;

static void
panicFunc(const char *format, va_list a)
{
  fprintf(stderr, "\nPANIC: ");
  vfprintf(stderr, format, a);
  fprintf(stderr, "\n\n");
  longjmp(PanicJmp, SNSR_RC_NO_MEMORY);
}

// In main() before any API calls that are not snsrConfig()
int r;
snsrConfig(SNSR_CONFIG_PANIC_FUNC, panicFunc);
if ((r = setjmp(PanicJmp))) {
  snsrTearDown();
  // handle out-of-memory case. Abandon heap, re-initialize.
}
config, heap allocators
STT_SUPPORT

Library Speech-To-Text support.

config returns OK if the SDK supports Speech-To-Text models, or NOT_SUPPORTED if STT support is not available.

You may retrieve this value even if active library handles exist.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_STT_SUPPORT
);
config, stt-support
THREAD_SUPPORT

Library multithreading support.

config returns OK if the SDK supports running multi-threaded models, or NOT_SUPPORTED if thread support is not available.

You may retrieve this value even if active library handles exist.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_THREAD_SUPPORT
);
config, thread-support
SECURITY_CHIP

Hardware security device communication.

Use iff recommended by Sensory.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_SECURITY_CHIP,
  SnsrChipComms commsFunc
);

commsFunc
chipComms callback function.

Example

Example:
uint32_t *chipComms(uint32_t *in) {
  // communicate with hardware
}

// ...
snsrConfig(SNSR_CONFIG_SECURITY_CHIP, chipComms);
config
CLOCK_FUNC

Set high-resolution monotonic time function and resolution.

Sets a function that must return a high-resolution monotonically-increasing value that measures clock time. This is used to limit the maximum amount of time spent in any one call to push with push-duration-limit, and for recognition pipeline profiling.

snsrConfig() parameters

SNSR_API SnsrRC
snsrConfig(
  SNSR_CONFIG_CLOCK_FUNC,
  SnsrClock clockFunc,
  double resolution
);

clockFunc
clock function, returns number of clock ticks.
resolution
The number of ticks per second.

Example

Example:
#include <snsr.h>
#include <time.h>

static uint64_t clockFunc(void) {
  struct timespec t;
  clock_gettime(CLOCK_MONOTONIC, &t);
  return (uint64_t)t.tv_sec * 1e9 + (uint64_t)t.tv_nsec;
}

int main(int argc, char *argv[]) {
  snsrConfig(SNSR_CONFIG_CLOCK_FUNC, clockFunc, 1e9);
  // ...
}
config, profile, push

Heap allocators

You can replace the heap memory allocator (malloc(), realloc(), free()) used by the TrulyNatural SDK with alternate implementations. This is most useful on small platforms where a standard library implementation is either not available, or not recommended, or RAM use must be strictly constrained.

By default this library uses the dynamic memory allocation functions defined in stdlib.h.

We provide allocator implementations allocBuddy, allocStdlib, allocTLSF (recommended), and an API for creating your own.

allocBuddy

SNSR_API const SnsrAlloc_Vmt *
snsrAllocBuddy(void *poolStart, size_t poolSizeInBytes);

Parameters and return value

poolStart
Points to the start of a read-write memory segment to use as the allocator backing store. This address must be aligned to the word size of the CPU.
poolSizeInBytes
the number of bytes available at poolStart.
A new custom allocator definition.

Buddy allocator.

This is an implementation of the Buddy memory allocation heap allocation algorithm. The size of returned blocks is the smallest power-of-two in which the requested block size fits. This allocator is fast and has low external fragmentation, but this comes at the expense of significant internal fragmentation (e.g. a 1025 byte request requires allocation of a 2048 byte block, wasting 1023 bytes).

This is a customization of the memsys5 allocator from SQLite that is in the Public Domain.

Example

#define POOL_SIZE 128000
static size_t pool[POOL_SIZE / sizeof(size_t)];

// in main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocBuddy(pool, sizeof(pool)));

config, CONFIG_ALLOC

allocLock

SNSR_API const SnsrAlloc_Vmt *
snsrAllocLock(const SnsrAlloc_Vmt *vmt);

Parameters and return value

vmt
A custom allocator definition.
A new custom allocator definition that is thread-safe.

Thread-safe allocator wrapper.

This takes a heap allocator and adds mutual exclusion locks to make it thread-safe.

This wrapper has no effect if the TrulyNatural SDK does not have thread support on the target platform: The function adds the lock wrapper only if snsrConfig(SNSR_CONFIG_THREAD_SUPPORT) == SNSR_RC_OK.

Example

#define POOL_SIZE 128000
static size_t pool[POOL_SIZE / sizeof(size_t)];

// in main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocLock(snsrAllocTLSF(pool, sizeof(pool))));

allocBuddy, allocStdlib, allocTLSF

allocPerf

SNSR_API const SnsrAlloc_Vmt *
snsrAllocPerf(const SnsrAlloc_Vmt *vmt);

Parameters and return value

vmt
A custom allocator definition.
A new custom allocator definition that gathers allocation statistics.

Statistics-gathering allocator wrapper.

Adds instrumentation to an allocator to determine the heap high-water mark, number of allocations, internal fragmentation overhead, etc.

Warning

This wrapper adds mutual exclusion locks to the wrapped allocator. Do not use with allocLock, as that will result in deadlock or undefined behavior.

Example

// in main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocPerf(snsrAllocStdlib()));

// application code
snsrTearDown();
snsrAllocPerfStats(snsrStreamFromFILE(stdout, SNSR_ST_MODE_WRITE));

allocBuddy, allocStdlib, allocTLSF, allocPerfStats

allocPerfStats

SNSR_API size_t
snsrAllocPerfStats(SnsrStream out);

Parameters and return value

out
A writable Stream to receive allocator statistics, or NULL to produce no output.
The smallest heap pool size required to repeat the instrumented allocation run.

Show allocator statistics

Returns the smallest heap pool size that could be sufficient to repeat the instrumented allocation run. To reduce the chance of out-of-heap errors, allocate a pool that is at least 10% larger than this minimum.

If the out Stream not NULL, this function writes heap allocator statistics in human-readable form to this stream.

Note

Requires use of allocPerf to gather statistics.

allocPerf

allocStdlib

SNSR_API const SnsrAlloc_Vmt *
snsrAllocStdlib(void);

Parameters and return value

A new custom allocator definition.

Standard C library allocator.

This is the standard library allocator: malloc(), realloc(), and free(). It is the default heap allocator used unless overridden with config.

Warning

This allocator is not thread-safe. If your application runs TrulyNatural SDK code from more than one execution thread you must add mutual exclusion locking by wrapping this allocator with allocLock.

Whether this allocator works with allocPerf depends on whether the standard C library for the platform has a malloc_usable_size() or malloc_size() implementation available. If in doubt, use allocTLSF for performance measurement instead.

Example

// in main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocStdlib());

allocLock, config, CONFIG_ALLOC

allocTLSF

recommended

SNSR_API const SnsrAlloc_Vmt *
snsrAllocTLSF(void *poolStart, size_t poolSizeInBytes);

Parameters and return value

poolStart
Points to the start of a read-write memory segment to use as the allocator backing store. This address must be aligned to the word size of the CPU.
poolSizeInBytes
the number of bytes available at poolStart.
A new custom allocator definition.

TLSF allocator.

We recommend this Two-level Segregated Fit allocator for embedded systems. It has O(1) cost for most operations, low overhead, low internal fragmentation, and supports multiple backing store pools for the heap.

This a customization of a TLSF library that is in the Public Domain.

Warning

This allocator is not thread-safe. If your application runs TrulyNatural SDK code from more than one execution thread you must add mutual exclusion locking by wrapping this allocator with allocLock.

Example

#define POOL_SIZE 128000
static size_t pool[POOL_SIZE / sizeof(size_t)];

// in main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, snsrAllocTLSF(pool, sizeof(pool)));

allocLock, config, CONFIG_ALLOC

User-defined allocator

You can create your own custom heap allocator by implementing the functions defined in alloc_Vmt, and then calling snsrConfig(SNSR_CONFIG_ALLOC, &vmt).

alloc_Vmt

typedef struct {
  void  *(*malloc)(void *ctx, size_t size); // (1)!
  void   (*free)(void *ctx, void *ptr); // (2)!
  void  *(*realloc)(void *ctx, void *ptr, size_t size); // (3)!
  size_t (*size)(void *ctx, void *ptr); // (4)!
  size_t (*roundUp)(void *ctx, size_t size); // (5)!
  size_t (*minPoolSize)(void *ctx, size_t maxAlloc, size_t maxCount); // (6)!
  SnsrAllocRC (*addPool)(void *ctx, void *pool, size_t size); // (7)!
  SnsrAllocRC (*setUp)(void *ctx); // (8)!
  SnsrAllocRC (*tearDown)(void *ctx); // (9)!
  void    *ctx; // Allocator context (10)
} SnsrAlloc_Vmt;
  1. required malloc
  2. required free
  3. required realloc
  4. optional size
  5. optional roundUp
  6. optional minPoolSize
  7. optional addPool
  8. optional setUp
  9. optional tearDown
  10. required ctx should point to the custom allocator data structure. It is passed to each implementation method as the first argument.

User-defined allocator virtual method table.

This method table defines a heap allocator to use in place of malloc(), realloc(), and free() from stdlib.h.

The struct must remain valid until a call to tearDown, or the application ends.

Example

This minimal example wraps the standard C library allocator.

static void *
vmtMalloc(void *ctx, size_t size) {
  return malloc(size);
}

static void *
vmtRealloc(void *ctx, void *ptr, size_t size) {
  return realloc(ptr, size);
}

static void
vmtFree(void *ctx, void *ptr)
{
  free(ptr);
}

static const SnsrAlloc_Vmt CustomAlloc = {
  vmtMalloc, vmtFree, vmtRealloc,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

// In main, before any other snsr* calls
snsrConfig(SNSR_CONFIG_ALLOC, &CustomAlloc);

config, CONFIG_ALLOC, AllocRC, malloc, free, realloc, size, roundUp, minPoolSize, addPool, setUp, tearDown

malloc

required

void *(*malloc)(void *ctx, size_t size);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
size
Number of bytes to allocate. size > 0.
A pointer to an aligned segment of writable memory, or NULL if memory could not be allocated.

Allocate memory from the heap.

This method should work like the standard library's malloc() function. It should return a pointer to at least size bytes of writable memory, aligned to at least the word size of the CPU.

alloc_Vmt

free

required

void (*free)(void *ctx, void *ptr);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
ptr
Pointer to previously allocated heap segment, as returned by malloc, or realloc.

Free memory allocated with the malloc or realloc methods.

This method should work like the standard library's free() function.

alloc_Vmt

realloc

required

void *(*realloc)(void *ctx, void *ptr, size_t size);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
ptr
Pointer to previously allocated heap segment, as returned by malloc, or realloc.
size
Number of bytes to allocate.
A pointer to an aligned segment of writable memory, or NULL if memory could not be allocated.

Allocate memory from the heap.

This method should work like the standard library's malloc() function. It should return a pointer to at least size bytes of writable memory, aligned to at least the word size of the CPU.

alloc_Vmt

size

optional

size_t (*size)(void *ctx, void *ptr);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
ptr
Pointer to previously allocated heap segment, as returned by malloc, or realloc.
The size of the memory block ptr points to.

Return the size of the ptr allocation.

This method should work like malloc_size() or malloc_usable_size(). It should return the actual size of the block of memory allocated for ptr. This will typically be a bit larger than the requested size.

Note

This method is optional and used only by allocPerf. If the allocation size is not available, set this method to NULL and avoid using allocPerf.

alloc_Vmt

roundUp

optional

size_t (*roundUp)(void *ctx, size_t size);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
size
Request size in bytes.
The size of the block that would be allocated for request size.

Return the allocation size for a given request size.

This returns the size of the block that would be allocated when requesting a size byte segment.

Note

This method is optional. If set to NULL, the block size will be the requested size rounded up to the next multiple of 8.

alloc_Vmt

minPoolSize

optional

size_t (*minPoolSize)(void *ctx, size_t maxAlloc, size_t maxCount);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
maxAlloc
The high water mark of bytes allocated by the instrumented application.
maxCount
The high water mark of the number of allocations made by the application.
An estimate of the smallest backing store pool that could satisfy maxAlloc and maxCount.

Return an estimate of the pool size required.

This method returns an estimate of the smallest allocator pool required to repeat an instrumented run.

Note

This method is optional. If set to NULL, the estimated pool size will not be available in allocPerfStats

alloc_Vmt

addPool

optional

SnsrAllocRC (*addPool)(void *ctx, void *pool, size_t size);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
pool
void * to the start of a read-write memory segment to use as additional allocator store. This address must be aligned to the word size of the CPU.
size
The number of bytes available at pool.
ALLOC_RC_OK upon success, any other AllocRC on failure.

Add a new backing store pool to the allocator.

This method adds a new pool to the backing store used for the heap.

Note

This method is optional. If set to NULL, the allocator does not support adding pools to the heap store.

alloc_Vmt, AllocRC

setUp

optional

SnsrAllocRC (*setUp)(void *ctx);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
ALLOC_RC_OK upon success, any other AllocRC on failure.

Initialize the memory allocator.

This method is called before any other to initialize the allocator.

Note

This method is optional. If set to NULL, no additional initialization is done.

alloc_Vmt, AllocRC

tearDown

optional

SnsrAllocRC (*tearDown)(void *ctx);

Parameters and return value

ctx
The value of the ctx member in alloc_Vmt.
ALLOC_RC_OK upon success, any other AllocRC on failure.

Shut down the memory allocator.

This method should deallocate any resources this allocator initialized.

Note

This method is optional. If set to NULL, no additional deallocation is done.

alloc_Vmt, AllocRC

AllocRC

typedef enum {
    SNSR_ALLOC_RC_{name},
    ...
} SnsrAllocRC;

// Where {name} is from the table below, e.g.: SNSR_ALLOC_RC_OK

This is the return code used by the implementation methods in alloc_Vmt.

alloc_Vmt

Name Description
OK Success.
ERROR Unspecified failure.
ALLOCATOR_EXISTS Allocator already configured.
ALLOC_FAILED Out of heap memory.
NO_FUNC Required implementation method is NULL.
NOT_SUPPORTED Not supported by this allocator.