Переглянути джерело

Merge pull request #736 from floooh/sokol-fetch-range

sokol_fetch.h: replace discrete ptr/size pairs with sfetch_range_t

Same as has happend in other sokol headers already, discrete pointer/size pairs are replaced with a sfetch_range_t ptr/size pair struct. This affects:

- sfetch_request_t: .buffer_ptr/.buffer_size have become .buffer, and .user_data_ptr/.user_data_size have become .user_data
- sfetch_response_t: the .buffer_ptr/.fetched_size (no typo) has become .data, the .buffer_ptr/.buffer_size has become .buffer, and for .fetched_offset has been renamed to .data_offset
- in the function sfetch_bind_buffer() the buffer_ptr/buffer_size args have been merged into a single buffer arg

In sokol_app.h, the HTML5 specific structs sapp_html5_fetch_request and sapp_html5_fetch_response has been updated likewise.
Andre Weissflog 2 роки тому
батько
коміт
8263d54bce
6 змінених файлів з 205 додано та 169 видалено
  1. 15 0
      CHANGELOG.md
  2. 1 1
      README.md
  3. 26 25
      sokol_app.h
  4. 114 91
      sokol_fetch.h
  5. 4 0
      tests/CMakeLists.txt
  6. 45 52
      tests/functional/sokol_fetch_test.c

+ 15 - 0
CHANGELOG.md

@@ -1,5 +1,20 @@
 ## Updates
 
+- **05-Nov-2022** A breaking change in sokol_fetch.h, and a minor change in
+  sokol_app.h which should only break for very users:
+  - An ```sfetch_range_t``` ptr/size pair struct has been added to sokol_fetch.h,
+    and discrete ptr/size pairs have been replaced with sfetch_range_t
+    items. This affects the structs ```sfetch_request_t``` and ```sfetch_response_t```,
+    and the function ```sfetch_bind_buffer()```.
+  - The required changes in ```sfetch_response_t``` might be a bit non-obviois: To
+    access the fetched data, previous ```.buffer_ptr``` and ```.fetched_size```
+    was used. The fetched data is now accessible through an ```sfetch_range_t data```
+    item (```data.ptr``` and ```data.size```). The old ```.fetched_offset``` item
+    has been renamed to ```.data_offset``` to better conform with the new naming.
+  - The last two occurances of discrete ptr/size pairs in sokol_app.h now have also
+    been replaced with ```sapp_range_t``` items, this only affects the structs
+    ```sapp_html5_fetch_request``` and ```sapp_html5_fetch_response```.
+
 - **03-Nov-2022** The language bindings generation has been updated for Zig 0.10.0,
   and clang-14 (there was a minor change in the JSON ast-dump format).
   Many thanks to github user @kcbanner for the Zig PR!

+ 1 - 1
README.md

@@ -4,7 +4,7 @@ Simple
 [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
 cross-platform libraries for C and C++, written in C.
 
-[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**02-Oct-2022** new header: sokol_spine.h)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**05-Nov-2022** breaking changes in sokol_fetch.h, and for some users in sokol_app.h)
 
 [![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)
 

+ 26 - 25
sokol_app.h

@@ -633,8 +633,10 @@
         sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){
             .dropped_file_index = 0,
             .callback = fetch_cb
-            .buffer_ptr = buf,
-            .buffer_size = buf_size,
+            .buffer = {
+                .ptr = buf,
+                .size = sizeof(buf)
+            },
             .user_data = ...
         });
 
