diff options
| author | Andre Weissflog <floooh@gmail.com> | 2022-05-15 17:20:52 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-15 17:20:52 +0200 |
| commit | b2e593137f6a5a1e8383d7f4ee891879470986af (patch) | |
| tree | d81cebda1b790da450e6584f124a51bc14b3c08f /sokol_fetch.h | |
| parent | d634c620b207d34be5a7423771e911c6bd76063b (diff) | |
Add memory allocator callbacks to sokol headers. (#662)
Diffstat (limited to 'sokol_fetch.h')
| -rw-r--r-- | sokol_fetch.h | 170 |
1 files changed, 129 insertions, 41 deletions
diff --git a/sokol_fetch.h b/sokol_fetch.h index e067abb4..e8b1f0c7 100644 --- a/sokol_fetch.h +++ b/sokol_fetch.h @@ -16,8 +16,6 @@ Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_FETCH_API_DECL - public function declaration prefix (default: extern) @@ -793,6 +791,39 @@ the blocking traditional file IO functions, not for performance reasons. + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sfetch_setup(&(sfetch_desc_t){ + // ... + .allocator = { + .alloc = my_alloc, + .free = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_fetch.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where sfetch_setup() + was called, so you don't need to worry about thread-safety. + + FUTURE PLANS / V2.0 IDEA DUMP ============================= - An optional polling API (as alternative to callback API) @@ -837,6 +868,7 @@ distribution. */ #define SOKOL_FETCH_INCLUDED (1) +#include <stddef.h> // size_t #include <stdint.h> #include <stdbool.h> @@ -857,13 +889,30 @@ extern "C" { #endif +/* + sfetch_allocator_t + + Used in sfetch_desc_t to provide custom memory-alloc and -free functions + to sokol_fetch.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef void*(*sfetch_malloc_t)(size_t size, void* user_data); +typedef void(*sfetch_free_t)(void* ptr, void* user_data); + +typedef struct sfetch_allocator_t { + sfetch_malloc_t alloc; + sfetch_free_t free; + void* user_data; +} sfetch_allocator_t; + + /* configuration values for sfetch_setup() */ typedef struct sfetch_desc_t { - uint32_t _start_canary; - uint32_t max_requests; /* max number of active requests across all channels, default is 128 */ - uint32_t num_channels; /* number of channels to fetch requests in parallel, default is 1 */ - uint32_t num_lanes; /* max number of requests active on the same channel, default is 1 */ - uint32_t _end_canary; + uint32_t max_requests; /* max number of active requests across all channels (default: 128) */ + uint32_t num_channels; /* number of channels to fetch requests in parallel (default: 1) */ + uint32_t num_lanes; /* max number of requests active on the same channel (default: 1) */ + sfetch_allocator_t allocator; /* optional memory allocation overrides (default: malloc/free) */ } sfetch_desc_t; /* a request handle to identify an active fetch request, returned by sfetch_send() */ @@ -905,7 +954,6 @@ typedef void(*sfetch_callback_t)(const sfetch_response_t*); /* request parameters passed to sfetch_send() */ typedef struct sfetch_request_t { - uint32_t _start_canary; uint32_t channel; /* index of channel this request is assigned to (default: 0) */ const char* path; /* filesystem path or HTTP URL (required) */ sfetch_callback_t callback; /* response callback function pointer (required) */ @@ -914,7 +962,6 @@ typedef struct sfetch_request_t { uint32_t chunk_size; /* number of bytes to load per stream-block (optional) */ const void* user_data_ptr; /* pointer to a POD user-data block which will be memcpy'd(!) (optional) */ uint32_t user_data_size; /* size of user-data block (optional) */ - uint32_t _end_canary; } sfetch_request_t; /* setup sokol-fetch (can be called on multiple threads) */ @@ -961,6 +1008,12 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe /*--- IMPLEMENTATION ---------------------------------------------------------*/ #ifdef SOKOL_FETCH_IMPL #define SOKOL_FETCH_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfetch_desc_t.allocator to override memory allocation functions" +#endif + +#include <stdlib.h> /* malloc, free */ #include <string.h> /* memset, memcpy */ #ifndef SFETCH_MAX_PATH @@ -985,11 +1038,6 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe #include <assert.h> #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_MALLOC - #include <stdlib.h> - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif #ifndef SOKOL_LOG #ifdef SOKOL_DEBUG #include <stdio.h> @@ -1206,6 +1254,43 @@ static _sfetch_t* _sfetch; /*=== general helper functions and macros =====================================*/ #define _sfetch_def(val, def) (((val) == 0) ? (def) : (val)) +_SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (allocator->alloc) { + ptr = allocator->alloc(size, allocator->user_data); + } + else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sfetch_malloc(size_t size) { + return _sfetch_malloc_with_allocator(&_sfetch->desc.allocator, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) { + void* ptr = _sfetch_malloc(size); + _sfetch_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sfetch_free(void* ptr) { + if (_sfetch->desc.allocator.free) { + _sfetch->desc.allocator.free(ptr, _sfetch->desc.allocator.user_data); + } + else { + free(ptr); + } +} + _SOKOL_PRIVATE _sfetch_t* _sfetch_ctx(void) { return _sfetch; } @@ -1221,7 +1306,7 @@ _SOKOL_PRIVATE void _sfetch_path_copy(_sfetch_path_t* dst, const char* src) { dst->buf[SFETCH_MAX_PATH-1] = 0; } else { - memset(dst->buf, 0, SFETCH_MAX_PATH); + _sfetch_clear(dst->buf, SFETCH_MAX_PATH); } } @@ -1253,7 +1338,7 @@ _SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) _SOKOL_PRIVATE void _sfetch_ring_discard(_sfetch_ring_t* rb) { SOKOL_ASSERT(rb); if (rb->buf) { - SOKOL_FREE(rb->buf); + _sfetch_free(rb->buf); rb->buf = 0; } rb->head = 0; @@ -1269,9 +1354,8 @@ _SOKOL_PRIVATE bool _sfetch_ring_init(_sfetch_ring_t* rb, uint32_t num_slots) { /* one slot reserved to detect full vs empty */ rb->num = num_slots + 1; const size_t queue_size = rb->num * sizeof(sfetch_handle_t); - rb->buf = (uint32_t*) SOKOL_MALLOC(queue_size); + rb->buf = (uint32_t*) _sfetch_malloc_clear(queue_size); if (rb->buf) { - memset(rb->buf, 0, queue_size); return true; } else { @@ -1332,7 +1416,7 @@ _SOKOL_PRIVATE uint32_t _sfetch_ring_peek(const _sfetch_ring_t* rb, uint32_t ind _SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, const sfetch_request_t* request) { SOKOL_ASSERT(item && (0 == item->handle.id)); SOKOL_ASSERT(request && request->path); - memset(item, 0, sizeof(_sfetch_item_t)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); item->handle.id = slot_id; item->state = _SFETCH_STATE_INITIAL; item->channel = request->channel; @@ -1356,21 +1440,21 @@ _SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, co _SOKOL_PRIVATE void _sfetch_item_discard(_sfetch_item_t* item) { SOKOL_ASSERT(item && (0 != item->handle.id)); - memset(item, 0, sizeof(_sfetch_item_t)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); } _SOKOL_PRIVATE void _sfetch_pool_discard(_sfetch_pool_t* pool) { SOKOL_ASSERT(pool); if (pool->free_slots) { - SOKOL_FREE(pool->free_slots); + _sfetch_free(pool->free_slots); pool->free_slots = 0; } if (pool->gen_ctrs) { - SOKOL_FREE(pool->gen_ctrs); + _sfetch_free(pool->gen_ctrs); pool->gen_ctrs = 0; } if (pool->items) { - SOKOL_FREE(pool->items); + _sfetch_free(pool->items); pool->items = 0; } pool->size = 0; @@ -1385,17 +1469,15 @@ _SOKOL_PRIVATE bool _sfetch_pool_init(_sfetch_pool_t* pool, uint32_t num_items) pool->size = num_items + 1; pool->free_top = 0; const size_t items_size = pool->size * sizeof(_sfetch_item_t); - pool->items = (_sfetch_item_t*) SOKOL_MALLOC(items_size); + pool->items = (_sfetch_item_t*) _sfetch_malloc_clear(items_size); /* generation counters indexable by pool slot index, slot 0 is reserved */ const size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + pool->gen_ctrs = (uint32_t*) _sfetch_malloc_clear(gen_ctrs_size); SOKOL_ASSERT(pool->gen_ctrs); /* NOTE: it's not a bug to only reserve num_items here */ const size_t free_slots_size = num_items * sizeof(int); - pool->free_slots = (uint32_t*) SOKOL_MALLOC(free_slots_size); + pool->free_slots = (uint32_t*) _sfetch_malloc_clear(free_slots_size); if (pool->items && pool->free_slots) { - memset(pool->items, 0, items_size); - memset(pool->gen_ctrs, 0, gen_ctrs_size); /* never allocate the 0-th item, this is the reserved 'invalid item' */ for (uint32_t i = pool->size - 1; i >= 1; i--) { pool->free_slots[pool->free_top++] = i; @@ -1621,7 +1703,7 @@ _SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _s #if _SFETCH_PLATFORM_WINDOWS _SOKOL_PRIVATE bool _sfetch_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); + _sfetch_clear(dst, (size_t)dst_num_bytes); const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); if ((dst_needed > 0) && (dst_needed < dst_chars)) { @@ -2162,7 +2244,7 @@ _SOKOL_PRIVATE bool _sfetch_channel_send(_sfetch_channel_t* chn, uint32_t slot_i _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { sfetch_response_t response; - memset(&response, 0, sizeof(response)); + _sfetch_clear(&response, sizeof(response)); response.handle = item->handle; response.dispatched = (item->state == _SFETCH_STATE_DISPATCHED); response.fetched = (item->state == _SFETCH_STATE_FETCHED); @@ -2338,23 +2420,30 @@ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_reques return true; } +_SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + sfetch_desc_t res = *desc; + res.max_requests = _sfetch_def(desc->max_requests, 128); + res.num_channels = _sfetch_def(desc->num_channels, 1); + res.num_lanes = _sfetch_def(desc->num_lanes, 1); + return res; +} + /*=== PUBLIC API FUNCTIONS ===================================================*/ -SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc) { - SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); +SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc_) { + SOKOL_ASSERT(desc_); SOKOL_ASSERT(0 == _sfetch); - _sfetch = (_sfetch_t*) SOKOL_MALLOC(sizeof(_sfetch_t)); + + sfetch_desc_t desc = _sfetch_desc_defaults(desc_); + _sfetch = (_sfetch_t*) _sfetch_malloc_with_allocator(&desc.allocator, sizeof(_sfetch_t)); SOKOL_ASSERT(_sfetch); - memset(_sfetch, 0, sizeof(_sfetch_t)); _sfetch_t* ctx = _sfetch_ctx(); - ctx->desc = *desc; + _sfetch_clear(ctx, sizeof(_sfetch_t)); + ctx->desc = desc; ctx->setup = true; ctx->valid = true; /* replace zero-init items with default values */ - ctx->desc.max_requests = _sfetch_def(ctx->desc.max_requests, 128); - ctx->desc.num_channels = _sfetch_def(ctx->desc.num_channels, 1); - ctx->desc.num_lanes = _sfetch_def(ctx->desc.num_lanes, 1); if (ctx->desc.num_channels > SFETCH_MAX_CHANNELS) { ctx->desc.num_channels = SFETCH_MAX_CHANNELS; SOKOL_LOG("sfetch_setup: clamping num_channels to SFETCH_MAX_CHANNELS"); @@ -2381,7 +2470,7 @@ SOKOL_API_IMPL void sfetch_shutdown(void) { } _sfetch_pool_discard(&ctx->pool); ctx->setup = false; - SOKOL_FREE(ctx); + _sfetch_free(ctx); _sfetch = 0; } @@ -2417,7 +2506,6 @@ SOKOL_API_IMPL bool sfetch_handle_valid(sfetch_handle_t h) { SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { _sfetch_t* ctx = _sfetch_ctx(); SOKOL_ASSERT(ctx && ctx->setup); - SOKOL_ASSERT(request && (request->_start_canary == 0) && (request->_end_canary == 0)); const sfetch_handle_t invalid_handle = _sfetch_make_handle(0); if (!ctx->valid) { |