aboutsummaryrefslogtreecommitdiff
path: root/sokol_fetch.h
diff options
context:
space:
mode:
authorAndre Weissflog <floooh@gmail.com>2022-05-15 17:20:52 +0200
committerGitHub <noreply@github.com>2022-05-15 17:20:52 +0200
commitb2e593137f6a5a1e8383d7f4ee891879470986af (patch)
treed81cebda1b790da450e6584f124a51bc14b3c08f /sokol_fetch.h
parentd634c620b207d34be5a7423771e911c6bd76063b (diff)
Add memory allocator callbacks to sokol headers. (#662)
Diffstat (limited to 'sokol_fetch.h')
-rw-r--r--sokol_fetch.h170
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) {