@@ -648,9 +650,9 @@
             // IMPORTANT: check if the loading operation actually succeeded:
             if (response->succeeded) {
                 // the size of the loaded file:
-                const uint32_t num_bytes = response->fetched_size;
+                const size_t num_bytes = response->data.size;
                 // and the pointer to the data (same as 'buf' in the fetch-call):
-                const void* ptr = response->buffer_ptr;
+                const void* ptr = response->data.ptr;
             }
             else {
                 // on error check the error code:
@@ -1531,21 +1533,19 @@ typedef enum sapp_html5_fetch_error {
 } sapp_html5_fetch_error;
 
 typedef struct sapp_html5_fetch_response {
-    bool succeeded;         /* true if the loading operation has succeeded */
+    bool succeeded;         // true if the loading operation has succeeded
     sapp_html5_fetch_error error_code;
-    int file_index;         /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */
-    uint32_t fetched_size;  /* size in bytes of loaded data */
-    void* buffer_ptr;       /* pointer to user-provided buffer which contains the loaded data */
-    uint32_t buffer_size;   /* size of user-provided buffer (buffer_size >= fetched_size) */
-    void* user_data;        /* user-provided user data pointer */
+    int file_index;         // index of the dropped file (0..sapp_get_num_dropped_filed()-1)
+    sapp_range data;        // pointer and size of the fetched data (data.ptr == buffer.ptr, data.size <= buffer.size)
+    sapp_range buffer;      // the user-provided buffer ptr/size pair (buffer.ptr == data.ptr, buffer.size >= data.size)
+    void* user_data;        // user-provided user data pointer
 } sapp_html5_fetch_response;
 
 typedef struct sapp_html5_fetch_request {
-    int dropped_file_index;                 /* 0..sapp_get_num_dropped_files()-1 */
-    void (*callback)(const sapp_html5_fetch_response*);     /* response callback function pointer (required) */
-    void* buffer_ptr;                       /* pointer to buffer to load data into */
-    uint32_t buffer_size;                   /* size in bytes of buffer */
-    void* user_data;                        /* optional userdata pointer */
+    int dropped_file_index; // 0..sapp_get_num_dropped_files()-1
+    void (*callback)(const sapp_html5_fetch_response*);     // response callback function pointer (required)
+    sapp_range buffer;      // ptr/size of a memory buffer to load the data into
+    void* user_data;        // optional userdata pointer
 } sapp_html5_fetch_request;
 
 /*
@@ -4574,9 +4574,10 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int
     response.succeeded = (0 != success);
     response.error_code = (sapp_html5_fetch_error) error_code;
     response.file_index = index;
-    response.fetched_size = fetched_size;
-    response.buffer_ptr = buf_ptr;
-    response.buffer_size = buf_size;
+    response.data.ptr = buf_ptr;
+    response.data.size = fetched_size;
+    response.buffer.ptr = buf_ptr;
+    response.buffer.size = buf_size;
     response.user_data = user_data;
     callback(&response);
 }
@@ -12159,15 +12160,15 @@ SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request
     SOKOL_ASSERT(_sapp.drop.enabled);
     SOKOL_ASSERT(request);
     SOKOL_ASSERT(request->callback);
-    SOKOL_ASSERT(request->buffer_ptr);
-    SOKOL_ASSERT(request->buffer_size > 0);
+    SOKOL_ASSERT(request->buffer.ptr);
+    SOKOL_ASSERT(request->buffer.size > 0);
     #if defined(_SAPP_EMSCRIPTEN)
         const int index = request->dropped_file_index;
         sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR;
         if ((index < 0) || (index >= _sapp.drop.num_files)) {
             error_code = SAPP_HTML5_FETCH_ERROR_OTHER;
         }
-        if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) {
+        if (sapp_html5_get_dropped_file_size(index) > request->buffer.size) {
             error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL;
         }
         if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) {
@@ -12176,15 +12177,15 @@ SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request
                 (int)error_code,
                 request->callback,
                 0, // fetched_size
-                request->buffer_ptr,
-                request->buffer_size,
+                (void*)request->buffer.ptr,
+                request->buffer.size,
                 request->user_data);
         }
         else {
             sapp_js_fetch_dropped_file(index,
                 request->callback,
-                request->buffer_ptr,
-                request->buffer_size,
+                (void*)request->buffer.ptr,
+                request->buffer.size,
                 request->user_data);
         }
     #else

+ 114 - 91
sokol_fetch.h

@@ -87,8 +87,19 @@
         sfetch_send(&(sfetch_request_t){
             .path = "my_file.txt",
             .callback = response_callback,
-            .buffer_ptr = buf,
-            .buffer_size = sizeof(buf)
+            .buffer = {
+                .ptr = buf,
+                .size = sizeof(buf)
+            }
+        });
+
+        When initialization from a 'value type', the buffer item can be initialized with
+        the SFETCH_RANGE() helper macro:
+
+        sfetch_send(&(sfetch_request_t){
+            .path = "my_file.txt",
+            .callback = response_callback,
+            .buffer = SFETCH_RANGE(buf)
         });
 
     (3) write a 'response-callback' function, this will be called whenever
@@ -97,10 +108,10 @@
 
         void response_callback(const sfetch_response_t* response) {
             if (response->fetched) {
-                // data has been loaded, and is available via
-                // 'buffer_ptr' and 'fetched_size':
-                const void* data = response->buffer_ptr;
-                uint64_t num_bytes = response->fetched_size;
+                // data has been loaded, and is available via the
+                // sfetch_range_t struct item 'data':
+                const void* ptr = response->data.ptr;
+                size_t num_bytes = response->data.size;
             }
             if (response->finished) {
                 // the 'finished'-flag is the catch-all flag for when the request
@@ -242,7 +253,7 @@
             important information how streaming works if the web server
             is serving compressed data.
 
-        - buffer_ptr, buffer_size (void*, uint64_t, optional)
+        - buffer (sfetch_range_t)
             This is a optional pointer/size pair describing a chunk of memory where
             data will be loaded into (if no buffer is provided upfront, this
             must happen in the response callback). If a buffer is provided,
@@ -250,15 +261,15 @@
             is zero), or the *uncompressed* data for one downloaded chunk
             (if chunk_size is > 0).
 
-        - user_data_ptr, user_data_size (const void*, uint32_t, both optional)
-            user_data_ptr and user_data_size describe an optional POD (plain-old-data)
-            associated with the request which will be copied(!) into an internal
-            memory block. The maximum default size of this memory block is
-            128 bytes (but can be overridden by defining SFETCH_MAX_USERDATA_UINT64
-            before including the notification, note that this define is in
-            "number of uint64_t", not number of bytes). The user-data
-            block is 8-byte aligned, and will be copied via memcpy() (so don't
-            put any C++ "smart members" in there).
+        - user_data (sfetch_range_t)
+            The user_data ptr/size range struct describe an optional POD blob
+            (plain-old-data) associated with the request which will be copied(!)
+            into an internal memory block. The maximum default size of this
+            memory block is 128 bytes (but can be overridden by defining
+            SFETCH_MAX_USERDATA_UINT64 before including the notification, note
+            that this define is in "number of uint64_t", not number of bytes).
+            The user-data block is 8-byte aligned, and will be copied via
+            memcpy() (so don't put any C++ "smart members" in there).
 
     NOTE that request handles are strictly thread-local and only unique
     within the thread the handle was created on, and all function calls
@@ -345,9 +356,9 @@
     ---------------------------------------------
     Continues a paused request, counterpart to the sfetch_pause() function.
 
-    void sfetch_bind_buffer(sfetch_handle_t request, void* buffer_ptr, uint64_t buffer_size)
+    void sfetch_bind_buffer(sfetch_handle_t request, sfetch_range_t buffer)
     ----------------------------------------------------------------------------------------
-    This "binds" a new buffer (pointer/size pair) to an active request. The
+    This "binds" a new buffer (as pointer/size pair) to an active request. The
     function *must* be called from inside the response-callback, and there
     must not already be another buffer bound.
 
@@ -474,9 +485,9 @@
         The response callback will be called so that the user-code can
         process the loaded data using the following sfetch_response_t struct members:
 
-            - fetched_size: the number of bytes in the provided buffer
-            - buffer_ptr: pointer to the start of fetched data
-            - fetched_offset: the byte offset of the loaded data chunk in the
+            - data.ptr: pointer to the start of fetched data
+            - data.size: the number of bytes in the provided buffer
+            - data_offset: the byte offset of the loaded data chunk in the
               overall file (this is only set to a non-zero value in a streaming
               scenario)
 
@@ -500,10 +511,10 @@
             void response_callback(const sfetch_response_t* response) {
                 if (response->fetched) {
                     // request is in FETCHED state, the loaded data is available
-                    // in .buffer_ptr, and the number of bytes that have been
-                    // loaded in .fetched_size:
-                    const void* data = response->buffer_ptr;
-                    const uint64_t num_bytes = response->fetched_size;
+                    // in .data.ptr, and the number of bytes that have been
+                    // loaded in .data.size:
+                    const void* data = response->data.ptr;
+                    size_t num_bytes = response->data.size;
                 }
                 if (response->finished) {
                     // the finished flag is set either when all data
@@ -910,6 +921,29 @@
 extern "C" {
 #endif
 
+/*
+    sfetch_range_t
+
+    A pointer-size pair struct to pass memory ranges into and out of sokol-fetch.
+    When initialized from a value type (array or struct) you can use the
+    SFETCH_RANGE() helper macro to build an sfetch_range_t struct.
+*/
+typedef struct sfetch_range_t {
+    const void* ptr;
+    size_t size;
+} sfetch_range_t;
+
+// disabling this for every includer isn't great, but the warnings are also quite pointless
+#if defined(_MSC_VER)
+#pragma warning(disable:4221)   // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y'
+#pragma warning(disable:4204)   // VS2015: nonstandard extension used: non-constant aggregate initializer
+#endif
+#if defined(__cplusplus)
+#define SFETCH_RANGE(x) sfetch_range_t{ &x, sizeof(x) }
+#else
+#define SFETCH_RANGE(x) (sfetch_range_t){ &x, sizeof(x) }
+#endif
+
 /*
     sfetch_allocator_t
 
@@ -937,11 +971,11 @@ typedef struct sfetch_logger_t {
 
 /* configuration values for sfetch_setup() */
 typedef struct sfetch_desc_t {
-    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_logger_t logger;         /* optional log function overrides (default: SOKOL_LOG(message)) */
+    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_logger_t logger;         // optional log function overrides (default: SOKOL_LOG(message))
 } sfetch_desc_t;
 
 /* a request handle to identify an active fetch request, returned by sfetch_send() */
@@ -960,22 +994,21 @@ typedef enum sfetch_error_t {
 
 /* the response struct passed to the response callback */
 typedef struct sfetch_response_t {
-    sfetch_handle_t handle;         /* request handle this response belongs to */
-    bool dispatched;                /* true when request is in DISPATCHED state (lane has been assigned) */
-    bool fetched;                   /* true when request is in FETCHED state (fetched data is available) */
-    bool paused;                    /* request is currently in paused state */
-    bool finished;                  /* this is the last response for this request */
-    bool failed;                    /* request has failed (always set together with 'finished') */
-    bool cancelled;                 /* request was cancelled (always set together with 'finished') */
-    sfetch_error_t error_code;      /* more detailed error code when failed is true */
-    uint32_t channel;               /* the channel which processes this request */
-    uint32_t lane;                  /* the lane this request occupies on its channel */
-    const char* path;               /* the original filesystem path of the request (FIXME: this is unsafe, wrap in API call?) */
-    void* user_data;                /* pointer to read/write user-data area (FIXME: this is unsafe, wrap in API call?) */
-    uint32_t fetched_offset;        /* current offset of fetched data chunk in file data */
-    uint32_t fetched_size;          /* size of fetched data chunk in number of bytes */
-    void* buffer_ptr;               /* pointer to buffer with fetched data */
-    uint32_t buffer_size;           /* overall buffer size (may be >= than fetched_size!) */
+    sfetch_handle_t handle;         // request handle this response belongs to
+    bool dispatched;                // true when request is in DISPATCHED state (lane has been assigned)
+    bool fetched;                   // true when request is in FETCHED state (fetched data is available)
+    bool paused;                    // request is currently in paused state
+    bool finished;                  // this is the last response for this request
+    bool failed;                    // request has failed (always set together with 'finished')
+    bool cancelled;                 // request was cancelled (always set together with 'finished')
+    sfetch_error_t error_code;      // more detailed error code when failed is true
+    uint32_t channel;               // the channel which processes this request
+    uint32_t lane;                  // the lane this request occupies on its channel
+    const char* path;               // the original filesystem path of the request
+    void* user_data;                // pointer to read/write user-data area
+    uint32_t data_offset;           // current offset of fetched data chunk in the overall file data
+    sfetch_range_t data;            // the fetched data as ptr/size pair (data.ptr == buffer.ptr, data.size <= buffer.size)
+    sfetch_range_t buffer;          // the user-provided buffer which holds the fetched data
 } sfetch_response_t;
 
 /* response callback function signature */
@@ -983,14 +1016,12 @@ typedef void(*sfetch_callback_t)(const sfetch_response_t*);
 
 /* request parameters passed to sfetch_send() */
 typedef struct sfetch_request_t {
-    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) */
-    void* buffer_ptr;               /* buffer pointer where data will be loaded into (optional) */
-    uint32_t buffer_size;           /* buffer size in number of bytes (optional) */
-    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 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)
+    uint32_t chunk_size;            // number of bytes to load per stream-block (optional)
+    sfetch_range_t buffer;          // a memory buffer where the data will be loaded into (optional)
+    sfetch_range_t user_data;       // ptr/size of a POD user data block which will be memcpy'd (optional)
 } sfetch_request_t;
 
 /* setup sokol-fetch (can be called on multiple threads) */
