diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index eb8c4a569..6a92b1fd5 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -30,6 +30,7 @@ #include "http.h" #include "image.h" #include "keyhandler.h" +#include "qblist.h" #include "mem.h" #include "mutex.h" #include "qbs.h" @@ -426,174 +427,6 @@ int64 display_frame_order_next = 1; int64 last_rendered_hardware_display_frame_order = 0; int64 last_hardware_display_frame_order = 0; -// List Interface -// Purpose: Unify and optimize the way QB64 references lists of objects (such as handles) -// Notes: Does not use index 0 -struct list { - ptrszint user_structure_size; - ptrszint internal_structure_size; - uint8 *structure; // block of structures of user-specified size - ptrszint structures; - ptrszint structures_last; - ptrszint *structure_freed; // quickly re-reference available structures after they have been removed - ptrszint *structure_freed_cleanup; // the previous *structure_freed memory block - ptrszint structures_freed; - ptrszint structures_freed_last; - ptrszint structure_base[64]; // every time the 'structure' block is full a new and larger block is allocated - // because the list doubles each time, 64 entries will never be exceeded - ptrszint structure_bases; - ptrszint *index; // pointers to the structures referred to by each index value - ptrszint *index_cleanup; - ptrszint indexes; - ptrszint indexes_last; - struct libqb_mutex *lock_add; - struct libqb_mutex *lock_remove; -}; - -// fwd refs -void *list_get(list *L, ptrszint i); - -list *list_new(ptrszint structure_size) { - list *L; - L = (list *)calloc(1, sizeof(list)); - L->structure = (uint8 *)malloc(sizeof(uint8 *)); - L->structure_base[1] = (ptrszint)L->structure; - L->structure_bases = 1; - L->structure_freed = (ptrszint *)malloc(sizeof(ptrszint *)); - L->index = (ptrszint *)malloc(sizeof(ptrszint *)); - L->user_structure_size = structure_size; - L->internal_structure_size = structure_size + sizeof(ptrszint); - return L; -} - -list *list_new_threadsafe(ptrszint structure_size) { - list *L = list_new(structure_size); - L->lock_add = libqb_mutex_new(); - L->lock_remove = libqb_mutex_new(); - return L; -} - -ptrszint list_add(list *L) { - if (L->lock_add) - libqb_mutex_lock(L->lock_add); - - ptrszint i; - if (L->structures_freed) { // retrieve index from freed list if possible - if (L->lock_remove) - libqb_mutex_lock(L->lock_remove); - - i = L->structure_freed[L->structures_freed--]; - uint8 *structure; - structure = (uint8 *)L->index[i]; - memset(structure, 0, L->user_structure_size); - *(ptrszint *)(structure + L->user_structure_size) = i; - - if (L->lock_remove) - libqb_mutex_unlock(L->lock_remove); - } else { - // create new buffer? - if ((L->structures + 1) > L->structures_last) { - ptrszint new_structures_last; - new_structures_last = (L->structures_last * 2) + 1; - // note: L->structure is only modified by list_add - L->structure = (uint8 *)calloc(1, L->internal_structure_size * (new_structures_last + 1)); - if (L->structure == NULL) { - gui_alert("list_add: failed to allocate new buffer, structure size: %lld", (int64_t)L->internal_structure_size); - } - L->structures_last = new_structures_last; - L->structures = 0; - L->structure_base[++L->structure_bases] = (ptrszint)L->structure; - } - i = ++L->indexes; - *(ptrszint *)(L->structure + (L->internal_structure_size * (++L->structures)) + L->user_structure_size) = i; - // allocate new index - if (L->indexes > L->indexes_last) { - if (L->index_cleanup != NULL) - free(L->index_cleanup); - L->index_cleanup = L->index; - int32 new_indexes_last = (L->indexes_last * 2) + 1; - ptrszint *temp = (ptrszint *)malloc(sizeof(ptrszint) * (new_indexes_last + 1)); - memcpy(temp, L->index, sizeof(ptrszint) * (L->indexes_last + 1)); - L->index = temp; - L->index[i] = (ptrszint)(L->structure + (L->internal_structure_size * L->structures)); - L->indexes_last = new_indexes_last; - } else { - L->index[i] = (ptrszint)(L->structure + (L->internal_structure_size * L->structures)); - } - } - - if (L->lock_add) - libqb_mutex_unlock(L->lock_add); - - return i; -} // list_add - -ptrszint list_remove(list *L, ptrszint i) { // returns -1 on success, 0 on failure - if (L->lock_remove) - libqb_mutex_lock(L->lock_remove); - - if ((i < 1) || (i > L->indexes)) { - if (L->lock_remove) - libqb_mutex_unlock(L->lock_remove); - - return 0; - } - uint8 *structure; - structure = (uint8 *)(L->index[i]); - if (!*(ptrszint *)(structure + L->user_structure_size)) { - if (L->lock_remove) - libqb_mutex_unlock(L->lock_remove); - - return 0; - } - // expand buffer? - if ((L->structures_freed + 1) > L->structures_freed_last) { - ptrszint new_structures_freed_last; - new_structures_freed_last = (L->structures_freed_last * 2) + 1; - ptrszint *temp = (ptrszint *)malloc(sizeof(ptrszint) * (new_structures_freed_last + 1)); - memcpy(temp, L->structure_freed, sizeof(ptrszint) * (L->structures_freed + 1)); - if (L->structure_freed_cleanup != NULL) - free(L->structure_freed_cleanup); - L->structure_freed_cleanup = L->structure_freed; - L->structure_freed = temp; - L->structures_freed_last = new_structures_freed_last; - } - L->structure_freed[L->structures_freed + 1] = i; - *(ptrszint *)(structure + L->user_structure_size) = 0; - L->structures_freed++; - - if (L->lock_remove) - libqb_mutex_unlock(L->lock_remove); - - return -1; -}; - -void list_destroy(list *L) { - ptrszint i; - for (i = 1; i <= L->structure_bases; i++) { - free((void *)L->structure_base[i]); - } - free(L->structure_base); - free(L->structure_freed); - free(L); -} - -void *list_get(list *L, ptrszint i) { // Returns a pointer to an index's structure - if ((i < 1) || (i > L->indexes)) { - return NULL; - } - uint8 *structure; - structure = (uint8 *)(L->index[i]); - if (!*(ptrszint *)(structure + L->user_structure_size)) - return NULL; - return (void *)structure; -} - -ptrszint list_get_index(list *L, void *structure) { // Retrieves the index value of a structure - ptrszint i = *(ptrszint *)(((uint8 *)structure) + L->user_structure_size); - return i; -} - enum class special_handle_type { Invalid, Stream, diff --git a/internal/c/libqb/build.mk b/internal/c/libqb/build.mk index 7df6c2686..a729a6b1e 100644 --- a/internal/c/libqb/build.mk +++ b/internal/c/libqb/build.mk @@ -7,6 +7,7 @@ libqb-objs-y += $(PATH_LIBQB)/src/filesystem.o libqb-objs-y += $(PATH_LIBQB)/src/datetime.o libqb-objs-y += $(PATH_LIBQB)/src/error_handle.o libqb-objs-y += $(PATH_LIBQB)/src/gfs.o +libqb-objs-y += $(PATH_LIBQB)/src/qblist.o libqb-objs-y += $(PATH_LIBQB)/src/mem.o libqb-objs-y += $(PATH_LIBQB)/src/rounding.o libqb-objs-y += $(PATH_LIBQB)/src/qbs.o diff --git a/internal/c/libqb/include/qblist.h b/internal/c/libqb/include/qblist.h new file mode 100644 index 000000000..a4d582aa8 --- /dev/null +++ b/internal/c/libqb/include/qblist.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "mutex.h" + +// List Interface +// Purpose: Unify and optimize the way QB64 references lists of objects (such as handles) +// Notes: Does not use index 0 +struct list { + intptr_t user_structure_size; + intptr_t internal_structure_size; + uint8_t *structure; // block of structures of user-specified size + intptr_t structures; + intptr_t structures_last; + intptr_t *structure_freed; // quickly re-reference available structures after they have been removed + intptr_t *structure_freed_cleanup; // the previous *structure_freed memory block + intptr_t structures_freed; + intptr_t structures_freed_last; + intptr_t structure_base[64]; // every time the 'structure' block is full a new and larger block is allocated + // because the list doubles each time, 64 entries will never be exceeded + intptr_t structure_bases; + intptr_t *index; // pointers to the structures referred to by each index value + intptr_t *index_cleanup; + intptr_t indexes; + intptr_t indexes_last; + struct libqb_mutex *lock_add; + struct libqb_mutex *lock_remove; +}; + +list *list_new(intptr_t structure_size); +list *list_new_threadsafe(intptr_t structure_size); +void list_destroy(list *L); + +intptr_t list_add(list *L); +intptr_t list_remove(list *L, intptr_t i); // returns -1 on success, 0 on failure + // +void *list_get(list *L, intptr_t i); // Returns a pointer to an index's structure + // +intptr_t list_get_index(list *L, void *structure); // Retrieves the index value of a structure diff --git a/internal/c/libqb/src/qblist.cpp b/internal/c/libqb/src/qblist.cpp new file mode 100644 index 000000000..4d5c7e437 --- /dev/null +++ b/internal/c/libqb/src/qblist.cpp @@ -0,0 +1,149 @@ + +#include "libqb-common.h" + +#include +#include + +#include "gui.h" +#include "qblist.h" + +list *list_new(intptr_t structure_size) { + list *L; + L = (list *)calloc(1, sizeof(list)); + L->structure = (uint8_t *)malloc(sizeof(uint8_t *)); + L->structure_base[1] = (intptr_t)L->structure; + L->structure_bases = 1; + L->structure_freed = (intptr_t *)malloc(sizeof(intptr_t *)); + L->index = (intptr_t *)malloc(sizeof(intptr_t *)); + L->user_structure_size = structure_size; + L->internal_structure_size = structure_size + sizeof(intptr_t); + return L; +} + +list *list_new_threadsafe(intptr_t structure_size) { + list *L = list_new(structure_size); + L->lock_add = libqb_mutex_new(); + L->lock_remove = libqb_mutex_new(); + return L; +} + +intptr_t list_add(list *L) { + if (L->lock_add) + libqb_mutex_lock(L->lock_add); + + intptr_t i; + if (L->structures_freed) { // retrieve index from freed list if possible + if (L->lock_remove) + libqb_mutex_lock(L->lock_remove); + + i = L->structure_freed[L->structures_freed--]; + uint8_t *structure; + structure = (uint8_t *)L->index[i]; + memset(structure, 0, L->user_structure_size); + *(intptr_t *)(structure + L->user_structure_size) = i; + + if (L->lock_remove) + libqb_mutex_unlock(L->lock_remove); + } else { + // create new buffer? + if ((L->structures + 1) > L->structures_last) { + intptr_t new_structures_last; + new_structures_last = (L->structures_last * 2) + 1; + // note: L->structure is only modified by list_add + L->structure = (uint8_t *)calloc(1, L->internal_structure_size * (new_structures_last + 1)); + if (L->structure == NULL) { + gui_alert("list_add: failed to allocate new buffer, structure size: %lld", (int64_t)L->internal_structure_size); + } + L->structures_last = new_structures_last; + L->structures = 0; + L->structure_base[++L->structure_bases] = (intptr_t)L->structure; + } + i = ++L->indexes; + *(intptr_t *)(L->structure + (L->internal_structure_size * (++L->structures)) + L->user_structure_size) = i; + // allocate new index + if (L->indexes > L->indexes_last) { + if (L->index_cleanup != NULL) + free(L->index_cleanup); + L->index_cleanup = L->index; + int32_t new_indexes_last = (L->indexes_last * 2) + 1; + intptr_t *temp = (intptr_t *)malloc(sizeof(intptr_t) * (new_indexes_last + 1)); + memcpy(temp, L->index, sizeof(intptr_t) * (L->indexes_last + 1)); + L->index = temp; + L->index[i] = (intptr_t)(L->structure + (L->internal_structure_size * L->structures)); + L->indexes_last = new_indexes_last; + } else { + L->index[i] = (intptr_t)(L->structure + (L->internal_structure_size * L->structures)); + } + } + + if (L->lock_add) + libqb_mutex_unlock(L->lock_add); + + return i; +} // list_add + +intptr_t list_remove(list *L, intptr_t i) { // returns -1 on success, 0 on failure + if (L->lock_remove) + libqb_mutex_lock(L->lock_remove); + + if ((i < 1) || (i > L->indexes)) { + if (L->lock_remove) + libqb_mutex_unlock(L->lock_remove); + + return 0; + } + uint8_t *structure; + structure = (uint8_t *)(L->index[i]); + if (!*(intptr_t *)(structure + L->user_structure_size)) { + if (L->lock_remove) + libqb_mutex_unlock(L->lock_remove); + + return 0; + } + // expand buffer? + if ((L->structures_freed + 1) > L->structures_freed_last) { + intptr_t new_structures_freed_last; + new_structures_freed_last = (L->structures_freed_last * 2) + 1; + intptr_t *temp = (intptr_t *)malloc(sizeof(intptr_t) * (new_structures_freed_last + 1)); + memcpy(temp, L->structure_freed, sizeof(intptr_t) * (L->structures_freed + 1)); + if (L->structure_freed_cleanup != NULL) + free(L->structure_freed_cleanup); + L->structure_freed_cleanup = L->structure_freed; + L->structure_freed = temp; + L->structures_freed_last = new_structures_freed_last; + } + L->structure_freed[L->structures_freed + 1] = i; + *(intptr_t *)(structure + L->user_structure_size) = 0; + L->structures_freed++; + + if (L->lock_remove) + libqb_mutex_unlock(L->lock_remove); + + return -1; +}; + +void list_destroy(list *L) { + intptr_t i; + for (i = 1; i <= L->structure_bases; i++) { + free((void *)L->structure_base[i]); + } + free(L->structure_base); + free(L->structure_freed); + free(L); +} + +void *list_get(list *L, intptr_t i) { // Returns a pointer to an index's structure + if ((i < 1) || (i > L->indexes)) { + return NULL; + } + uint8_t *structure; + structure = (uint8_t *)(L->index[i]); + if (!*(intptr_t *)(structure + L->user_structure_size)) + return NULL; + return (void *)structure; +} + +intptr_t list_get_index(list *L, void *structure) { // Retrieves the index value of a structure + intptr_t i = *(intptr_t *)(((uint8_t *)structure) + L->user_structure_size); + return i; +}