diff --git a/docs/api_reference.md b/docs/api_reference.md index 252405453..b262c0772 100644 --- a/docs/api_reference.md +++ b/docs/api_reference.md @@ -116,6 +116,7 @@ This is a list of all functions in Cute Framework organized by categories. This - [acap](/array/acap.md) - [aclear](/array/aclear.md) - [acount](/array/acount.md) +- [adel](/array/adel.md) - [aend](/array/aend.md) - [afit](/array/afit.md) - [afree](/array/afree.md) diff --git a/docs/array/acap.md b/docs/array/acap.md index 76dc65ef9..bc304213d 100644 --- a/docs/array/acap.md +++ b/docs/array/acap.md @@ -39,4 +39,5 @@ a | The array. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/aclear.md b/docs/array/aclear.md index 8056dc1b5..0e77215c2 100644 --- a/docs/array/aclear.md +++ b/docs/array/aclear.md @@ -38,4 +38,5 @@ Returns zero. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/acount.md b/docs/array/acount.md index 184322153..53f9ff924 100644 --- a/docs/array/acount.md +++ b/docs/array/acount.md @@ -49,4 +49,5 @@ afree(a); [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/adel.md b/docs/array/adel.md new file mode 100644 index 000000000..2f27d9a78 --- /dev/null +++ b/docs/array/adel.md @@ -0,0 +1,44 @@ +[//]: # (This file is automatically generated by Cute Framework's docs parser.) +[//]: # (Do not edit this file by hand!) +[//]: # (See: https://github.com/RandyGaul/cute_framework/blob/master/samples/docs_parser.cpp) +[](../header.md ':include') + +# adel + +Category: [array](/api_reference?id=array) +GitHub: [cute_array.h](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_array.h) +--- + +Performs an unordered removal of the element at index i. + +```cpp +#define adel(a, i) cf_array_del(a) +``` + +Parameters | Description +--- | --- +a | The array. +i | The index of the element to remove. + +## Remarks + +The last element of the array is swapped into the index `i`. This is a constant time +operation, but does not preserve order of the array. + +## Related Pages + +[dyna](/array/dyna.md) +[asize](/array/asize.md) +[acount](/array/acount.md) +[acap](/array/acap.md) +[afit](/array/afit.md) +[apush](/array/apush.md) +[apop](/array/apop.md) +[aend](/array/aend.md) +[alast](/array/alast.md) +[aclear](/array/aclear.md) +[aset](/array/aset.md) +[arev](/array/arev.md) +[ahash](/array/ahash.md) +[afree](/array/afree.md) +[astatic](/array/astatic.md) diff --git a/docs/array/aend.md b/docs/array/aend.md index b851de15d..97dc7a9d4 100644 --- a/docs/array/aend.md +++ b/docs/array/aend.md @@ -34,4 +34,5 @@ a | The array. Can be `NULL`. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/afit.md b/docs/array/afit.md index 8e8303e95..7a94fe0b1 100644 --- a/docs/array/afit.md +++ b/docs/array/afit.md @@ -45,4 +45,5 @@ automatically re-assigned to a new pointer if the array was internally regrown. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/afree.md b/docs/array/afree.md index 96d0d3ab6..94a9ab2e2 100644 --- a/docs/array/afree.md +++ b/docs/array/afree.md @@ -38,4 +38,5 @@ Sets `a` to `NULL`. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/ahash.md b/docs/array/ahash.md index b36321660..9cc5f8bf8 100644 --- a/docs/array/ahash.md +++ b/docs/array/ahash.md @@ -34,4 +34,5 @@ a | The array. [aset](/array/aset.md) [arev](/array/arev.md) [afree](/array/afree.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/alast.md b/docs/array/alast.md index f8945c471..ec25c6543 100644 --- a/docs/array/alast.md +++ b/docs/array/alast.md @@ -34,4 +34,5 @@ a | The array. Can not be `NULL`. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/alen.md b/docs/array/alen.md index 29789703f..46e8291ae 100644 --- a/docs/array/alen.md +++ b/docs/array/alen.md @@ -52,5 +52,6 @@ afree(a); [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) [afree](/array/afree.md) diff --git a/docs/array/apop.md b/docs/array/apop.md index 8ea20f7ad..d2fe9c63d 100644 --- a/docs/array/apop.md +++ b/docs/array/apop.md @@ -42,4 +42,5 @@ The last element of the array is fetched and will be returned. The size of the a [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/apush.md b/docs/array/apush.md index 2d8169ffc..a45c92fad 100644 --- a/docs/array/apush.md +++ b/docs/array/apush.md @@ -58,4 +58,5 @@ dynamic array is allocated on-the-spot for you, and assigned back to `a`. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/arev.md b/docs/array/arev.md index 3718523a3..026b4b1dc 100644 --- a/docs/array/arev.md +++ b/docs/array/arev.md @@ -34,4 +34,5 @@ a | The array. Can be `NULL`. [aset](/array/aset.md) [afree](/array/afree.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/aset.md b/docs/array/aset.md index 4d5ae23f6..47db3ced4 100644 --- a/docs/array/aset.md +++ b/docs/array/aset.md @@ -39,4 +39,5 @@ Returns a pointer to `a`. `a` will automatically be reassigned to any new pointe [afree](/array/afree.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/asize.md b/docs/array/asize.md index 72058f7e9..167681b0f 100644 --- a/docs/array/asize.md +++ b/docs/array/asize.md @@ -49,4 +49,5 @@ afree(a); [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/array/astatic.md b/docs/array/astatic.md index 58e9093ba..a5056712b 100644 --- a/docs/array/astatic.md +++ b/docs/array/astatic.md @@ -46,4 +46,5 @@ dynamically grow into the heap. [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [afree](/array/afree.md) diff --git a/docs/array/dyna.md b/docs/array/dyna.md index a285f5c43..513ee1ff2 100644 --- a/docs/array/dyna.md +++ b/docs/array/dyna.md @@ -51,4 +51,5 @@ parameters or struct member definitions. It's saying "Hey, I'm a dynamic array!" [aset](/array/aset.md) [arev](/array/arev.md) [ahash](/array/ahash.md) +[adel](/array/adel.md) [astatic](/array/astatic.md) diff --git a/docs/getting_started.md b/docs/getting_started.md index 67aa50a9b..86965f66a 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -16,7 +16,7 @@ Make sure you have a compiler installed that you're familiar with beforehand. If 1. Download and install CMake (v3.14 or higher, you can just get the latest version). CMake is for easy cross-platform building. Also install [git](https://git-scm.com/downloads). If you're new to git and a Windows user it's highly recommended to use [Github Desktop](https://desktop.github.com/). 2. Copy CMakeLists.txt ([this one here](https://github.com/RandyGaul/cute_framework_project_template/blob/main/CMakeLists.txt)) into the top-level of your project directory. -3. Find + replace "my_project_name". +3. Find + replace "mygame". 4. Make a folder called `src` in the top-level of your project, and place your initial `main.cpp` there. 5. Run CMake on your project folder. If you need help with this step, try reading the [CF + CMake 101 section here](https://github.com/RandyGaul/cute_framework_project_template#cmake-101-walkthrough). diff --git a/docs/hash/hfind.md b/docs/hash/hfind.md index 56836e245..360e4f819 100644 --- a/docs/hash/hfind.md +++ b/docs/hash/hfind.md @@ -17,7 +17,7 @@ Fetches the item that `k` maps to. Parameters | Description --- | --- -h | The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. +h | The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. k | The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. ## Return Value diff --git a/docs/hash/hget.md b/docs/hash/hget.md index 4b1e3de64..0153da4a8 100644 --- a/docs/hash/hget.md +++ b/docs/hash/hget.md @@ -17,7 +17,7 @@ Fetches the item that `k` maps to. Parameters | Description --- | --- -h | The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. +h | The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. k | The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. ## Return Value diff --git a/docs/string/sfmt.md b/docs/string/sfmt.md index a09263da3..6057f0ed5 100644 --- a/docs/string/sfmt.md +++ b/docs/string/sfmt.md @@ -12,7 +12,7 @@ GitHub: [cute_string.h](https://github.com/RandyGaul/cute_framework/blob/master/ Printf's into the string using the format string `fmt`. ```cpp -#define sfmt(s, fmt, ...) cf_string_fmt(s, fmt, (__VA_ARGS__)) +#define sfmt(s, fmt, ...) cf_string_fmt(s, fmt, __VA_ARGS__) ``` Parameters | Description diff --git a/docs/string/sfmt_append.md b/docs/string/sfmt_append.md index a68945acd..7dfbaf11e 100644 --- a/docs/string/sfmt_append.md +++ b/docs/string/sfmt_append.md @@ -12,7 +12,7 @@ GitHub: [cute_string.h](https://github.com/RandyGaul/cute_framework/blob/master/ Printf's into the end of the string, using the format string `fmt`. ```cpp -#define sfmt_append(s, fmt, ...) cf_string_fmt_append(s, fmt, (__VA_ARGS__)) +#define sfmt_append(s, fmt, ...) cf_string_fmt_append(s, fmt, __VA_ARGS__) ``` Parameters | Description diff --git a/docs/time/cf_set_fixed_timestep.md b/docs/time/cf_set_fixed_timestep.md index 091672370..31c433bea 100644 --- a/docs/time/cf_set_fixed_timestep.md +++ b/docs/time/cf_set_fixed_timestep.md @@ -17,7 +17,7 @@ void cf_set_fixed_timestep(int frames_per_second); Parameters | Description --- | --- -frames_per_second | The frequency for fixed-timestep updates to occur, e.g. 30 is a good default number. +frames_per_second | The frequency for fixed-timestep updates to occur, e.g. 30 is a good default number. Set to 0 to disable fixed timestep. ## Remarks diff --git a/docs/topics/virtual_file_system.md b/docs/topics/virtual_file_system.md index a9aaf9f65..5462b1d00 100644 --- a/docs/topics/virtual_file_system.md +++ b/docs/topics/virtual_file_system.md @@ -85,21 +85,21 @@ If you have all your loadable files in a folder called `content` right beside a ```cpp void mount_content_folder() { - char* path = spnorm(fs_get_base_dir()); + char* path = spnorm(cf_fs_get_base_directory()); int n = 1; - char* dir = spdir_of(path); - if (siequ(dir, "Debug") || siequ(dir, "Release")) { + if (ssuffix(path, "Debug") || ssuffix(path, "Release")) { // MSVC/XCode places the .exe into one-level deeper /Debug or /Release folders. n = 2; } - sfree(dir); path = sppopn(path, n); scat(path, "/content"); - fs_mount(path, "/"); + cf_fs_mount(path, "/", true); sfree(path); } ``` +Of course, when you release your game for people to install and play on their own machines you will need to modify this function. In all likelihood your content folder would sit next to your executable, instead of a build folder, so the call to `sppopn` wouldn't be necessary. + ## The Write Directory Your application gets a single write directory. You set it with [`cf_fs_set_write_directory`](https://randygaul.github.io/cute_framework/#/file/cf_fs_set_write_directory). This greatly aids security and keeps writing operations locked within a single directory for simplicity. It's highly recommended to setup your write directory as the user directory from [`cf_fs_get_user_directory`](https://randygaul.github.io/cute_framework/#/file/cf_fs_get_user_directory). This directory is guaranteed to be a write-enabled and safe place to store game-specific files for your player. diff --git a/include/cute_array.h b/include/cute_array.h index 24d9521fd..4be6cdba1 100644 --- a/include/cute_array.h +++ b/include/cute_array.h @@ -31,7 +31,7 @@ * on typed pointers, there's no actual array struct type. It can get really annoying to sometimes forget if a pointer is an * array, a hashtable, or just a pointer. This macro can be used to markup the type to make it much more clear for function * parameters or struct member definitions. It's saying "Hey, I'm a dynamic array!" to mitigate this downside. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define dyna @@ -49,7 +49,7 @@ * CF_ASSERT(alen(a) == 0); * afree(a); * @remarks `a` must not by `NULL`. This function returns a proper l-value, so you can assign to it, i.e. increment/decrement can be quite useful. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define alen(a) cf_array_len(a) @@ -64,7 +64,7 @@ * CF_ASSERT(asize(a) == 1); * afree(a); * @remarks `a` can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define asize(a) cf_array_size(a) @@ -79,7 +79,7 @@ * CF_ASSERT(acount(a) == 1); * afree(a); * @remarks `a` can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define acount(a) cf_array_count(a) @@ -90,7 +90,7 @@ * @param a The array. * @remarks `a` can be `NULL`. The capacity automatically grows if the size of the array grows over the capacity. * You can use `afit` to ensure a minimum capacity as an optimization. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define acap(a) cf_array_capacity(a) @@ -104,7 +104,7 @@ * @remarks This function does not change the number of live elements, the count/size, of the array. Only the capacity. * This function is only useful as an optimization to avoid extra/unnecessary internal allocations. `a` is * automatically re-assigned to a new pointer if the array was internally regrown. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define afit(a, n) cf_array_fit(a, n) @@ -125,7 +125,7 @@ * afree(a); * @remarks `a` is automatically re-assigned to a new pointer if the array was internally regrown. If `a` is `NULL` a new * dynamic array is allocated on-the-spot for you, and assigned back to `a`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define apush(a, ...) cf_array_push(a, (__VA_ARGS__)) @@ -136,7 +136,7 @@ * @param a The array. Can not be `NULL`. * @return Returns the popped element. * @remarks The last element of the array is fetched and will be returned. The size of the array is decremented by one. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define apop(a) cf_array_pop(a) @@ -145,7 +145,7 @@ * @category array * @brief Returns a pointer one element beyond the end of the array. * @param a The array. Can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define aend(a) cf_array_end(a) @@ -154,7 +154,7 @@ * @category array * @brief Returns the last element in the array. * @param a The array. Can not be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define alast(a) cf_array_last(a) @@ -164,7 +164,7 @@ * @brief Sets the array's count to zero. Does not free any resources. * @param a The array. Can be `NULL`. * @return Returns zero. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define aclear(a) cf_array_clear(a) @@ -175,7 +175,7 @@ * @param a The array to copy into (destination). * @param b The array to copy from (source). * @return Returns a pointer to `a`. `a` will automatically be reassigned to any new pointer. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define aset(a, b) cf_array_set(a, b) @@ -184,7 +184,7 @@ * @category array * @brief Reverses the elements in an array. * @param a The array. Can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define arev(a) cf_array_reverse(a) @@ -193,10 +193,22 @@ * @category array * @brief Returns the hash of all the bytes in the array. * @param a The array. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define ahash(a) cf_array_hash(a) +/** + * @function adel + * @category array + * @brief Performs an *unordered* removal of the element at index i. + * @param a The array. + * @param i The index of the element to remove. + * @remarks The last element of the array is swapped into the index `i`. This is a constant time + * operation, but does *not preserve order* of the array. + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + */ +#define adel(a, i) cf_array_del(a) + /** * @function astatic * @category array @@ -208,7 +220,7 @@ * @remarks This macro is useful as an optimization to avoid any dynamic memory allocation in the common case for implementing * certain data structures (such as strings or stack vectors). As the array grows too large for the `buffer` it will * dynamically grow into the heap. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define astatic(a, buffer, buffer_size) cf_array_static(a, buffer, buffer_size) @@ -218,7 +230,7 @@ * @brief Frees up all resources used by the array. * @param a The array. * @remarks Sets `a` to `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash astatic afree + * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree */ #define afree(a) cf_array_free(a) @@ -242,6 +254,7 @@ #define cf_array_set(a, b) (*(void**)&(a) = cf_aset((void*)(a), (void*)(b), sizeof(*a))) #define cf_array_reverse(a) (a ? cf_arev(a, sizeof(*a)) : NULL) #define cf_array_hash(a) cf_fnv1a(a, cf_array_size(a)) +#define cf_array_del(a) (a[i] = a[alen(a)--]) #define cf_array_static(a, buffer, buffer_size) (*(void**)&(a) = cf_astatic(buffer, buffer_size, sizeof(*a))) #define cf_array_free(a) do { CF_ACANARY(a); if (a && !CF_AHDR(a)->is_static) cf_free(CF_AHDR(a)); a = NULL; } while (0) diff --git a/include/cute_hashtable.h b/include/cute_hashtable.h index 8cfe651d1..a4112e0ce 100644 --- a/include/cute_hashtable.h +++ b/include/cute_hashtable.h @@ -96,7 +96,7 @@ * @function hget * @category hash * @brief Fetches the item that `k` maps to. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. + * @param h The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. * @example > Set and get a few elements from a hashtable. * htbl int* table = NULL; @@ -117,7 +117,7 @@ * @function hfind * @category hash * @brief Fetches the item that `k` maps to. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. + * @param h The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. * @example > Set and get a few elements from a hashtable. * htbl int* table = NULL; @@ -344,7 +344,7 @@ // Longform C API. #define cf_htbl -#define cf_hashtable_set(h, k, ...) ((h) ? (h) : (*(void**)&(h) = cf_hashtable_make_impl(sizeof(uint64_t), sizeof(*(h)), 1)), CF_HCANARY(h), h[-1] = (__VA_ARGS__), *(void**)&(h) = cf_hashtable_insert_impl(CF_HHDR(h), (uint64_t)k), h + CF_HHDR(h)->return_index) +#define cf_hashtable_set(h, k, ...) ((h) ? (h) : (*(void**)&(h) = cf_hashtable_make_impl(sizeof(uint64_t), sizeof(*(h)), 1)), CF_HCANARY(h), (h)[-1] = (__VA_ARGS__), *(void**)&(h) = cf_hashtable_insert_impl(CF_HHDR(h), (uint64_t)k), (h) + CF_HHDR(h)->return_index) #define cf_hashtable_add(h, k, ...) cf_hashtable_set(h, k, (__VA_ARGS__)) #define cf_hashtable_get(h, k) ((h)[cf_hashtable_find_impl(CF_HHDR(h), (uint64_t)k)]) #define cf_hashtable_find(h, k) cf_hashtable_get(h, k) @@ -352,7 +352,7 @@ #define cf_hashtable_find_ptr(h, k) cf_hashtable_get_ptr(h, k) #define cf_hashtable_has(h, k) ((h) ? cf_hashtable_has_impl(CF_HHDR(h), (uint64_t)k) : false) #define cf_hashtable_del(h, k) ((h) ? cf_hashtable_remove_impl(CF_HHDR(h), (uint64_t)k) : (void)0) -#define cf_hashtable_clear(h) (CF_HCANARY(h), cf_hashtable_clear_impl(CF_HHDR(h))) +#define cf_hashtable_clear(h) ((h) ? CF_HCANARY(h), cf_hashtable_clear_impl(CF_HHDR(h)) : (void)0) #define cf_hashtable_keys(h) (CF_HCANARY(h), h ? (const uint64_t*)cf_hashtable_keys_impl(CF_HHDR(h)) : (const uint64_t*)NULL) #define cf_hashtable_items(h) (CF_HCANARY(h), h) #define cf_hashtable_swap(h, index_a, index_b) (CF_HCANARY(h), cf_hashtable_swap_impl(CF_HHDR(h), index_a, index_b)) diff --git a/include/cute_string.h b/include/cute_string.h index 101feb354..d4a13fd93 100644 --- a/include/cute_string.h +++ b/include/cute_string.h @@ -146,7 +146,7 @@ extern "C" { * @remarks The string will be overwritten from the beginning. Will automatically adjust capacity as needed. * @related sfmt sfmt_append svfmt svfmt_append sset */ -#define sfmt(s, fmt, ...) cf_string_fmt(s, fmt, (__VA_ARGS__)) +#define sfmt(s, fmt, ...) cf_string_fmt(s, fmt, __VA_ARGS__) /** * @function sfmt_append @@ -158,7 +158,7 @@ extern "C" { * @remarks All printed data is appended to the end of the string. Will automatically adjust it's capacity as needed. * @related sfmt sfmt_append svfmt svfmt_append sset */ -#define sfmt_append(s, fmt, ...) cf_string_fmt_append(s, fmt, (__VA_ARGS__)) +#define sfmt_append(s, fmt, ...) cf_string_fmt_append(s, fmt, __VA_ARGS__) /** * @function svfmt diff --git a/libraries/pico/pico_unit.h b/libraries/pico/pico_unit.h index 35e05e2c2..6c357e161 100644 --- a/libraries/pico/pico_unit.h +++ b/libraries/pico/pico_unit.h @@ -341,7 +341,7 @@ pu_run_test (const char* const name, pu_test_fn test_fp) printf("\n"); - pu_num_passed++; + pu_num_passed++;_CrtDumpMemoryLeaks(); if (NULL != pu_teardown_fp) { diff --git a/src/cute_alloc.cpp b/src/cute_alloc.cpp index 785ddee10..f97059a41 100644 --- a/src/cute_alloc.cpp +++ b/src/cute_alloc.cpp @@ -186,7 +186,7 @@ void cf_destroy_memory_pool(CF_MemoryPool* pool) // Attempted to destroy pool without freeing all overflow allocations. CF_ASSERT(pool->overflow_count == 0); } - CF_FREE(pool); + cf_aligned_free(pool); } void* cf_memory_pool_alloc(CF_MemoryPool* pool) diff --git a/src/cute_array.cpp b/src/cute_array.cpp index a419b2b5c..d897d9ed6 100644 --- a/src/cute_array.cpp +++ b/src/cute_array.cpp @@ -60,6 +60,10 @@ void* cf_astatic(const void* a, int buffer_size, size_t element_size) void* cf_aset(const void* a, const void* b, size_t element_size) { CF_ACANARY(a); + if (!b) { + aclear(a); + return (void*)a; + } CF_ACANARY(b); if (acap(a) < asize(b)) { int len = asize(b); diff --git a/src/cute_hashtable.cpp b/src/cute_hashtable.cpp index 544eeb102..1b941f136 100644 --- a/src/cute_hashtable.cpp +++ b/src/cute_hashtable.cpp @@ -165,7 +165,17 @@ static CF_Hhdr* s_expand_items(CF_Hhdr* table) void* cf_hashtable_insert_impl2(CF_Hhdr* table, const void* key, const void* item) { uint32_t hash = (uint32_t)fnv1a(key, table->key_size); - CF_ASSERT(s_find_slot(table, hash, key) < 0); + int slot = s_find_slot(table, hash, key); + if (slot >= 0) { + int item_index = table->slots[slot].item_index; + void* item_dst = s_get_item(table, item_index); + if (item) { + CF_MEMCPY(item_dst, item, table->item_size); + } else { + CF_MEMSET(item_dst, 0, table->item_size); + } + return s_get_item(table, 0); + } if (table->count >= (table->slot_capacity - table->slot_capacity / 3)) { s_expand_slots(table); @@ -174,7 +184,7 @@ void* cf_hashtable_insert_impl2(CF_Hhdr* table, const void* key, const void* ite uint32_t slot_capacity = (uint32_t)table->slot_capacity; int base_slot = (int)(hash % slot_capacity); int base_count = table->slots[base_slot].base_count; - int slot = base_slot; + slot = base_slot; int first_free = slot; while (base_count) { if (table->slots[slot].item_index < 0 && table->slots[first_free].item_index >= 0) first_free = slot; @@ -207,7 +217,11 @@ void* cf_hashtable_insert_impl2(CF_Hhdr* table, const void* key, const void* ite void* item_dst = s_get_item(table, table->count); void* key_dst = s_get_key(table, table->count); - if (item) CF_MEMCPY(item_dst, item, table->item_size); + if (item) { + CF_MEMCPY(item_dst, item, table->item_size); + } else { + CF_MEMSET(item_dst, 0, table->item_size); + } CF_MEMCPY(key_dst, key, table->key_size); table->items_slot_index[table->count] = slot; table->return_index = table->count++; diff --git a/test/main.cpp b/test/main.cpp index 3826f923c..f551517c9 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -39,12 +39,16 @@ TEST_SUITE(test_string); TEST_SUITE(test_json); TEST_SUITE(test_markups); +#include + int main(int argc, char* argv[]) { cf_fs_init(argv[0]); printf("Tests are running from \"%s\"\n\n", cf_fs_get_base_directory()); cf_fs_destroy(); + _CrtSetBreakAlloc(4277); + #ifdef _MSC_VER _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); @@ -54,7 +58,7 @@ int main(int argc, char* argv[]) _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); #undef RUN_TEST_SUITE - #define RUN_TEST_SUITE(suite_fp) pu_run_suite(#suite_fp, suite_fp); sinuke(); _CrtDumpMemoryLeaks(); + #define RUN_TEST_SUITE(suite_fp) pu_run_suite(#suite_fp, suite_fp); sinuke(); SDL_Quit(); _CrtDumpMemoryLeaks(); #endif pu_display_colors(true); diff --git a/test/test_audio.cpp b/test/test_audio.cpp index 002ff5c47..4a31eb792 100644 --- a/test/test_audio.cpp +++ b/test/test_audio.cpp @@ -16,6 +16,9 @@ using namespace Cute; #include "thingy.h" #include "jump.h" +#define _CRTDBG_MAP_ALLOC +#include + /* Load and free wav/ogg files synchronously. */ TEST_CASE(test_audio_load_synchronous) {