@@ -1014,7 +1045,7 @@ SOKOL_FETCH_API_DECL bool sfetch_handle_valid(sfetch_handle_t h);
 SOKOL_FETCH_API_DECL void sfetch_dowork(void);
 
 /* bind a data buffer to a request (request must not currently have a buffer bound, must be called from response callback */
-SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, void* buffer_ptr, uint32_t buffer_size);
+SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer);
 /* clear the 'buffer binding' of a request, returns previous buffer pointer (can be 0), must be called from response callback */
 SOKOL_FETCH_API_DECL void* sfetch_unbind_buffer(sfetch_handle_t h);
 /* cancel a request that's in flight (will call response callback with .cancelled + .finished) */
@@ -1127,11 +1158,6 @@ typedef struct _sfetch_path_t {
     char buf[SFETCH_MAX_PATH];
 } _sfetch_path_t;
 
-typedef struct _sfetch_buffer_t {
-    uint8_t* ptr;
-    uint32_t size;
-} _sfetch_buffer_t;
-
 /* a thread with incoming and outgoing message queue syncing */
 #if _SFETCH_PLATFORM_POSIX
 typedef struct {
@@ -1179,7 +1205,7 @@ typedef struct {
     sfetch_error_t error_code;
     bool finished;
     /* user thread only */
-    uint32_t user_data_size;
+    size_t user_data_size;
     uint64_t user_data[SFETCH_MAX_USERDATA_UINT64];
 } _sfetch_item_user_t;
 
@@ -1220,7 +1246,7 @@ typedef struct {
     uint32_t lane;
     uint32_t chunk_size;
     sfetch_callback_t callback;
-    _sfetch_buffer_t buffer;
+    sfetch_range_t buffer;
 
     /* updated by IO-thread, off-limits to user thread */
     _sfetch_item_thread_t thread;
@@ -1253,8 +1279,7 @@ typedef struct {
 /* an IO channel with its own IO thread */
 struct _sfetch_t;
 typedef struct {
-    struct _sfetch_t* ctx;  /* back-pointer to thread-local _sfetch state pointer,
-                               since this isn't accessible from the IO threads */
+    struct _sfetch_t* ctx;  // back-pointer to thread-local _sfetch state pointer, since this isn't accessible from the IO threads
     _sfetch_ring_t free_lanes;
     _sfetch_ring_t user_sent;
     _sfetch_ring_t user_incoming;
@@ -1470,18 +1495,17 @@ _SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, co
     item->chunk_size = request->chunk_size;
     item->lane = _SFETCH_INVALID_LANE;
     item->callback = request->callback;
-    item->buffer.ptr = (uint8_t*) request->buffer_ptr;
-    item->buffer.size = request->buffer_size;
+    item->buffer = request->buffer;
     item->path = _sfetch_path_make(request->path);
     #if !_SFETCH_PLATFORM_EMSCRIPTEN
     item->thread.file_handle = _SFETCH_INVALID_FILE_HANDLE;
     #endif
-    if (request->user_data_ptr &&
-        (request->user_data_size > 0) &&
-        (request->user_data_size <= (SFETCH_MAX_USERDATA_UINT64*8)))
+    if (request->user_data.ptr &&
+        (request->user_data.size > 0) &&
+        (request->user_data.size <= (SFETCH_MAX_USERDATA_UINT64*8)))
     {
-        item->user.user_data_size = request->user_data_size;
-        memcpy(item->user.user_data, request->user_data_ptr, request->user_data_size);
+        item->user.user_data_size = request->user_data.size;
+        memcpy(item->user.user_data, request->user_data.ptr, request->user_data.size);
     }
 }
 
@@ -1934,7 +1958,7 @@ _SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) {
     _sfetch_state_t state;
     _sfetch_path_t* path;
     _sfetch_item_thread_t* thread;
-    _sfetch_buffer_t* buffer;
+    sfetch_range_t* buffer;
     uint32_t chunk_size;
     {
         _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id);
@@ -2003,7 +2027,7 @@ _SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) {
                     }
                 }
                 if (!thread->failed) {
-                    if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, buffer->ptr)) {
+                    if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, (void*)buffer->ptr)) {
                         thread->fetched_size = bytes_to_read;
                         thread->fetched_offset += bytes_to_read;
                     }
@@ -2123,7 +2147,7 @@ void _sfetch_emsc_send_get_request(uint32_t slot_id, _sfetch_item_t* item) {
             SOKOL_ASSERT(bytes_to_read > 0);
             offset = item->thread.http_range_offset;
         }
-        sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, item->buffer.ptr, item->buffer.size);
+        sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, (void*)item->buffer.ptr, item->buffer.size);
     }
 }
 
@@ -2304,10 +2328,10 @@ _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) {
     response.lane = item->lane;
     response.path = item->path.buf;
     response.user_data = item->user.user_data;
-    response.fetched_offset = item->user.fetched_offset - item->user.fetched_size;
-    response.fetched_size = item->user.fetched_size;
-    response.buffer_ptr = item->buffer.ptr;
-    response.buffer_size = item->buffer.size;
+    response.data_offset = item->user.fetched_offset - item->user.fetched_size;
+    response.data.ptr = item->buffer.ptr;
+    response.data.size = item->user.fetched_size;
+    response.buffer = item->buffer;
     item->callback(&response);
 }
 
@@ -2444,20 +2468,20 @@ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_reques
             SFETCH_LOG("_sfetch_validate_request: request.callback missing");
             return false;
         }
-        if (req->chunk_size > req->buffer_size) {
-            SFETCH_LOG("_sfetch_validate_request: request.chunk_size is greater request.buffer_size)");
+        if (req->chunk_size > req->buffer.size) {
+            SFETCH_LOG("_sfetch_validate_request: request.chunk_size is greater request.buffer.size)");
             return false;
         }
-        if (req->user_data_ptr && (req->user_data_size == 0)) {
-            SFETCH_LOG("_sfetch_validate_request: request.user_data_ptr is set, but request.user_data_size is null");
+        if (req->user_data.ptr && (req->user_data.size == 0)) {
+            SFETCH_LOG("_sfetch_validate_request: request.user_data.ptr is set, but request.user_data.size is null");
             return false;
         }
-        if (!req->user_data_ptr && (req->user_data_size > 0)) {
-            SFETCH_LOG("_sfetch_validate_request: request.user_data_ptr is null, but request.user_data_size is not");
+        if (!req->user_data.ptr && (req->user_data.size > 0)) {
+            SFETCH_LOG("_sfetch_validate_request: request.user_data.ptr is null, but request.user_data.size is not");
             return false;
         }
-        if (req->user_data_size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) {
-            SFETCH_LOG("_sfetch_validate_request: request.user_data_size is too big (see SFETCH_MAX_USERDATA_UINT64");
+        if (req->user_data.size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) {
+            SFETCH_LOG("_sfetch_validate_request: request.user_data.size is too big (see SFETCH_MAX_USERDATA_UINT64");
             return false;
         }
     #else
@@ -2595,15 +2619,15 @@ SOKOL_API_IMPL void sfetch_dowork(void) {
     ctx->in_callback = false;
 }
 
-SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, void* buffer_ptr, uint32_t buffer_size) {
+SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer) {
     _sfetch_t* ctx = _sfetch_ctx();
     SOKOL_ASSERT(ctx && ctx->valid);
     SOKOL_ASSERT(ctx->in_callback);
+    SOKOL_ASSERT(buffer.ptr && (buffer.size > 0));
     _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id);
     if (item) {
         SOKOL_ASSERT((0 == item->buffer.ptr) && (0 == item->buffer.size));
-        item->buffer.ptr = (uint8_t*) buffer_ptr;
-        item->buffer.size = buffer_size;
+        item->buffer = buffer;
     }
 }
 
@@ -2613,7 +2637,7 @@ SOKOL_API_IMPL void* sfetch_unbind_buffer(sfetch_handle_t h) {
     SOKOL_ASSERT(ctx->in_callback);
     _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id);
     if (item) {
-        void* prev_buf_ptr = item->buffer.ptr;
+        void* prev_buf_ptr = (void*)item->buffer.ptr;
         item->buffer.ptr = 0;
         item->buffer.size = 0;
         return prev_buf_ptr;
@@ -2653,5 +2677,4 @@ SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) {
         item->user.cancel = true;
     }
 }
-
 #endif /* SOKOL_FETCH_IMPL */

+ 4 - 0
tests/CMakeLists.txt

@@ -29,6 +29,7 @@ else()
     message(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME")
 endif()
 
+message(">> CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
 message(">> SOKOL_BACKEND: ${SOKOL_BACKEND}")
 message(">> SOKOL_FORCE_EGL: ${SOKOL_FORCE_EGL}")
 if (OSX_IOS OR OSX_MACOS)
@@ -213,6 +214,9 @@ target_include_directories(spine SYSTEM PUBLIC ext/spine-runtimes/spine-c/spine-
 if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
     target_compile_options(spine PRIVATE /wd4267 /wd4244)   # conversion from 'x' to 'y' possible loss of data
 endif()
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    target_compile_options(spine PRIVATE -Wno-shorten-64-to-32)
+endif()
 
 
 add_library(nuklear ext/nuklear.c)

+ 45 - 52
tests/functional/sokol_fetch_test.c

@@ -61,8 +61,7 @@ UTEST(sokol_fetch, item_init_discard) {
         .channel = 4,
         .path = "hello_world.txt",
         .chunk_size = 128,
-        .user_data_ptr = &user_data,
-        .user_data_size = sizeof(user_data)
+        .user_data = SFETCH_RANGE(user_data)
     };
     _sfetch_item_t item = zeroed_item;
     uint32_t slot_id = _sfetch_make_id(1, 1);
@@ -102,8 +101,7 @@ UTEST(sokol_fetch, item_init_userdata_overflow) {
     uint8_t big_data[128] = { 0xFF };
     sfetch_request_t request = {
         .path = "hello_world.txt",
-        .user_data_ptr = big_data,
-        .user_data_size = sizeof(big_data)
+        .user_data = SFETCH_RANGE(big_data),
     };
     _sfetch_item_t item = zeroed_item;
     _sfetch_item_init(&item, _sfetch_make_id(1, 1), &request);
@@ -137,8 +135,7 @@ UTEST(sokol_fetch, pool_alloc_free) {
     _sfetch_pool_init(&pool, num_items);
     uint32_t slot_id = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){
         .path = "hello_world.txt",
-        .buffer_ptr = buf,
-        .buffer_size = sizeof(buf)
+        .buffer = SFETCH_RANGE(buf),
     });
     T(slot_id == 0x00010001);
     T(pool.items[1].state == _SFETCH_STATE_ALLOCATED);
@@ -381,8 +378,7 @@ UTEST(sokol_fetch, fail_open) {
     sfetch_handle_t h = sfetch_send(&(sfetch_request_t){
         .path = "non_existing_file.txt",
         .callback = fail_open_callback,
-        .buffer_ptr = fail_open_buffer,
-        .buffer_size = sizeof(fail_open_buffer)
+        .buffer = SFETCH_RANGE(fail_open_buffer),
     });
     fail_open_passed = false;
     int frame_count = 0;
@@ -407,10 +403,11 @@ static void load_file_fixed_buffer_callback(const sfetch_response_t* response) {
     // when loading the whole file at once, the fetched state
     // is the best place to grab/process the data
     if (response->fetched) {
-        if ((response->fetched_offset == 0) &&
-            (response->fetched_size == combatsignal_file_size) &&
-            (response->buffer_ptr == load_file_buf) &&
-            (response->buffer_size == sizeof(load_file_buf)) &&
+        if ((response->data_offset == 0) &&
+            (response->data.ptr == load_file_buf) &&
+            (response->data.size == combatsignal_file_size) &&
+            (response->buffer.ptr == load_file_buf) &&
+            (response->buffer.size == sizeof(load_file_buf)) &&
             response->finished)
         {
             load_file_fixed_buffer_passed = true;
@@ -428,8 +425,7 @@ UTEST(sokol_fetch, load_file_fixed_buffer) {
     sfetch_handle_t h = sfetch_send(&(sfetch_request_t){
         .path = "comsi.s3m",
         .callback = load_file_fixed_buffer_callback,
-        .buffer_ptr = load_file_buf,
-        .buffer_size = sizeof(load_file_buf)
+        .buffer = SFETCH_RANGE(load_file_buf),
     });
     // simulate a frame-loop for as long as the request is in flight, normally
     // the sfetch_dowork() function is just called somewhere in the frame
@@ -450,21 +446,23 @@ static bool load_file_unknown_size_opened_passed;
 static bool load_file_unknown_size_fetched_passed;
 static void load_file_unknown_size_callback(const sfetch_response_t* response) {
     if (response->dispatched) {
-        if ((response->fetched_offset == 0) &&
-            (response->fetched_size == 0) &&
-            (response->buffer_ptr == 0) &&
-            (response->buffer_size == 0) &&
+        if ((response->data_offset == 0) &&
+            (response->data.ptr == 0) &&
+            (response->data.size == 0) &&
+            (response->buffer.ptr == 0) &&
+            (response->buffer.size == 0) &&
             !response->finished)
         {
             load_file_unknown_size_opened_passed = true;
-            sfetch_bind_buffer(response->handle, load_file_buf, sizeof(load_file_buf));
+            sfetch_bind_buffer(response->handle, SFETCH_RANGE(load_file_buf));
         }
     }
     else if (response->fetched) {
-        if ((response->fetched_offset == 0) &&
-            (response->fetched_size == combatsignal_file_size) &&
-            (response->buffer_ptr == load_file_buf) &&
-            (response->buffer_size == sizeof(load_file_buf)) &&
+        if ((response->data_offset == 0) &&
+            (response->data.ptr == load_file_buf) &&
+            (response->data.size == combatsignal_file_size) &&
+            (response->buffer.ptr == load_file_buf) &&
+            (response->buffer.size == sizeof(load_file_buf)) &&
             response->finished)
         {
             load_file_unknown_size_fetched_passed = true;
@@ -496,10 +494,11 @@ static bool load_file_no_buffer_opened_passed;
 static bool load_file_no_buffer_failed_passed;
 static void load_file_no_buffer_callback(const sfetch_response_t* response) {
     if (response->dispatched) {
-        if ((response->fetched_offset == 0) &&
-            (response->fetched_size == 0) &&
-            (response->buffer_ptr == 0) &&
-            (response->buffer_size == 0) &&
+        if ((response->data_offset == 0) &&
+            (response->data.ptr == 0) &&
+            (response->data.size == 0) &&
+            (response->buffer.ptr == 0) &&
+            (response->buffer.size == 0) &&
             !response->finished)
         {
             /* DO NOT provide a buffer here, see if that properly fails */
@@ -546,8 +545,7 @@ UTEST(sokol_fetch, load_file_too_small_buffer) {
     sfetch_handle_t h = sfetch_send(&(sfetch_request_t){
         .path = "comsi.s3m",
         .callback = load_file_too_small_callback,
-        .buffer_ptr = load_file_too_small_buf,
-        .buffer_size = sizeof(load_file_too_small_buf)
+        .buffer = SFETCH_RANGE(load_file_too_small_buf),
     });
     int frame_count = 0;
     const int max_frames = 10000;
@@ -570,9 +568,9 @@ static uint8_t load_chunk_buf[8192];
 static uint8_t load_file_chunked_content[500000];
 static void load_file_chunked_callback(const sfetch_response_t* response) {
     if (response->fetched) {
-        const uint8_t* src = response->buffer_ptr;
-        uint8_t* dst = &load_file_chunked_content[response->fetched_offset];
-        size_t num_bytes = response->fetched_size;
+        uint8_t* dst = &load_file_chunked_content[response->data_offset];
+        const uint8_t* src = response->data.ptr;
+        size_t num_bytes = response->data.size;
         memcpy(dst, src, num_bytes);
         if (response->finished) {
             load_file_chunked_passed = true;
@@ -590,16 +588,14 @@ UTEST(sokol_fetch, load_file_chunked) {
     sfetch_handle_t h0 = sfetch_send(&(sfetch_request_t){
         .path = "comsi.s3m",
         .callback = load_file_chunked_callback,
-        .buffer_ptr = load_chunk_buf,
-        .buffer_size = sizeof(load_chunk_buf),
+        .buffer = SFETCH_RANGE(load_chunk_buf),
         .chunk_size = sizeof(load_chunk_buf)
     });
     // request for all-in-one loading for comparing with the chunked buffer
     sfetch_handle_t h1 = sfetch_send(&(sfetch_request_t){
         .path = "comsi.s3m",
         .callback = load_file_fixed_buffer_callback,
-        .buffer_ptr = load_file_buf,
-        .buffer_size = sizeof(load_file_buf)
+        .buffer = SFETCH_RANGE(load_file_buf),
     });
     int frame_count = 0;
     const int max_frames = 10000;
@@ -622,9 +618,9 @@ int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES];
 static void load_file_lanes_callback(const sfetch_response_t* response) {
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
     if (response->fetched) {
-        const uint8_t* src = response->buffer_ptr;
-        uint8_t* dst = &load_file_lanes_content[response->lane][response->fetched_offset];
-        size_t num_bytes = response->fetched_size;
+        uint8_t* dst = &load_file_lanes_content[response->lane][response->data_offset];
+        const uint8_t* src = response->data.ptr;
+        size_t num_bytes = response->data.size;
         memcpy(dst, src, num_bytes);
         if (response->finished) {
             load_file_lanes_passed[response->lane]++;
@@ -645,8 +641,7 @@ UTEST(sokol_fetch, load_file_lanes) {
         h[lane] = sfetch_send(&(sfetch_request_t){
             .path = "comsi.s3m",
             .callback = load_file_lanes_callback,
-            .buffer_ptr = load_file_lanes_chunk_buf[lane],
-            .buffer_size = sizeof(load_file_lanes_chunk_buf[0]),
+            .buffer = { .ptr = load_file_lanes_chunk_buf[lane], .size = sizeof(load_file_lanes_chunk_buf[0]) },
             .chunk_size = sizeof(load_file_lanes_chunk_buf[0])
         });
     }
@@ -682,9 +677,9 @@ static void load_file_throttle_callback(const sfetch_response_t* response) {
     assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES));
     if (response->fetched) {
         assert(load_file_throttle_passed[response->lane] < LOAD_FILE_THROTTLE_NUM_PASSES);
-        const uint8_t* src = response->buffer_ptr;
-        uint8_t* dst = &load_file_throttle_content[load_file_throttle_passed[response->lane]][response->lane][response->fetched_offset];
-        size_t num_bytes = response->fetched_size;
+        uint8_t* dst = &load_file_throttle_content[load_file_throttle_passed[response->lane]][response->lane][response->data_offset];
+        const uint8_t* src = response->data.ptr;
+        size_t num_bytes = response->data.size;
         memcpy(dst, src, num_bytes);
         if (response->finished) {
             load_file_throttle_passed[response->lane]++;
@@ -707,8 +702,10 @@ UTEST(sokol_fetch, load_file_throttle) {
         h[i] = sfetch_send(&(sfetch_request_t){
             .path = "comsi.s3m",
             .callback = load_file_throttle_callback,
-            .buffer_ptr = load_file_throttle_chunk_buf[i % LOAD_FILE_THROTTLE_NUM_LANES],
-            .buffer_size = sizeof(load_file_throttle_chunk_buf[0]),
+            .buffer = {
+                .ptr = load_file_throttle_chunk_buf[i % LOAD_FILE_THROTTLE_NUM_LANES],
+                .size = sizeof(load_file_throttle_chunk_buf[0]),
+            },
             .chunk_size = sizeof(load_file_throttle_chunk_buf[0])
         });
         T(sfetch_handle_valid(h[i]));
@@ -743,9 +740,7 @@ void load_channel_callback(const sfetch_response_t* response) {
     assert(response->channel < LOAD_CHANNEL_NUM_CHANNELS);
     assert(!load_channel_passed[response->channel]);
     if (response->fetched) {
-        if ((response->fetched_size == combatsignal_file_size) &&
-            response->finished)
-        {
+        if ((response->data.size == combatsignal_file_size) && response->finished) {
             load_channel_passed[response->channel] = true;
         }
     }
@@ -764,8 +759,7 @@ UTEST(sokol_fetch, load_channel) {
             .path = "comsi.s3m",
             .channel = chn,
             .callback = load_channel_callback,
-            .buffer_ptr = load_channel_buf[chn],
-            .buffer_size = sizeof(load_channel_buf[chn])
+            .buffer = SFETCH_RANGE(load_channel_buf[chn]),
         });
     }
     bool done = false;
@@ -817,4 +811,3 @@ UTEST(sokol_fetch, load_file_cancel) {
     T(load_file_cancel_passed);
     sfetch_shutdown();
 }
-