Widget parent = XtParent(w);
Display *dp = XtDisplay(parent);
Window root = XDefaultRootWindow(dp);
-
+
int x, y;
Window child;
XTranslateCoordinates(dp, XtWindow(parent), root, 0, 0, &x, &y, &child);
open_file_arg = argv[1];
}
+ // load settings
+ if(load_config()) {
+ return 1;
+ }
+
+ // try single instance open
+ if(open_file_arg) {
+ char *instance_path = InstanceFilePath(display);
+ int instance_fd = ConnectToInstance(instance_path);
+ free(instance_path);
+
+ if(instance_fd >= 0) {
+ write(instance_fd, "open ", 5);
+ write(instance_fd, open_file_arg, strlen(open_file_arg));
+ write(instance_fd, "\n", 1);
+ close(instance_fd);
+ return 0;
+ }
+ }
+
XtAppAddInput(
app,
event_pipe[0],
input_proc,
NULL);
- // load settings
- if(load_config()) {
- return 1;
- }
-
MainWindow *window = WindowCreate(display);
toplevel_window = window->window;
void PlayListInit(MainWindow *win) {
win->playlist.tracks = cxArrayListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS, 64);
+ win->playlist.tracks->simple_destructor = free;
win->playlist.current_track = -1;
}
}
}
}
+
+void PlayListClear(MainWindow *win) {
+ cxListClear(win->playlist.tracks);
+}
void PlayListPlayTrack(MainWindow *win, int i);
+void PlayListClear(MainWindow *win);
+
#ifdef __cplusplus
}
#endif
value = cx_str(v->value);
written = 0;
- written += fwrite(v->key->data.bytes, 1, v->key->len, file);
+ written += fwrite(v->key->data, 1, v->key->len, file);
written += fwrite(" = ", 1, 3, file);
written += fwrite(value.ptr, 1, value.length, file);
written += fwrite("\n", 1, 1, file);
#include <sys/stat.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+
#include "main.h"
#include "utils.h"
#include "json.h"
+#include "window.h"
+#include "playlist.h"
#include <cx/map.h>
#include <cx/hash_map.h>
//include <ucx/properties.h>
#include <cx/buffer.h>
#include <cx/utils.h>
+#include <cx/printf.h>
#define CONFIG_BASE_DIR ".config"
#define UWP_CONFIG_DIR "uwplayer"
static char *uwp_config_dir;
+static int instance_socket = -1;
+
/*
* root json config object
*/
char* SettingsGetPlayerBin(void) {
return cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN));
}
+
+
+char *InstanceFilePath(Display *dp) {
+ cxmutstr instance_file = cx_asprintf("instance%s", DisplayString(dp));
+ char *path = util_concat_path(uwp_config_dir, instance_file.ptr);
+ free(instance_file.ptr);
+ return path;
+}
+
+int ConnectToInstance(const char *path) {
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(!fd) {
+ return -1;
+ }
+
+ size_t path_len = strlen(path);
+
+ struct sockaddr_un addr;
+ if(path_len > sizeof(addr.sun_path)-1) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, path, strlen(path) + 1);
+
+ if(connect(fd, (struct sockaddr*)&addr, sizeof(addr))) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int CreateSingleInstanceSocket(Display *dp, Bool *already_running) {
+ char *instance_path = InstanceFilePath(dp);
+ size_t instance_path_len = strlen(instance_path);
+ *already_running = 0;
+
+ // check path
+ struct sockaddr_un addr;
+ if(instance_path_len > sizeof(addr.sun_path)-1) {
+ fprintf(stderr, "instance path '%s' too long for unix domain socket", instance_path);
+ free(instance_path);
+ return 1;
+ }
+
+ // check if the socket already exists and is open
+ struct stat s;
+ if(!stat(instance_path, &s)) {
+ int fd = ConnectToInstance(instance_path);
+ close(fd);
+ if(fd >= 0) {
+ *already_running = 1;
+ return 0; // instance already running
+ }
+
+ // instance not running but socket file exists
+ // remove socket before creating a new socket
+ unlink(instance_path);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, instance_path, instance_path_len+1);
+
+ free(instance_path);
+ instance_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(instance_socket < 0) {
+ fprintf(stderr, "cannot create instance socket: %s", strerror(errno));
+ return 1;
+ }
+
+ if(bind(instance_socket, (struct sockaddr*)&addr, sizeof(addr))) {
+ fprintf(stderr, "cannot bind instance socket: %s", strerror(errno));
+ return 1;
+ }
+
+ pthread_t tid;
+ if(pthread_create(&tid, NULL, instance_socket_thread, NULL)) {
+ close(instance_socket);
+ instance_socket = -1;
+ return -1;
+ }
+ pthread_detach(tid);
+
+ return 0;
+}
+
+static Boolean cmd_open(XtPointer data) {
+ MainWindow *win = GetMainWindow();
+ char *file = data;
+ printf("open %s\n", file);
+
+ PlayListClear(win);
+ PlayListAddFile(win, file);
+ PlayListPlayTrack(win, win->playlist.tracks->size-1);
+
+ free(data);
+ return 0;
+}
+
+static void process_msg(CxBuffer *msgbuf, size_t *rpos) {
+ cxstring msg = cx_strn(NULL, 0);
+ for(size_t i=*rpos;i<msgbuf->size;i++) {
+ if(msgbuf->space[i] == '\n') {
+ msg.ptr = msgbuf->space + *rpos;
+ msg.length = i - *rpos;
+ *rpos = i+1;
+ break;
+ }
+ }
+
+ if(msg.length > 0) {
+ if(cx_strprefix(msg, CX_STR("open "))) {
+ cxstring file = cx_strsubs(msg, 5);
+ cxmutstr mfile = cx_strdup(file);
+ AppExecProc(cmd_open, mfile.ptr);
+ } else {
+ fprintf(stderr, "unknown instance command: {%.*s}\n", (int)msg.length, msg.ptr);
+ }
+
+ if(*rpos < msgbuf->size) {
+ process_msg(msgbuf, rpos);
+ }
+ }
+}
+
+void* instance_socket_thread(void *data) {
+ listen(instance_socket, 8);
+
+ CxBuffer msgbuf;
+ cxBufferInit(&msgbuf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+
+ char buf[1024];
+
+ while(TRUE) {
+ printf("accept instance socket\n");
+ int fd = accept(instance_socket, NULL, 0);
+ if(fd < 0) {
+ break;
+ }
+ printf("accept instance connection\n");
+
+ msgbuf.pos = 0;
+ msgbuf.size = 0;
+ size_t rpos = 0;
+
+ ssize_t r;
+ while((r = read(fd, buf, 1024)) > 0) {
+ cxBufferWrite(buf, 1, r, &msgbuf);
+ process_msg(&msgbuf, &rpos);
+ }
+
+ printf("close instance connection\n");
+ close(fd);
+ }
+
+ cxBufferDestroy(&msgbuf);
+
+ printf("instance socket shutdown\n");
+
+ return NULL;
+}
+
+void ShutdownInstanceSocket(Display *dp) {
+ if(instance_socket < 0) {
+ return;
+ }
+
+ shutdown(instance_socket, SHUT_RDWR);
+ close(instance_socket);
+ instance_socket = -1;
+
+ char *instance_path = InstanceFilePath(dp);
+ unlink(instance_path);
+ free(instance_path);
+}
#ifndef UWP_SETTINGS_H
#define UWP_SETTINGS_H
+#include <Xm/Xm.h>
+
#ifdef __cplusplus
extern "C" {
#endif
char* SettingsGetPlayerBin(void);
+char *InstanceFilePath(Display *dp);
+
+int ConnectToInstance(const char *path);
+
+int CreateSingleInstanceSocket(Display *dp, Bool *already_running);
+
+void* instance_socket_thread(void *data);
+
+void ShutdownInstanceSocket(Display *dp);
+
#ifdef __cplusplus
}
#include "player.h"
#include "playlist.h"
#include "xdnd.h"
+#include "settings.h"
#include "Fsb.h"
#include "Sidebar.h"
static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
static void ViewSidebarCB(Widget w, void *udata, void *cdata);
static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata);
+static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata);
static void WindowRealized(MainWindow *win);
XmStringFree(s);
Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
+ s = XmStringCreateSimple("Preferences");
+ Widget prefMenuItem = XtVaCreateManagedWidget(
+ "menuitem",
+ xmCascadeButtonWidgetClass,
+ menubar,
+ XmNlabelString, s,
+ NULL);
+ XmStringFree(s);
+ Widget prefMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 3, NULL, NULL);
+
// file menu
createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
createMenuItem(fileMenu, "fileQuit", "Exit", 'E', "Ctrl<Key>Q", "Ctrl+Q", FileQuitCB, NULL);
XtVaSetValues(win->playAutoPlayButton, XmNindicatorType, XmONE_OF_MANY, NULL);
XtVaSetValues(win->playRandom, XmNindicatorType, XmONE_OF_MANY, NULL);
-
// view menu
createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
win->viewSidebarButton = createToggleMenuItem(viewMenu, "viewSidebar", "View Sidebar", 'S', False, NULL, NULL, ViewSidebarCB, win);
createMenuSeparator(viewMenu);
win->viewAdjustWindowSize = createToggleMenuItem(viewMenu, "viewAdjustWindowSize", "Adjust Window Size", 'W', TRUE, NULL, NULL, ViewAdjustWindowSizeCB, win);
+
+ // preferences menu
+ win->prefSingleInstanceButton = createToggleMenuItem(prefMenu, "prefSingleInstance", "Single Instance", 'S', FALSE, NULL, NULL, PrefSingleInstanceCB, win);
}
void go_fullscreen(Display *dsp, Window win)
win->adjustWindowSize = XmToggleButtonGadgetGetState(w);
}
+static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata) {
+ MainWindow *win = udata;
+ win->singleInstance = XmToggleButtonGadgetGetState(w);
+
+ Display *dp = XtDisplay(w);
+
+ if(!win->singleInstance) {
+ ShutdownInstanceSocket(dp);
+ return;
+ }
+
+ Bool disable_item = False;
+ if(CreateSingleInstanceSocket(dp, &disable_item)) {
+ // TODO: err
+ disable_item = True;
+ }
+ if(disable_item) {
+ win->singleInstance = 0;
+ XmToggleButtonGadgetSetState(w, False, False);
+ }
+}
+
void WindowAdjustAspectRatio(MainWindow *win) {
if(!win->player) return;
if(!win->player->isactive || win->player->width <= 0 || win->player->height <= 0) return;
Widget playRandom;
Widget viewSidebarButton;
Widget viewAdjustWindowSize;
+ Widget prefSingleInstanceButton;
PlayList playlist;
bool adjustWindowSize;
+ bool singleInstance;
Time player_event_time;
Time button_press_time;
# list of source files
SRC = allocator.c
SRC += array_list.c
-SRC += basic_mempool.c
+SRC += mempool.c
SRC += buffer.c
SRC += compare.c
SRC += hash_key.c
SRC += hash_map.c
SRC += linked_list.c
SRC += list.c
+SRC += map.c
SRC += printf.c
SRC += string.c
SRC += utils.c
};
CxAllocator *cxDefaultAllocator = &cx_default_allocator;
+
+int cx_reallocate(
+ void **mem,
+ size_t n
+) {
+ void *nmem = realloc(*mem, n);
+ if (nmem == NULL) {
+ return 1;
+ } else {
+ *mem = nmem;
+ return 0;
+ }
+}
+
// IMPLEMENTATION OF HIGH LEVEL API
void *cxMalloc(
}
#ifndef CX_ARRAY_SWAP_SBO_SIZE
-#define CX_ARRAY_SWAP_SBO_SIZE 512
+#define CX_ARRAY_SWAP_SBO_SIZE 128
#endif
void cx_array_swap(
typedef struct {
struct cx_list_s base;
void *data;
+ size_t capacity;
struct cx_array_reallocator_s reallocator;
} cx_array_list;
static void cx_arl_destructor(struct cx_list_s *list) {
cx_array_list *arl = (cx_array_list *) list;
+
+ char *ptr = arl->data;
+
+ if (list->simple_destructor) {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_invoke_simple_destructor(list, ptr);
+ ptr += list->item_size;
+ }
+ }
+ if (list->advanced_destructor) {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_invoke_advanced_destructor(list, ptr);
+ ptr += list->item_size;
+ }
+ }
+
cxFree(list->allocator, arl->data);
+ cxFree(list->allocator, list);
}
static size_t cx_arl_insert_array(
// do we need to move some elements?
if (index < list->size) {
char const *first_to_move = (char const *) arl->data;
- first_to_move += index * list->itemsize;
+ first_to_move += index * list->item_size;
size_t elems_to_move = list->size - index;
size_t start_of_moved = index + n;
if (CX_ARRAY_COPY_SUCCESS != cx_array_copy(
&arl->data,
&list->size,
- &list->capacity,
+ &arl->capacity,
start_of_moved,
first_to_move,
- list->itemsize,
+ list->item_size,
elems_to_move,
&arl->reallocator
)) {
if (CX_ARRAY_COPY_SUCCESS == cx_array_copy(
&arl->data,
&list->size,
- &list->capacity,
+ &arl->capacity,
index,
array,
- list->itemsize,
+ list->item_size,
n,
&arl->reallocator
)) {
);
if (result == 0 && prepend != 0) {
iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + list->itemsize;
+ iter->elem_handle = ((char *) iter->elem_handle) + list->item_size;
}
return result;
} else {
}
// content destruction
- if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
- char *ptr = arl->data;
- ptr += index * list->itemsize;
- cx_list_invoke_destructor(list, ptr);
- }
+ cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size);
// short-circuit removal of last element
if (index == list->size - 1) {
int result = cx_array_copy(
&arl->data,
&list->size,
- &list->capacity,
+ &arl->capacity,
index,
- ((char *) arl->data) + (index + 1) * list->itemsize,
- list->itemsize,
+ ((char *) arl->data) + (index + 1) * list->item_size,
+ list->item_size,
list->size - index - 1,
&arl->reallocator
);
cx_array_list *arl = (cx_array_list *) list;
char *ptr = arl->data;
- switch (list->content_destructor_type) {
- case CX_DESTRUCTOR_SIMPLE: {
- for (size_t i = 0; i < list->size; i++) {
- cx_list_invoke_simple_destructor(list, ptr);
- ptr += list->itemsize;
- }
- break;
+ if (list->simple_destructor) {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_invoke_simple_destructor(list, ptr);
+ ptr += list->item_size;
}
- case CX_DESTRUCTOR_ADVANCED: {
- for (size_t i = 0; i < list->size; i++) {
- cx_list_invoke_advanced_destructor(list, ptr);
- ptr += list->itemsize;
- }
- break;
+ }
+ if (list->advanced_destructor) {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_invoke_advanced_destructor(list, ptr);
+ ptr += list->item_size;
}
- case CX_DESTRUCTOR_NONE:
- break; // nothing
}
- memset(arl->data, 0, list->size * list->itemsize);
+ memset(arl->data, 0, list->size * list->item_size);
list->size = 0;
}
) {
if (i >= list->size || j >= list->size) return 1;
cx_array_list *arl = (cx_array_list *) list;
- cx_array_swap(arl->data, list->itemsize, i, j);
+ cx_array_swap(arl->data, list->item_size, i, j);
return 0;
}
if (index < list->size) {
cx_array_list const *arl = (cx_array_list const *) list;
char *space = arl->data;
- return space + index * list->itemsize;
+ return space + index * list->item_size;
} else {
return NULL;
}
}
-static size_t cx_arl_find(
+static ssize_t cx_arl_find(
struct cx_list_s const *list,
void const *elem
) {
assert(list->cmpfunc != NULL);
+ assert(list->size < SIZE_MAX / 2);
char *cur = ((cx_array_list const *) list)->data;
- for (size_t i = 0; i < list->size; i++) {
+ for (ssize_t i = 0; i < (ssize_t) list->size; i++) {
if (0 == list->cmpfunc(elem, cur)) {
return i;
}
- cur += list->itemsize;
+ cur += list->item_size;
}
- return list->size;
+ return -1;
}
static void cx_arl_sort(struct cx_list_s *list) {
assert(list->cmpfunc != NULL);
qsort(((cx_array_list *) list)->data,
list->size,
- list->itemsize,
+ list->item_size,
list->cmpfunc
);
}
if (d != 0) {
return d;
}
- left += list->itemsize;
- right += other->itemsize;
+ left += list->item_size;
+ right += other->item_size;
}
return 0;
} else {
void *data = ((cx_array_list const *) list)->data;
size_t half = list->size / 2;
for (size_t i = 0; i < half; i++) {
- cx_array_swap(data, list->itemsize, i, list->size - 1 - i);
+ cx_array_swap(data, list->item_size, i, list->size - 1 - i);
}
}
iter->index++;
iter->elem_handle =
((char *) iter->elem_handle)
- + ((struct cx_list_s const *) iter->src_handle)->itemsize;
+ + ((struct cx_list_s const *) iter->src_handle)->item_size;
}
}
iter->index--;
if (iter->index < list->base.size) {
iter->elem_handle = ((char *) list->data)
- + iter->index * list->base.itemsize;
+ + iter->index * list->base.item_size;
}
}
CxList *cxArrayListCreate(
CxAllocator const *allocator,
- CxListComparator comparator,
+ cx_compare_func comparator,
size_t item_size,
size_t initial_capacity
) {
list->base.cl = &cx_array_list_class;
list->base.allocator = allocator;
list->base.cmpfunc = comparator;
- list->base.capacity = initial_capacity;
+ list->capacity = initial_capacity;
if (item_size > 0) {
- list->base.itemsize = item_size;
+ list->base.item_size = item_size;
} else {
- item_size = sizeof(void*);
+ item_size = sizeof(void *);
cxListStorePointers((CxList *) list);
}
}
}
+CxBuffer *cxBufferCreate(
+ void *space,
+ size_t capacity,
+ CxAllocator const *allocator,
+ int flags
+) {
+ CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
+ if (buf == NULL) return NULL;
+ if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
+ return buf;
+ } else {
+ cxFree(allocator, buf);
+ return NULL;
+ }
+}
+
+void cxBufferFree(CxBuffer *buffer) {
+ if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+ cxFree(buffer->allocator, buffer->bytes);
+ }
+ cxFree(buffer->allocator, buffer);
+}
+
int cxBufferSeek(
CxBuffer *buffer,
off_t offset,
#include <math.h>
int cx_cmp_int(void const *i1, void const *i2) {
- int a = *((const int*) i1);
- int b = *((const int*) i2);
+ int a = *((const int *) i1);
+ int b = *((const int *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_longint(void const *i1, void const *i2) {
- long int a = *((const long int*) i1);
- long int b = *((const long int*) i2);
+ long int a = *((const long int *) i1);
+ long int b = *((const long int *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_longlong(void const *i1, void const *i2) {
- long long a = *((const long long*) i1);
- long long b = *((const long long*) i2);
+ long long a = *((const long long *) i1);
+ long long b = *((const long long *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_int16(void const *i1, void const *i2) {
- int16_t a = *((const int16_t*) i1);
- int16_t b = *((const int16_t*) i2);
+ int16_t a = *((const int16_t *) i1);
+ int16_t b = *((const int16_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_int32(void const *i1, void const *i2) {
- int32_t a = *((const int32_t*) i1);
- int32_t b = *((const int32_t*) i2);
+ int32_t a = *((const int32_t *) i1);
+ int32_t b = *((const int32_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_int64(void const *i1, void const *i2) {
- int64_t a = *((const int64_t*) i1);
- int64_t b = *((const int64_t*) i2);
+ int64_t a = *((const int64_t *) i1);
+ int64_t b = *((const int64_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_uint(void const *i1, void const *i2) {
- unsigned int a = *((const unsigned int*) i1);
- unsigned int b = *((const unsigned int*) i2);
+ unsigned int a = *((const unsigned int *) i1);
+ unsigned int b = *((const unsigned int *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_ulongint(void const *i1, void const *i2) {
- unsigned long int a = *((const unsigned long int*) i1);
- unsigned long int b = *((const unsigned long int*) i2);
+ unsigned long int a = *((const unsigned long int *) i1);
+ unsigned long int b = *((const unsigned long int *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_ulonglong(void const *i1, void const *i2) {
- unsigned long long a = *((const unsigned long long*) i1);
- unsigned long long b = *((const unsigned long long*) i2);
+ unsigned long long a = *((const unsigned long long *) i1);
+ unsigned long long b = *((const unsigned long long *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_uint16(void const *i1, void const *i2) {
- uint16_t a = *((const uint16_t*) i1);
- uint16_t b = *((const uint16_t*) i2);
+ uint16_t a = *((const uint16_t *) i1);
+ uint16_t b = *((const uint16_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_uint32(void const *i1, void const *i2) {
- uint32_t a = *((const uint32_t*) i1);
- uint32_t b = *((const uint32_t*) i2);
+ uint32_t a = *((const uint32_t *) i1);
+ uint32_t b = *((const uint32_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_uint64(void const *i1, void const *i2) {
- uint64_t a = *((const uint64_t*) i1);
- uint64_t b = *((const uint64_t*) i2);
+ uint64_t a = *((const uint64_t *) i1);
+ uint64_t b = *((const uint64_t *) i2);
if (a == b) {
return 0;
} else {
}
int cx_cmp_float(void const *f1, void const *f2) {
- float a = *((const float*) f1);
- float b = *((const float*) f2);
+ float a = *((const float *) f1);
+ float b = *((const float *) f2);
if (fabsf(a - b) < 1e-6f) {
return 0;
} else {
) __attribute__((__nonnull__(2)));
/**
- * Structure holding an advanced destructor function and the desired payload.
- * Invocations of func should use data as first argument.
- */
-typedef struct {
- /**
- * A pointer to the data that SHALL be used to invoke func.
- */
- void *data;
- /**
- * A pointer to the function to invoke.
- */
- cx_destructor_func2 func;
-} cx_advanced_destructor;
-
-/**
- * Specifies the type of destructor to use.
+ * Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
+ *
+ * \par Error handling
+ * \c errno will be set by realloc() on failure.
+ *
+ * @param mem pointer to the pointer to allocated block
+ * @param n the new size in bytes
+ * @return zero on success, non-zero on failure
*/
-enum cx_destructor_type {
- /**
- * Do not use a destructor function.
- */
- CX_DESTRUCTOR_NONE,
- /**
- * Use a simple destructor.
- * @see cx_destructor_func
- */
- CX_DESTRUCTOR_SIMPLE,
- /**
- * Use an advanced destructor.
- * @see cx_advanced_destructor
- */
- CX_DESTRUCTOR_ADVANCED
-};
+int cx_reallocate(
+ void **mem,
+ size_t n
+)
+__attribute__((__nonnull__));
/**
* Allocate \p n bytes of memory.
/**
* Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
* This function acts like cxRealloc() using the pointer pointed to by \p mem.
- * On success, the pointer is changed to the new location (in case the
*
* \note Re-allocating a block allocated by a different allocator is undefined.
*
*/
CxList *cxArrayListCreate(
CxAllocator const *allocator,
- CxListComparator comparator,
+ cx_compare_func comparator,
size_t item_size,
size_t initial_capacity
);
int flags
);
+/**
+ * Allocates and initializes a fresh buffer.
+ *
+ * \note You may provide \c NULL as argument for \p space.
+ * Then this function will allocate the space and enforce
+ * the #CX_BUFFER_FREE_CONTENTS flag.
+ *
+ * @param space pointer to the memory area, or \c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param allocator the allocator to use for allocating the structure and the automatic
+ * memory management within the buffer. If \c NULL, the default heap allocator will be used.
+ * @param flags buffer features (see cx_buffer_s.flags)
+ * @return a pointer to the buffer on success, \c NULL if a required allocation failed
+ */
+CxBuffer *cxBufferCreate(
+ void *space,
+ size_t capacity,
+ CxAllocator const *allocator,
+ int flags
+);
+
/**
* Destroys the buffer contents.
*
* Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled.
+ * If you want to free the memory of the entire buffer, use cxBufferFree().
*
* @param buffer the buffer which contents shall be destroyed
+ * @see cxBufferInit()
*/
__attribute__((__nonnull__))
void cxBufferDestroy(CxBuffer *buffer);
+/**
+ * Deallocates the buffer.
+ *
+ * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys
+ * the contents. If you \em only want to destroy the contents, use cxBufferDestroy().
+ *
+ * @param buffer the buffer to deallocate
+ * @see cxBufferCreate()
+ */
+__attribute__((__nonnull__))
+void cxBufferFree(CxBuffer *buffer);
+
/**
* Shifts the contents of the buffer by the given offset.
*
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file collection.h
+ * \brief Common definitions for various collection implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_COLLECTION_H
+#define UCX_COLLECTION_H
+
+#include "allocator.h"
+#include "iterator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Special constant used for creating collections that are storing pointers.
+ */
+#define CX_STORE_POINTERS 0
+
+/**
+ * A comparator function comparing two collection elements.
+ */
+typedef int(*cx_compare_func)(
+ void const *left,
+ void const *right
+);
+
+/**
+ * Use this macro to declare common members for a collection structure.
+ */
+#define CX_COLLECTION_MEMBERS \
+ /** \
+ * The allocator to use. \
+ */ \
+ CxAllocator const *allocator; \
+ /** \
+ * The comparator function for the elements. \
+ */ \
+ cx_compare_func cmpfunc; \
+ /** \
+ * The size of each element. \
+ */ \
+ size_t item_size; \
+ /** \
+ * The number of currently stored elements. \
+ */ \
+ size_t size; \
+ /** \
+ * An optional simple destructor for the collection's elements. \
+ * \
+ * @attention Read the documentation of the particular collection implementation \
+ * whether this destructor shall only destroy the contents or also free the memory. \
+ */ \
+ cx_destructor_func simple_destructor; \
+ /** \
+ * An optional advanced destructor for the collection's elements. \
+ * \
+ * @attention Read the documentation of the particular collection implementation \
+ * whether this destructor shall only destroy the contents or also free the memory. \
+ */ \
+ cx_destructor_func2 advanced_destructor; \
+ /** \
+ * The pointer to additional data that is passed to the advanced destructor. \
+ */ \
+ void *destructor_data; \
+ /** \
+ * Indicates if this instance of a collection is supposed to store pointers \
+ * instead of copies of the actual objects. \
+ */ \
+ bool store_pointer;
+
+/**
+ * Invokes the simple destructor function for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c the collection
+ * @param e the element
+ */
+#define cx_invoke_simple_destructor(c, e) \
+ (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e))
+
+/**
+ * Invokes the advanced destructor function for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c the collection
+ * @param e the element
+ */
+#define cx_invoke_advanced_destructor(c, e) \
+ (c)->advanced_destructor((c)->destructor_data, \
+ (c)->store_pointer ? (*((void **) (e))) : (e))
+
+
+/**
+ * Invokes all available destructor functions for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c the collection
+ * @param e the element
+ */
+#define cx_invoke_destructor(c, e) \
+ if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \
+ if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_COLLECTION_H
/** Version constant which ensures to increase monotonically. */
#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
+// Common Includes
+
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
+#include <sys/types.h>
/**
* Function pointer compatible with fwrite-like functions.
void *
);
-#ifdef _WIN32
-
-#ifdef __MINGW32__
-#include <sys/types.h>
-#endif // __MINGW32__
-
-#else // !_WIN32
-
-#include <sys/types.h>
-#endif // _WIN32
+// Compiler specific stuff
#ifndef __GNUC__
/**
#define __attribute__(x)
#endif
+#ifdef _MSC_VER
+
+// fix missing ssize_t definition
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+
+// fix missing _Thread_local support
+#define _Thread_local __declspec(thread)
+
+#endif
+
#endif // UCX_COMMON_H
#ifndef UCX_HASH_KEY_H
#define UCX_HASH_KEY_H
-#include "stddef.h"
+#include "common.h"
#ifdef __cplusplus
extern "C" {
/** Internal structure for a key within a hash map. */
struct cx_hash_key_s {
/** The key data. */
- union {
- unsigned char *bytes;
- unsigned char const *cbytes;
- char *str;
- char const *cstr;
- void *obj;
- void const *cobj;
- } data;
+ void const *data;
/**
* The key data length.
*/
*
* If \p buckets is zero, an implementation defined default will be used.
*
- * If \p itemsize is CX_STORE_POINTERS, the created map will be created as if
+ * If \p item_size is CX_STORE_POINTERS, the created map will be created as if
* cxMapStorePointers() was called immediately after creation.
*
* @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is the incremented when the iterator advanced without removal.
+ * The index value of an iterator is incremented when the iterator advanced without removal.
* In other words, when the iterator is finished, \c index==size .
*
* @param allocator the allocator to use
*/
__attribute__((__nonnull__, __warn_unused_result__))
CxMap *cxHashMapCreate(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
size_t itemsize,
size_t buckets
);
+/**
+ * Creates a new hash map with a default number of buckets.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created map will be created as if
+ * cxMapStorePointers() was called immediately after creation.
+ *
+ * @note Iterators provided by this hash map implementation provide the remove operation.
+ * The index value of an iterator is incremented when the iterator advanced without removal.
+ * In other words, when the iterator is finished, \c index==size .
+ *
+ * @param itemsize the size of one element
+ * @return a pointer to the new hash map
+ */
+#define cxHashMapCreateSimple(itemsize) \
+ cxHashMapCreate(cxDefaultAllocator, itemsize, 0)
+
/**
* Increases the number of buckets, if necessary.
*
/**
* Returns a pointer to the current element.
+ *
+ * When valid returns false, the behavior of this function is undefined.
*/
__attribute__ ((__nonnull__))
void *(*current)(void const *);
/**
* Advances the iterator.
+ *
+ * When valid returns false, the behavior of this function is undefined.
*/
__attribute__ ((__nonnull__))
void (*next)(void *);
/**
* Flag current element for removal, if possible.
+ *
+ * When valid returns false, the behavior of this function is undefined.
*/
__attribute__ ((__nonnull__))
bool (*flag_removal)(void *);
/**
- * Indicates whether this iterator is muting.
+ * Indicates whether this iterator may remove elements.
*/
bool mutating;
/**
* Iterator value type.
- * An iterator points to a certain element in an (possibly unbounded) chain of elements.
+ * An iterator points to a certain element in a (possibly unbounded) chain of elements.
* Iterators that are based on collections (which have a defined "first" element), are supposed
* to be "position-aware", which means that they keep track of the current index within the collection.
*
* @note Objects that are pointed to by an iterator are always mutable through that iterator. However,
* this iterator cannot mutate the collection itself (add or remove elements) and any mutation of the
- * collection by other means make this iterator invalid (regardless of what cxIteratorValid() returns).
+ * collection by other means makes this iterator invalid (regardless of what cxIteratorValid() returns).
*
* @see CxMutIterator
*/
*/
CxList *cxLinkedListCreate(
CxAllocator const *allocator,
- CxListComparator comparator,
+ cx_compare_func comparator,
size_t item_size
);
* @param loc_data the location of the \c data pointer within your node struct
* @param cmp_func a compare function to compare \p elem against the node data
* @param elem a pointer to the element to find
- * @return the index of the element or a past-one index if the element could not be found
+ * @return the index of the element or a negative value if it could not be found
*/
-size_t cx_linked_list_find(
+ssize_t cx_linked_list_find(
void const *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- CxListComparator cmp_func,
+ cx_compare_func cmp_func,
void const *elem
) __attribute__((__nonnull__));
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
- CxListComparator cmp_func
+ cx_compare_func cmp_func
) __attribute__((__nonnull__(1, 6)));
void const *begin_right,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- CxListComparator cmp_func
+ cx_compare_func cmp_func
) __attribute__((__nonnull__(5)));
/**
#define UCX_LIST_H
#include "common.h"
-#include "allocator.h"
-#include "iterator.h"
+#include "collection.h"
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef CX_STORE_POINTERS
-/**
- * Special constant used for creating collections that are storing pointers.
- */
-#define CX_STORE_POINTERS 0
-#endif
-
-/**
- * A comparator function comparing two list elements.
- */
-typedef int(*CxListComparator)(
- void const *left,
- void const *right
-);
-
/**
* List class type.
*/
* Structure for holding the base data of a list.
*/
struct cx_list_s {
+ CX_COLLECTION_MEMBERS
/**
* The list class definition.
*/
* The actual implementation in case the list class is delegating.
*/
cx_list_class const *climpl;
- /**
- * The allocator to use.
- */
- CxAllocator const *allocator;
- /**
- * The comparator function for the elements.
- */
- CxListComparator cmpfunc;
- /**
- * The size of each element (payload only).
- */
- size_t itemsize;
- /**
- * The size of the list (number of currently stored elements).
- */
- size_t size;
- /**
- * The capacity of the list (maximum number of elements).
- */
- size_t capacity;
- union {
- /**
- * An optional simple destructor for the list contents that admits the free() interface.
- *
- * @remark Set content_destructor_type to #CX_DESTRUCTOR_SIMPLE.
- *
- * @attention Read the documentation of the particular list implementation
- * whether this destructor shall only destroy the contents or also free the memory.
- */
- cx_destructor_func simple_destructor;
- /**
- * An optional advanced destructor for the list contents providing additional data.
- *
- * @remark Set content_destructor_type to #CX_DESTRUCTOR_ADVANCED.
- *
- * @attention Read the documentation of the particular list implementation
- * whether this destructor shall only destroy the contents or also free the memory.
- */
- cx_advanced_destructor advanced_destructor;
- };
- /**
- * The type of destructor to use.
- */
- enum cx_destructor_type content_destructor_type;
};
/**
struct cx_list_class_s {
/**
* Destructor function.
+ *
+ * Implementations SHALL invoke the content destructor functions if provided
+ * and SHALL deallocate the list memory, if an allocator is provided.
*/
void (*destructor)(struct cx_list_s *list);
/**
- * Member function for inserting a single elements.
+ * Member function for inserting a single element.
* Implementors SHOULD see to performant implementations for corner cases.
*/
int (*insert_element)(
/**
* Member function for finding an element.
*/
- size_t (*find)(
+ ssize_t (*find)(
struct cx_list_s const *list,
void const *elem
);
/**
- * Member function for sorting the list in place.
+ * Member function for sorting the list in-place.
*/
void (*sort)(struct cx_list_s *list);
*/
typedef struct cx_list_s CxList;
-/**
- * Invokes the configured destructor function for a specific element.
- *
- * Usually only used by list implementations. There should be no need
- * to invoke this function manually.
- *
- * @param list the list
- * @param elem the element
- */
-__attribute__((__nonnull__))
-void cx_list_invoke_destructor(
- struct cx_list_s const *list,
- void *elem
-);
-
-/**
- * Invokes the simple destructor function for a specific element.
- *
- * Usually only used by list implementations. There should be no need
- * to invoke this function manually.
- *
- * @param list the list
- * @param elem the element
- */
-__attribute__((__nonnull__))
-void cx_list_invoke_simple_destructor(
- struct cx_list_s const *list,
- void *elem
-);
-
-/**
- * Invokes the advanced destructor function for a specific element.
- *
- * Usually only used by list implementations. There should be no need
- * to invoke this function manually.
- *
- * @param list the list
- * @param elem the element
- */
-__attribute__((__nonnull__))
-void cx_list_invoke_advanced_destructor(
- struct cx_list_s const *list,
- void *elem
-);
-
/**
* Advises the list to store copies of the objects (default mode of operation).
*
* Returns true, if this list is storing pointers instead of the actual data.
*
* @param list
- * @return
+ * @return true, if this list is storing pointers
* @see cxListStorePointers()
*/
__attribute__((__nonnull__))
-bool cxListIsStoringPointers(CxList const *list);
+static inline bool cxListIsStoringPointers(CxList const *list) {
+ return list->store_pointer;
+}
+
+/**
+ * Returns the number of elements currently stored in the list.
+ *
+ * @param list the list
+ * @return the number of currently stored elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListSize(CxList const *list) {
+ return list->size;
+}
/**
* Adds an item to the end of the list.
*
* @param list the list
* @param elem the element to find
- * @return the index of the element or \c (size+1) if the element is not found
+ * @return the index of the element or a negative
+ * value when the element is not found
*/
__attribute__((__nonnull__))
-static inline size_t cxListFind(
+static inline ssize_t cxListFind(
CxList const *list,
void const *elem
) {
}
/**
- * Sorts the list in place.
+ * Sorts the list in-place.
*
* \remark The underlying sort algorithm is implementation defined.
*
__attribute__((__nonnull__))
void cxListDestroy(CxList *list);
+/**
+ * A shared instance of an empty list.
+ *
+ * Writing to that list is undefined.
+ */
+extern CxList * const cxEmptyList;
+
+
#ifdef __cplusplus
} // extern "C"
#endif
#define UCX_MAP_H
#include "common.h"
-#include "allocator.h"
-#include "iterator.h"
+#include "collection.h"
+#include "string.h"
#include "hash_key.h"
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef CX_STORE_POINTERS
-/**
- * Special constant used for creating collections that are storing pointers.
- */
-#define CX_STORE_POINTERS 0
-#endif
-
/** Type for the UCX map. */
typedef struct cx_map_s CxMap;
/** Structure for the UCX map. */
struct cx_map_s {
+ CX_COLLECTION_MEMBERS
/** The map class definition. */
cx_map_class *cl;
- /** An allocator that is used for the map elements. */
- CxAllocator *allocator;
- /** The number of elements currently stored. */
- size_t size;
+};
+
+/**
+ * The type of iterator for a map.
+ */
+enum cx_map_iterator_type {
/**
- * The size of an element.
+ * Iterates over key/value pairs.
*/
- size_t itemsize;
+ CX_MAP_ITERATOR_PAIRS,
/**
- * True, if this map shall store pointers instead
- * of copies of objects.
+ * Iterates over keys only.
*/
- bool store_pointers;
+ CX_MAP_ITERATOR_KEYS,
+ /**
+ * Iterates over values only.
+ */
+ CX_MAP_ITERATOR_VALUES
};
/**
__attribute__((__nonnull__))
void *(*remove)(
CxMap *map,
- CxHashKey key
+ CxHashKey key,
+ bool destroy
);
/**
- * Iterator over the key/value pairs.
- */
- __attribute__((__nonnull__, __warn_unused_result__))
- CxIterator (*iterator)(CxMap const *map);
-
- /**
- * Iterator over the keys.
- */
- __attribute__((__nonnull__, __warn_unused_result__))
- CxIterator (*iterator_keys)(CxMap const *map);
-
- /**
- * Iterator over the values.
- */
- __attribute__((__nonnull__, __warn_unused_result__))
- CxIterator (*iterator_values)(CxMap const *map);
-
- /**
- * Mutating iterator over the key/value pairs.
- */
- __attribute__((__nonnull__, __warn_unused_result__))
- CxMutIterator (*mut_iterator)(CxMap *map);
-
- /**
- * Mutating iterator over the keys.
- */
- __attribute__((__nonnull__, __warn_unused_result__))
- CxMutIterator (*mut_iterator_keys)(CxMap *map);
-
- /**
- * Mutating iterator over the values.
+ * Creates an iterator for this map.
*/
__attribute__((__nonnull__, __warn_unused_result__))
- CxMutIterator (*mut_iterator_values)(CxMap *map);
+ CxIterator (*iterator)(CxMap const *map, enum cx_map_iterator_type type);
};
/**
void *value;
};
+/**
+ * A shared instance of an empty map.
+ *
+ * Writing to that map is undefined.
+ */
+extern CxMap *const cxEmptyMap;
+
/**
* Advises the map to store copies of the objects (default mode of operation).
*
*/
__attribute__((__nonnull__))
static inline void cxMapStoreObjects(CxMap *map) {
- map->store_pointers = false;
+ map->store_pointer = false;
}
/**
*/
__attribute__((__nonnull__))
static inline void cxMapStorePointers(CxMap *map) {
- map->store_pointers = true;
- map->itemsize = sizeof(void *);
+ map->store_pointer = true;
+ map->item_size = sizeof(void *);
}
*/
__attribute__((__nonnull__))
static inline void cxMapDestroy(CxMap *map) {
- // TODO: likely to add auto-free feature for contents in the future
map->cl->destructor(map);
}
map->cl->clear(map);
}
-/**
- * Puts a key/value-pair into the map.
- *
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- */
-__attribute__((__nonnull__))
-static inline int cxMapPut(
- CxMap *map,
- CxHashKey key,
- void *value
-) {
- return map->cl->put(map, key, value);
-}
-
-/**
- * Retrieves a value by using a key.
- *
- * @param map the map
- * @param key the key
- * @return the value
- */
-__attribute__((__nonnull__, __warn_unused_result__))
-static inline void *cxMapGet(
- CxMap const *map,
- CxHashKey key
-) {
- return map->cl->get(map, key);
-}
-
-/**
- * Removes a key/value-pair from the map by using the key.
- *
- * If this map is storing pointers, you should make sure that the map
- * is not the last location where this pointer is stored.
- * Otherwise, use cxMapRemoveAndGet() to retrieve the pointer while
- * removing it from the map.
- *
- * @param map the map
- * @param key the key
- * @see cxMapRemoveAndGet()
- */
-__attribute__((__nonnull__))
-static inline void cxMapRemove(
- CxMap *map,
- CxHashKey key
-) {
- (void) map->cl->remove(map, key);
-}
-
-/**
- * Removes a key/value-pair from the map by using the key.
- *
- * This function should only be used when the map is storing pointers,
- * in order to retrieve the pointer you are about to remove.
- * In any other case, cxMapRemove() is sufficient.
- *
- * @param map the map
- * @param key the key
- * @return the stored pointer or \c NULL if either the key is not present
- * in the map or the map is not storing pointers
- * @see cxMapStorePointers()
- */
-__attribute__((__nonnull__, __warn_unused_result__))
-static inline void *cxMapRemoveAndGet(
- CxMap *map,
- CxHashKey key
-) {
- return map->cl->remove(map, key);
-}
// TODO: set-like map operations (union, intersect, difference)
* @return an iterator for the currently stored values
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIteratorValues(CxMap *map) {
- return map->cl->iterator_values(map);
+static inline CxIterator cxMapIteratorValues(CxMap const *map) {
+ return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
}
/**
* @return an iterator for the currently stored keys
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIteratorKeys(CxMap *map) {
- return map->cl->iterator_keys(map);
+static inline CxIterator cxMapIteratorKeys(CxMap const *map) {
+ return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
}
/**
* @see cxMapIteratorValues()
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIterator(CxMap *map) {
- return map->cl->iterator(map);
+static inline CxIterator cxMapIterator(CxMap const *map) {
+ return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
}
* @return an iterator for the currently stored values
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIteratorValues(CxMap *map) {
- return map->cl->mut_iterator_values(map);
-}
+CxMutIterator cxMapMutIteratorValues(CxMap *map);
/**
* Creates a mutating iterator over the keys of a map.
* @return an iterator for the currently stored keys
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIteratorKeys(CxMap *map) {
- return map->cl->mut_iterator_keys(map);
-}
+CxMutIterator cxMapMutIteratorKeys(CxMap *map);
/**
* Creates a mutating iterator for a map.
* @see cxMapMutIteratorValues()
*/
__attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIterator(CxMap *map) {
- return map->cl->mut_iterator(map);
+CxMutIterator cxMapMutIterator(CxMap *map);
+
+#ifdef __cplusplus
+} // end the extern "C" block here, because we want to start overloading
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+ CxMap *map,
+ CxHashKey const &key,
+ void *value
+) {
+ return map->cl->put(map, key, value);
}
-#ifdef __cplusplus
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+ CxMap *map,
+ cxstring const &key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), value);
}
-#endif
-#endif // UCX_MAP_H
\ No newline at end of file
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+ CxMap *map,
+ cxmutstr const &key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+ CxMap *map,
+ char const *key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_str(key), value);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+ CxMap const *map,
+ CxHashKey const &key
+) {
+ return map->cl->get(map, key);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+ CxMap const *map,
+ cxstring const &key
+) {
+ return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+ CxMap const *map,
+ cxmutstr const &key
+) {
+ return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+ CxMap const *map,
+ char const *key
+) {
+ return map->cl->get(map, cx_hash_key_str(key));
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+ CxMap *map,
+ CxHashKey const &key
+) {
+ (void) map->cl->remove(map, key, true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+ CxMap *map,
+ cxstring const &key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+ CxMap *map,
+ cxmutstr const &key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+ CxMap *map,
+ char const *key
+) {
+ (void) map->cl->remove(map, cx_hash_key_str(key), true);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+ CxMap *map,
+ CxHashKey const &key
+) {
+ (void) map->cl->remove(map, key, false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+ CxMap *map,
+ cxstring const &key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+ CxMap *map,
+ cxmutstr const &key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+ CxMap *map,
+ char const *key
+) {
+ (void) map->cl->remove(map, cx_hash_key_str(key), false);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+ CxMap *map,
+ CxHashKey key
+) {
+ return map->cl->remove(map, key, !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+ CxMap *map,
+ cxstring key
+) {
+ return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+ CxMap *map,
+ cxmutstr key
+) {
+ return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+ CxMap *map,
+ char const *key
+) {
+ return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer);
+}
+
+#else // __cplusplus
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put(
+ CxMap *map,
+ CxHashKey key,
+ void *value
+) {
+ return map->cl->put(map, key, value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_cxstr(
+ CxMap *map,
+ cxstring key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_mustr(
+ CxMap *map,
+ cxmutstr key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_str(
+ CxMap *map,
+ char const *key,
+ void *value
+) {
+ return map->cl->put(map, cx_hash_key_str(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+#define cxMapPut(map, key, value) _Generic((key), \
+ CxHashKey: cx_map_put, \
+ cxstring: cx_map_put_cxstr, \
+ cxmutstr: cx_map_put_mustr, \
+ char*: cx_map_put_str, \
+ char const*: cx_map_put_str) \
+ (map, key, value)
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get(
+ CxMap const *map,
+ CxHashKey key
+) {
+ return map->cl->get(map, key);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_cxstr(
+ CxMap const *map,
+ cxstring key
+) {
+ return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_mustr(
+ CxMap const *map,
+ cxmutstr key
+) {
+ return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_str(
+ CxMap const *map,
+ char const *key
+) {
+ return map->cl->get(map, cx_hash_key_str(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+#define cxMapGet(map, key) _Generic((key), \
+ CxHashKey: cx_map_get, \
+ cxstring: cx_map_get_cxstr, \
+ cxmutstr: cx_map_get_mustr, \
+ char*: cx_map_get_str, \
+ char const*: cx_map_get_str) \
+ (map, key)
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove(
+ CxMap *map,
+ CxHashKey key
+) {
+ (void) map->cl->remove(map, key, true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_cxstr(
+ CxMap *map,
+ cxstring key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_mustr(
+ CxMap *map,
+ cxmutstr key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_str(
+ CxMap *map,
+ char const *key
+) {
+ (void) map->cl->remove(map, cx_hash_key_str(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+#define cxMapRemove(map, key) _Generic((key), \
+ CxHashKey: cx_map_remove, \
+ cxstring: cx_map_remove_cxstr, \
+ cxmutstr: cx_map_remove_mustr, \
+ char*: cx_map_remove_str, \
+ char const*: cx_map_remove_str) \
+ (map, key)
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach(
+ CxMap *map,
+ CxHashKey key
+) {
+ (void) map->cl->remove(map, key, false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_cxstr(
+ CxMap *map,
+ cxstring key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_mustr(
+ CxMap *map,
+ cxmutstr key
+) {
+ (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_str(
+ CxMap *map,
+ char const *key
+) {
+ (void) map->cl->remove(map, cx_hash_key_str(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+#define cxMapDetach(map, key) _Generic((key), \
+ CxHashKey: cx_map_detach, \
+ cxstring: cx_map_detach_cxstr, \
+ cxmutstr: cx_map_detach_mustr, \
+ char*: cx_map_detach_str, \
+ char const*: cx_map_detach_str) \
+ (map, key)
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get(
+ CxMap *map,
+ CxHashKey key
+) {
+ return map->cl->remove(map, key, !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_cxstr(
+ CxMap *map,
+ cxstring key
+) {
+ return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_mustr(
+ CxMap *map,
+ cxmutstr key
+) {
+ return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_str(
+ CxMap *map,
+ char const *key
+) {
+ return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+#define cxMapRemoveAndGet(map, key) _Generic((key), \
+ CxHashKey: cx_map_remove_and_get, \
+ cxstring: cx_map_remove_and_get_cxstr, \
+ cxmutstr: cx_map_remove_and_get_mustr, \
+ char*: cx_map_remove_and_get_str, \
+ char const*: cx_map_remove_and_get_str) \
+ (map, key)
+
+#endif // __cplusplus
+
+#endif // UCX_MAP_H
#ifndef UCX_MEMPOOL_H
#define UCX_MEMPOOL_H
+#include "common.h"
#include "allocator.h"
#ifdef __cplusplus
extern "C" {
#endif
-/**
- * Memory pool class type.
- */
-typedef struct cx_mempool_class_s cx_mempool_class;
+/** Internal structure for pooled memory. */
+struct cx_mempool_memory_s;
/**
* The basic structure of a memory pool.
* Should be the first member of an actual memory pool implementation.
*/
struct cx_mempool_s {
+ /** The provided allocator. */
+ CxAllocator const *allocator;
+
/**
- * The pool class definition.
- */
- cx_mempool_class *cl;
- /**
- * The provided allocator.
+ * A destructor that shall be automatically registered for newly allocated memory.
+ * This destructor MUST NOT free the memory.
*/
- CxAllocator const *allocator;
+ cx_destructor_func auto_destr;
+
+ /** Array of pooled memory. */
+ struct cx_mempool_memory_s **data;
+
+ /** Number of pooled memory items. */
+ size_t size;
+
+ /** Memory pool capacity. */
+ size_t capacity;
};
/**
typedef struct cx_mempool_s CxMempool;
/**
- * The class definition for a memory pool.
+ * Creates an array-based memory pool with a shared destructor function.
+ *
+ * This destructor MUST NOT free the memory.
+ *
+ * @param capacity the initial capacity of the pool
+ * @param destr the destructor function to use for allocated memory
+ * @return the created memory pool or \c NULL if allocation failed
*/
-struct cx_mempool_class_s {
- /** Member function for destroying the pool. */
- __attribute__((__nonnull__))
- void (*destroy)(CxMempool *pool);
-
- /** Member function for setting a destructor. */
- __attribute__((__nonnull__))
- void (*set_destructor)(
- CxMempool *pool,
- void *memory,
- cx_destructor_func fnc
- );
-};
+__attribute__((__warn_unused_result__))
+CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * @param capacity the initial capacity of the pool
+ * @return the created memory pool or \c NULL if allocation failed
+ */
+__attribute__((__warn_unused_result__))
+static inline CxMempool *cxBasicMempoolCreate(size_t capacity) {
+ return cxMempoolCreate(capacity, NULL);
+}
/**
- * Destroys a memory pool including their contents.
+ * Destroys a memory pool and frees the managed memory.
*
* @param pool the memory pool to destroy
*/
__attribute__((__nonnull__))
-static inline void cxMempoolDestroy(CxMempool *pool) {
- pool->cl->destroy(pool);
-}
+void cxMempoolDestroy(CxMempool *pool);
/**
- * Sets a destructor function for an allocated memory object.
+ * Sets the destructor function for a specific allocated memory object.
*
- * If the memory is not managed by the pool, the behavior is undefined.
+ * If the memory is not managed by a UCX memory pool, the behavior is undefined.
+ * The destructor MUST NOT free the memory.
*
- * @param pool the pool
- * @param memory the objected allocated in the pool
+ * @param memory the object allocated in the pool
* @param fnc the destructor function
*/
__attribute__((__nonnull__))
-static inline void cxMempoolSetDestructor(
- CxMempool *pool,
+void cxMempoolSetDestructor(
void *memory,
cx_destructor_func fnc
-) {
- pool->cl->set_destructor(pool, memory, fnc);
-}
+);
+
+/**
+ * Registers foreign memory with this pool.
+ *
+ * The destructor, in contrast to memory allocated by the pool, MUST free the memory.
+ *
+ * A small portion of memory will be allocated to register the information in the pool.
+ * If that allocation fails, this function will return non-zero.
+ *
+ * @param pool the pool
+ * @param memory the object allocated in the pool
+ * @param destr the destructor function
+ * @return zero on success, non-zero on failure
+ */
+__attribute__((__nonnull__))
+int cxMempoolRegister(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func destr
+);
#ifdef __cplusplus
} // extern "C"
*/
__attribute__((__nonnull__(1, 2), __format__(printf, 2, 3)))
cxmutstr cx_asprintf_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
char const *fmt,
...
);
*/
__attribute__((__nonnull__))
cxmutstr cx_vasprintf_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
char const *fmt,
va_list ap
);
*/
typedef struct cx_strtok_ctx_s CxStrtokCtx;
+#ifdef __cplusplus
+extern "C" {
+
+/**
+ * A literal initializer for an UCX string structure.
+ *
+ * @param literal the string literal
+ */
+#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1}
+
+#else // __cplusplus
+
/**
* A literal initializer for an UCX string structure.
*
*
* @param literal the string literal
*/
-#define CX_STR(literal) {literal, sizeof(literal) - 1}
+#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1}
-#ifdef __cplusplus
-extern "C" {
#endif
*/
__attribute__((__nonnull__))
void cx_strfree_a(
- CxAllocator *alloc,
+ CxAllocator const *alloc,
cxmutstr *str
);
);
/**
- * Concatenates two or more strings.
+ * Concatenates strings.
*
* The resulting string will be allocated by the specified allocator.
- * So developers \em must pass the return value to cx_strfree() eventually.
- *
- * \note It is guaranteed that there is only one allocation.
- * It is also guaranteed that the returned string is zero-terminated.
+ * So developers \em must pass the return value to cx_strfree_a() eventually.
+ *
+ * If \p str already contains a string, the memory will be reallocated and
+ * the other strings are appended. Otherwise, new memory is allocated.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
*
* @param alloc the allocator to use
- * @param count the total number of strings to concatenate
- * @param ... all strings
+ * @param str the string the other strings shall be concatenated to
+ * @param count the number of the other following strings to concatenate
+ * @param ... all other strings
* @return the concatenated string
*/
__attribute__((__warn_unused_result__, __nonnull__))
-cxmutstr cx_strcat_a(
- CxAllocator *alloc,
+cxmutstr cx_strcat_ma(
+ CxAllocator const *alloc,
+ cxmutstr str,
size_t count,
...
);
/**
- * Concatenates two or more strings.
+ * Concatenates strings and returns a new string.
+ *
+ * The resulting string will be allocated by the specified allocator.
+ * So developers \em must pass the return value to cx_strfree_a() eventually.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param alloc the allocator to use
+ * @param count the number of the other following strings to concatenate
+ * @param ... all other strings
+ * @return the concatenated string
+ */
+#define cx_strcat_a(alloc, count, ...) \
+cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
+
+/**
+ * Concatenates strings and returns a new string.
*
* The resulting string will be allocated by standard \c malloc().
* So developers \em must pass the return value to cx_strfree() eventually.
* \note It is guaranteed that there is only one allocation.
* It is also guaranteed that the returned string is zero-terminated.
*
- * @param count the total number of strings to concatenate
- * @param ... all strings
+ * @param count the number of the other following strings to concatenate
+ * @param ... all other strings
* @return the concatenated string
*/
#define cx_strcat(count, ...) \
-cx_strcat_a(cxDefaultAllocator, count, __VA_ARGS__)
+cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
+
+/**
+ * Concatenates strings.
+ *
+ * The resulting string will be allocated by standard \c malloc().
+ * So developers \em must pass the return value to cx_strfree() eventually.
+ *
+ * If \p str already contains a string, the memory will be reallocated and
+ * the other strings are appended. Otherwise, new memory is allocated.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param str the string the other strings shall be concatenated to
+ * @param count the number of the other following strings to concatenate
+ * @param ... all other strings
+ * @return the concatenated string
+ */
+#define cx_strcat_m(str, count, ...) \
+cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__)
/**
* Returns a substring starting at the specified location.
*/
__attribute__((__warn_unused_result__, __nonnull__))
size_t cx_strsplit_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring string,
cxstring delim,
size_t limit,
*/
__attribute__((__warn_unused_result__, __nonnull__))
size_t cx_strsplit_ma(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxmutstr string,
cxstring delim,
size_t limit,
/**
* Compares two strings.
*
- * This function has a compatible signature for the use as a CxListComparator.
+ * This function has a compatible signature for the use as a cx_compare_func.
*
* @param s1 the first string
* @param s2 the second string
/**
* Compares two strings ignoring case.
*
- * This function has a compatible signature for the use as a CxListComparator.
+ * This function has a compatible signature for the use as a cx_compare_func.
*
* @param s1 the first string
* @param s2 the second string
*/
__attribute__((__warn_unused_result__, __nonnull__))
cxmutstr cx_strdup_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring string
);
*/
#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by \p allocator.
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the allocator to use
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup_m()
+ */
+#define cx_strdup_ma(allocator, string) cx_strdup_a(allocator, cx_strcast(string))
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by standard
+ * \c malloc(). So developers \em must pass the return value to cx_strfree().
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup_ma()
+ */
+#define cx_strdup_m(string) cx_strdup_a(cxDefaultAllocator, cx_strcast(string))
+
/**
* Omits leading and trailing spaces.
*
*/
__attribute__((__warn_unused_result__, __nonnull__))
cxmutstr cx_strreplacen_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring str,
cxstring pattern,
cxstring replacement,
* @param dest the destination stream
* @param rfnc the read function
* @param wfnc the write function
- * @param n the maximum number of bytes that shall be copied.
* @return total number of bytes copied
*/
#define cx_stream_copy(src, dest, rfnc, wfnc) \
#include <string.h>
void cx_hash_murmur(CxHashKey *key) {
- unsigned char const *data = key->data.cbytes;
+ unsigned char const *data = key->data;
if (data == NULL) {
// extension: special value for NULL
key->hash = 1574210520u;
h *= m;
__attribute__((__fallthrough__));
default: // do nothing
- ;
+ ;
}
h ^= h >> 13;
CxHashKey cx_hash_key_str(char const *str) {
CxHashKey key;
- key.data.cstr = str;
+ key.data = str;
key.len = str == NULL ? 0 : strlen(str);
cx_hash_murmur(&key);
return key;
size_t len
) {
CxHashKey key;
- key.data.cbytes = bytes;
+ key.data = bytes;
key.len = len;
cx_hash_murmur(&key);
return key;
size_t len
) {
CxHashKey key;
- key.data.cobj = obj;
+ key.data = obj;
key.len = len;
cx_hash_murmur(&key);
return key;
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <string.h>
#include "cx/hash_map.h"
#include "cx/utils.h"
+#include <string.h>
+#include <assert.h>
+
struct cx_hash_map_element_s {
/** A pointer to the next element in the current bucket. */
struct cx_hash_map_element_s *next;
if (elem != NULL) {
do {
struct cx_hash_map_element_s *next = elem->next;
+ // invoke the destructor
+ cx_invoke_destructor(map, elem->data);
// free the key data
- cxFree(map->allocator, elem->key.data.obj);
+ cxFree(map->allocator, (void *) elem->key.data);
// free the node
cxFree(map->allocator, elem);
// proceed
void *value
) {
struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
- CxAllocator *allocator = map->allocator;
+ CxAllocator const *allocator = map->allocator;
unsigned hash = key.hash;
if (hash == 0) {
}
if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
- memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
+ memcmp(elm->key.data, key.data, key.len) == 0) {
// overwrite existing element
- if (map->store_pointers) {
+ if (map->store_pointer) {
memcpy(elm->data, &value, sizeof(void *));
} else {
- memcpy(elm->data, value, map->itemsize);
+ memcpy(elm->data, value, map->item_size);
}
} else {
// allocate new element
struct cx_hash_map_element_s *e = cxMalloc(
allocator,
- sizeof(struct cx_hash_map_element_s) + map->itemsize
+ sizeof(struct cx_hash_map_element_s) + map->item_size
);
if (e == NULL) {
return -1;
}
// write the value
- if (map->store_pointers) {
+ if (map->store_pointer) {
memcpy(e->data, &value, sizeof(void *));
} else {
- memcpy(e->data, value, map->itemsize);
+ memcpy(e->data, value, map->item_size);
}
// copy the key
if (kd == NULL) {
return -1;
}
- memcpy(kd, key.data.obj, key.len);
- e->key.data.obj = kd;
+ memcpy(kd, key.data, key.len);
+ e->key.data = kd;
e->key.len = key.len;
e->key.hash = hash;
prev->next = elm->next;
}
// free element
- cxFree(hash_map->base.allocator, elm->key.data.obj);
+ cxFree(hash_map->base.allocator, (void *) elm->key.data);
cxFree(hash_map->base.allocator, elm);
// decrease size
hash_map->base.size--;
* @param map the map
* @param key the key to look up
* @param remove flag indicating whether the looked up entry shall be removed
+ * @param destroy flag indicating whether the destructor shall be invoked
* @return a pointer to the value corresponding to the key or \c NULL
*/
static void *cx_hash_map_get_remove(
CxMap *map,
CxHashKey key,
- bool remove
+ bool remove,
+ bool destroy
) {
struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
struct cx_hash_map_element_s *prev = NULL;
while (elm && elm->key.hash <= hash) {
if (elm->key.hash == hash && elm->key.len == key.len) {
- if (memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
+ if (memcmp(elm->key.data, key.data, key.len) == 0) {
void *data = NULL;
- if (map->store_pointers) {
- data = *(void **) elm->data;
- } else if (!remove) {
- data = elm->data;
+ if (destroy) {
+ cx_invoke_destructor(map, elm->data);
+ } else {
+ if (map->store_pointer) {
+ data = *(void **) elm->data;
+ } else {
+ data = elm->data;
+ }
}
if (remove) {
cx_hash_map_unlink(hash_map, slot, prev, elm);
CxMap const *map,
CxHashKey key
) {
- // we can safely cast, because we know when remove=false, the map stays untouched
- return cx_hash_map_get_remove((CxMap *) map, key, false);
+ // we can safely cast, because we know the map stays untouched
+ return cx_hash_map_get_remove((CxMap *) map, key, false, false);
}
static void *cx_hash_map_remove(
CxMap *map,
- CxHashKey key
+ CxHashKey key,
+ bool destroy
) {
- return cx_hash_map_get_remove(map, key, true);
+ return cx_hash_map_get_remove(map, key, true, destroy);
}
static void *cx_hash_map_iter_current_entry(void const *it) {
struct cx_iterator_s const *iter = it;
struct cx_hash_map_s const *map = iter->src_handle;
struct cx_hash_map_element_s *elm = iter->elem_handle;
- if (map->base.store_pointers) {
+ if (map->base.store_pointer) {
return *(void **) elm->data;
} else {
return elm->data;
}
}
+ // destroy
+ cx_invoke_destructor((struct cx_map_s *) map, elm->data);
+
// unlink
cx_hash_map_unlink(map, iter->slot, prev, elm);
iter->kv_data.value = NULL;
} else {
iter->kv_data.key = &elm->key;
- if (map->base.store_pointers) {
+ if (map->base.store_pointer) {
iter->kv_data.value = *(void **) elm->data;
} else {
iter->kv_data.value = elm->data;
}
}
-static CxIterator cx_hash_map_iterator(CxMap const *map) {
+static CxIterator cx_hash_map_iterator(
+ CxMap const *map,
+ enum cx_map_iterator_type type
+) {
CxIterator iter;
iter.src_handle = map;
iter.base.valid = cx_hash_map_iter_valid;
iter.base.next = cx_hash_map_iter_next;
- iter.base.current = cx_hash_map_iter_current_entry;
+
+ switch (type) {
+ case CX_MAP_ITERATOR_PAIRS:
+ iter.base.current = cx_hash_map_iter_current_entry;
+ break;
+ case CX_MAP_ITERATOR_KEYS:
+ iter.base.current = cx_hash_map_iter_current_key;
+ break;
+ case CX_MAP_ITERATOR_VALUES:
+ iter.base.current = cx_hash_map_iter_current_value;
+ break;
+ default:
+ assert(false);
+ }
+
iter.base.flag_removal = cx_hash_map_iter_flag_rm;
iter.base.remove = false;
iter.base.mutating = false;
}
iter.elem_handle = elm;
iter.kv_data.key = &elm->key;
- if (map->store_pointers) {
+ if (map->store_pointer) {
iter.kv_data.value = *(void **) elm->data;
} else {
iter.kv_data.value = elm->data;
return iter;
}
-static CxIterator cx_hash_map_iterator_keys(CxMap const *map) {
- CxIterator iter = cx_hash_map_iterator(map);
- iter.base.current = cx_hash_map_iter_current_key;
- return iter;
-}
-
-static CxIterator cx_hash_map_iterator_values(CxMap const *map) {
- CxIterator iter = cx_hash_map_iterator(map);
- iter.base.current = cx_hash_map_iter_current_value;
- return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator(CxMap *map) {
- CxIterator it = cx_hash_map_iterator(map);
- it.base.mutating = true;
-
- // we know the iterators share the same memory layout
- CxMutIterator iter;
- memcpy(&iter, &it, sizeof(CxMutIterator));
- return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator_keys(CxMap *map) {
- CxMutIterator iter = cx_hash_map_mut_iterator(map);
- iter.base.current = cx_hash_map_iter_current_key;
- return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator_values(CxMap *map) {
- CxMutIterator iter = cx_hash_map_mut_iterator(map);
- iter.base.current = cx_hash_map_iter_current_value;
- return iter;
-}
-
static cx_map_class cx_hash_map_class = {
cx_hash_map_destructor,
cx_hash_map_clear,
cx_hash_map_get,
cx_hash_map_remove,
cx_hash_map_iterator,
- cx_hash_map_iterator_keys,
- cx_hash_map_iterator_values,
- cx_hash_map_mut_iterator,
- cx_hash_map_mut_iterator_keys,
- cx_hash_map_mut_iterator_values,
};
CxMap *cxHashMapCreate(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
size_t itemsize,
size_t buckets
) {
buckets = 16;
}
- struct cx_hash_map_s *map = cxMalloc(allocator, sizeof(struct cx_hash_map_s));
+ struct cx_hash_map_s *map = cxCalloc(allocator, 1,
+ sizeof(struct cx_hash_map_s));
if (map == NULL) return NULL;
// initialize hash map members
map->bucket_count = buckets;
- map->buckets = cxCalloc(allocator, buckets, sizeof(struct cx_hash_map_element_s *));
+ map->buckets = cxCalloc(allocator, buckets,
+ sizeof(struct cx_hash_map_element_s *));
if (map->buckets == NULL) {
cxFree(allocator, map);
return NULL;
// initialize base members
map->base.cl = &cx_hash_map_class;
map->base.allocator = allocator;
- map->base.size = 0;
if (itemsize > 0) {
- map->base.store_pointers = false;
- map->base.itemsize = itemsize;
+ map->base.store_pointer = false;
+ map->base.item_size = itemsize;
} else {
- map->base.store_pointers = true;
- map->base.itemsize = sizeof(void *);
+ map->base.store_pointer = true;
+ map->base.item_size = sizeof(void *);
}
return (CxMap *) map;
if (map->size > ((hash_map->bucket_count * 3) >> 2)) {
size_t new_bucket_count = (map->size * 5) >> 1;
- struct cx_hash_map_element_s **new_buckets = cxCalloc(map->allocator,
- new_bucket_count, sizeof(struct cx_hash_map_element_s *));
+ struct cx_hash_map_element_s **new_buckets = cxCalloc(
+ map->allocator,
+ new_bucket_count, sizeof(struct cx_hash_map_element_s *)
+ );
if (new_buckets == NULL) {
return 1;
// find position where to insert
struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot];
struct cx_hash_map_element_s *bucket_prev = NULL;
- while (bucket_next != NULL && bucket_next->key.hash < elm->key.hash) {
+ while (bucket_next != NULL &&
+ bucket_next->key.hash < elm->key.hash) {
bucket_prev = bucket_next;
bucket_next = bucket_next->next;
}
return (void *) cur;
}
-size_t cx_linked_list_find(
+ssize_t cx_linked_list_find(
void const *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- CxListComparator cmp_func,
+ cx_compare_func cmp_func,
void const *elem
) {
assert(start != NULL);
assert(cmp_func);
void const *node = start;
- size_t index = 0;
+ ssize_t index = 0;
do {
void *current = ll_data(node);
if (cmp_func(current, elem) == 0) {
node = ll_advance(node);
index++;
} while (node != NULL);
- return index;
+ return -1;
}
void *cx_linked_list_first(
#define CX_LINKED_LIST_SORT_SBO_SIZE 1024
#endif
-static void *cx_linked_list_sort_merge(
+static void cx_linked_list_sort_merge(
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
void *ls,
void *le,
void *re,
- CxListComparator cmp_func
+ cx_compare_func cmp_func,
+ void **begin,
+ void **end
) {
void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
}
ll_next(sorted[length - 1]) = NULL;
- void *ret = sorted[0];
+ *begin = sorted[0];
+ *end = sorted[length-1];
if (sorted != sbo) {
free(sorted);
}
- return ret;
}
void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
- CxListComparator cmp_func
+ cx_compare_func cmp_func
) {
assert(begin != NULL);
assert(loc_next >= 0);
// set start node
ls = *begin;
+ // early exit when this list is empty
+ if (ls == NULL) return;
+
// check how many elements are already sorted
lc = ls;
size_t ln = 1;
re = ll_next(rc);
// {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
- void *sorted = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
- ln + rn, ls, le, re, cmp_func);
+ void *sorted_begin, *sorted_end;
+ cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ ln + rn, ls, le, re, cmp_func,
+ &sorted_begin, &sorted_end);
// Something left? Sort it!
size_t remainder_length = cx_linked_list_size(re, loc_next);
cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
// merge sorted list with (also sorted) remainder
- *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
- ln + rn + remainder_length,
- sorted, remainder, NULL, cmp_func);
- } else {
- // no remainder - we've got our sorted list
- *begin = sorted;
+ cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ ln + rn + remainder_length,
+ sorted_begin, remainder, NULL, cmp_func,
+ &sorted_begin, &sorted_end);
}
- if (end) *end = cx_linked_list_last(sorted, loc_next);
+ *begin = sorted_begin;
+ if (end) *end = sorted_end;
}
}
void const *begin_right,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- CxListComparator cmp_func
+ cx_compare_func cmp_func
) {
void const *left = begin_left, *right = begin_right;
// create the new new_node
cx_linked_list_node *new_node = cxMalloc(list->allocator,
- sizeof(cx_linked_list_node) + list->itemsize);
+ sizeof(cx_linked_list_node) + list->item_size);
// sortir if failed
if (new_node == NULL) return 1;
// initialize new new_node
new_node->prev = new_node->next = NULL;
- memcpy(new_node->payload, elem, list->itemsize);
+ memcpy(new_node->payload, elem, list->item_size);
// insert
cx_linked_list *ll = (cx_linked_list *) list;
// we can add the remaining nodes and immedately advance to the inserted node
char const *source = array;
for (size_t i = 1; i < n; i++) {
- source += list->itemsize;
+ source += list->item_size;
if (0 != cx_ll_insert_at(list, node, source)) {
return i;
}
if (node == NULL) return 1;
// element destruction
- if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
- cx_list_invoke_destructor(list, node->payload);
- }
+ cx_invoke_destructor(list, node->payload);
// remove
cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
cx_linked_list *ll = (cx_linked_list *) list;
cx_linked_list_node *node = ll->begin;
-
- // looks super redundant, but avoids repeatedly checking
- // the destructor type for each element
- switch (list->content_destructor_type) {
- case CX_DESTRUCTOR_SIMPLE: {
- while (node != NULL) {
- cx_list_invoke_simple_destructor(list, node->payload);
- cx_linked_list_node *next = node->next;
- cxFree(list->allocator, node);
- node = next;
- }
- break;
- }
- case CX_DESTRUCTOR_ADVANCED: {
- while (node != NULL) {
- cx_list_invoke_advanced_destructor(list, node->payload);
- cx_linked_list_node *next = node->next;
- cxFree(list->allocator, node);
- node = next;
- }
- break;
- }
- case CX_DESTRUCTOR_NONE: {
- while (node != NULL) {
- cx_linked_list_node *next = node->next;
- cxFree(list->allocator, node);
- node = next;
- }
- break;
- }
+ while (node != NULL) {
+ cx_invoke_destructor(list, node->payload);
+ cx_linked_list_node *next = node->next;
+ cxFree(list->allocator, node);
+ node = next;
}
-
ll->begin = ll->end = NULL;
list->size = 0;
}
#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
-#define CX_LINKED_LIST_SWAP_SBO_SIZE 16
+#define CX_LINKED_LIST_SWAP_SBO_SIZE 128
#endif
static int cx_ll_swap(
}
}
- if (list->itemsize > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
+ if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
cx_linked_list_node *prev = nleft->prev;
cx_linked_list_node *next = nright->next;
cx_linked_list_node *midstart = nleft->next;
} else {
// swap payloads to avoid relinking
char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
- memcpy(buf, nleft->payload, list->itemsize);
- memcpy(nleft->payload, nright->payload, list->itemsize);
- memcpy(nright->payload, buf, list->itemsize);
+ memcpy(buf, nleft->payload, list->item_size);
+ memcpy(nleft->payload, nright->payload, list->item_size);
+ memcpy(nright->payload, buf, list->item_size);
}
return 0;
return node == NULL ? NULL : node->payload;
}
-static size_t cx_ll_find(
+static ssize_t cx_ll_find(
struct cx_list_s const *list,
void const *elem
) {
cx_linked_list *ll = iter->src_handle;
cx_linked_list_node *node = iter->elem_handle;
iter->elem_handle = node->next;
- if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
- cx_list_invoke_destructor(list, node->payload);
- }
+ cx_invoke_destructor(list, node->payload);
cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
list->size--;
cx_linked_list_node *node = iter->elem_handle;
iter->elem_handle = node->prev;
iter->index--;
- if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
- cx_list_invoke_destructor(list, node->payload);
- }
+ cx_invoke_destructor(list, node->payload);
cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
list->size--;
cx_linked_list_node *node = ll->begin;
while (node) {
+ cx_invoke_destructor(list, node->payload);
void *next = node->next;
cxFree(list->allocator, node);
node = next;
}
- // do not free the list pointer, this is just a destructor!
+
+ cxFree(list->allocator, list);
}
static cx_list_class cx_linked_list_class = {
CxList *cxLinkedListCreate(
CxAllocator const *allocator,
- CxListComparator comparator,
+ cx_compare_func comparator,
size_t item_size
) {
if (allocator == NULL) {
list->base.cl = &cx_linked_list_class;
list->base.allocator = allocator;
list->base.cmpfunc = comparator;
- list->base.capacity = SIZE_MAX;
if (item_size > 0) {
- list->base.itemsize = item_size;
+ list->base.item_size = item_size;
} else {
cxListStorePointers((CxList *) list);
}
// <editor-fold desc="Store Pointers Functionality">
-static _Thread_local CxListComparator cx_pl_cmpfunc_impl;
+static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
static int cx_pl_cmpfunc(
void const *l,
return ptr == NULL ? NULL : *ptr;
}
-static size_t cx_pl_find(
+static ssize_t cx_pl_find(
struct cx_list_s const *list,
void const *elem
) {
cx_pl_hack_cmpfunc(list);
- size_t ret = list->climpl->find(list, &elem);
+ ssize_t ret = list->climpl->find(list, &elem);
cx_pl_unhack_cmpfunc(list);
return ret;
}
};
void cxListStoreObjects(CxList *list) {
+ list->store_pointer = false;
if (list->climpl != NULL) {
list->cl = list->climpl;
list->climpl = NULL;
}
void cxListStorePointers(CxList *list) {
- list->itemsize = sizeof(void *);
+ list->item_size = sizeof(void *);
+ list->store_pointer = true;
list->climpl = list->cl;
list->cl = &cx_pointer_list_class;
}
-bool cxListIsStoringPointers(CxList const *list) {
- return list->climpl != NULL;
+// </editor-fold>
+
+// <editor-fold desc="empty list implementation">
+
+static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) {
+ // this is a noop, but MUST be implemented
}
-// </editor-fold>
+static void *cx_emptyl_at(
+ __attribute__((__unused__)) struct cx_list_s const *list,
+ __attribute__((__unused__)) size_t index
+) {
+ return NULL;
+}
-void cx_list_invoke_destructor(
- CxList const *list,
- void *elem
+static ssize_t cx_emptyl_find(
+ __attribute__((__unused__)) struct cx_list_s const *list,
+ __attribute__((__unused__)) void const *elem
) {
- switch (list->content_destructor_type) {
- case CX_DESTRUCTOR_SIMPLE: {
- cx_list_invoke_simple_destructor(list, elem);
- break;
- }
- case CX_DESTRUCTOR_ADVANCED: {
- cx_list_invoke_advanced_destructor(list, elem);
- break;
- }
- case CX_DESTRUCTOR_NONE:
- break; // nothing
- }
+ return -1;
}
-void cx_list_invoke_simple_destructor(
- CxList const *list,
- void *elem
+static int cx_emptyl_compare(
+ __attribute__((__unused__)) struct cx_list_s const *list,
+ struct cx_list_s const *other
) {
- if (cxListIsStoringPointers(list)) {
- elem = *((void **) elem);
- }
- list->simple_destructor(elem);
+ if (other->size == 0) return 0;
+ return -1;
}
-void cx_list_invoke_advanced_destructor(
- CxList const *list,
- void *elem
+static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) {
+ return false;
+}
+
+static CxIterator cx_emptyl_iterator(
+ struct cx_list_s const *list,
+ size_t index,
+ __attribute__((__unused__)) bool backwards
) {
- if (cxListIsStoringPointers(list)) {
- elem = *((void **) elem);
- }
- list->advanced_destructor.func(list->advanced_destructor.data, elem);
+ CxIterator iter = {0};
+ iter.src_handle = list;
+ iter.index = index;
+ iter.base.valid = cx_emptyl_iter_valid;
+ return iter;
}
-void cxListDestroy(CxList *list) {
- switch (list->content_destructor_type) {
- case CX_DESTRUCTOR_SIMPLE: {
- CxIterator iter = cxListIterator(list);
- cx_foreach(void*, elem, iter) {
- // already correctly resolved pointer - immediately invoke dtor
- list->simple_destructor(elem);
- }
- break;
- }
- case CX_DESTRUCTOR_ADVANCED: {
- CxIterator iter = cxListIterator(list);
- cx_foreach(void*, elem, iter) {
- // already correctly resolved pointer - immediately invoke dtor
- list->advanced_destructor.func(list->advanced_destructor.data, elem);
- }
- break;
- }
- case CX_DESTRUCTOR_NONE:
- break; // nothing
- }
+static cx_list_class cx_empty_list_class = {
+ cx_emptyl_noop,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cx_emptyl_noop,
+ NULL,
+ cx_emptyl_at,
+ cx_emptyl_find,
+ cx_emptyl_noop,
+ cx_emptyl_compare,
+ cx_emptyl_noop,
+ cx_emptyl_iterator,
+};
+
+CxList cx_empty_list = {
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ &cx_empty_list_class,
+ NULL
+};
+
+CxList *const cxEmptyList = &cx_empty_list;
+// </editor-fold>
+
+void cxListDestroy(CxList *list) {
list->cl->destructor(list);
- cxFree(list->allocator, list);
}
int cxListCompare(
CxList const *list,
CxList const *other
) {
- if (list->cl->compare == other->cl->compare) {
- // same compare function, lists are compatible
- return list->cl->compare(list, other);
- } else {
- // different compare functions, use iterator
+ if (
+ // if one is storing pointers but the other is not
+ (list->store_pointer ^ other->store_pointer) ||
+
+ // if one class is wrapped but the other is not
+ ((list->climpl == NULL) ^ (other->climpl == NULL)) ||
+
+ // if the resolved compare functions are not the same
+ ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) !=
+ (other->climpl != NULL ? other->climpl->compare : other->cl->compare))
+ ) {
+ // lists are definitely different - cannot use internal compare function
if (list->size == other->size) {
CxIterator left = cxListIterator(list);
CxIterator right = cxListIterator(other);
} else {
return list->size < other->size ? -1 : 1;
}
+ } else {
+ // lists are compatible
+ return list->cl->compare(list, other);
}
}
--- /dev/null
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include "cx/map.h"\r
+#include <string.h>\r
+\r
+// <editor-fold desc="empty map implementation">\r
+\r
+static void cx_empty_map_noop(__attribute__((__unused__)) CxMap *map) {\r
+ // this is a noop, but MUST be implemented\r
+}\r
+\r
+static void *cx_empty_map_get(\r
+ __attribute__((__unused__)) CxMap const *map,\r
+ __attribute__((__unused__)) CxHashKey key\r
+) {\r
+ return NULL;\r
+}\r
+\r
+static bool cx_empty_map_iter_valid(__attribute__((__unused__)) void const *iter) {\r
+ return false;\r
+}\r
+\r
+static CxIterator cx_empty_map_iterator(\r
+ struct cx_map_s const *map,\r
+ __attribute__((__unused__)) enum cx_map_iterator_type type\r
+) {\r
+ CxIterator iter = {0};\r
+ iter.src_handle = map;\r
+ iter.base.valid = cx_empty_map_iter_valid;\r
+ return iter;\r
+}\r
+\r
+static struct cx_map_class_s cx_empty_map_class = {\r
+ cx_empty_map_noop,\r
+ cx_empty_map_noop,\r
+ NULL,\r
+ cx_empty_map_get,\r
+ NULL,\r
+ cx_empty_map_iterator\r
+};\r
+\r
+CxMap cx_empty_map = {\r
+ NULL,\r
+ NULL,\r
+ 0,\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ false,\r
+ &cx_empty_map_class\r
+};\r
+\r
+CxMap *const cxEmptyMap = &cx_empty_map;\r
+\r
+// </editor-fold>\r
+\r
+CxMutIterator cxMapMutIteratorValues(CxMap *map) {\r
+ CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
+ it.base.mutating = true;\r
+\r
+ // we know the iterators share the same memory layout\r
+ CxMutIterator iter;\r
+ memcpy(&iter, &it, sizeof(CxMutIterator));\r
+ return iter;\r
+}\r
+\r
+CxMutIterator cxMapMutIteratorKeys(CxMap *map) {\r
+ CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
+ it.base.mutating = true;\r
+\r
+ // we know the iterators share the same memory layout\r
+ CxMutIterator iter;\r
+ memcpy(&iter, &it, sizeof(CxMutIterator));\r
+ return iter;\r
+}\r
+\r
+CxMutIterator cxMapMutIterator(CxMap *map) {\r
+ CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
+ it.base.mutating = true;\r
+\r
+ // we know the iterators share the same memory layout\r
+ CxMutIterator iter;\r
+ memcpy(&iter, &it, sizeof(CxMutIterator));\r
+ return iter;\r
+}\r
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/mempool.h"
+#include "cx/utils.h"
+#include <string.h>
+
+struct cx_mempool_memory_s {
+ /** The destructor. */
+ cx_destructor_func destructor;
+ /** The actual memory. */
+ char c[];
+};
+
+static void *cx_mempool_malloc(
+ void *p,
+ size_t n
+) {
+ struct cx_mempool_s *pool = p;
+
+ if (pool->size >= pool->capacity) {
+ size_t newcap = pool->capacity - (pool->capacity % 16) + 16;
+ struct cx_mempool_memory_s **newdata = realloc(pool->data, newcap*sizeof(struct cx_mempool_memory_s*));
+ if (newdata == NULL) {
+ return NULL;
+ }
+ pool->data = newdata;
+ pool->capacity = newcap;
+ }
+
+ struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
+ if (mem == NULL) {
+ return NULL;
+ }
+
+ mem->destructor = pool->auto_destr;
+ pool->data[pool->size] = mem;
+ pool->size++;
+
+ return mem->c;
+}
+
+static void *cx_mempool_calloc(
+ void *p,
+ size_t nelem,
+ size_t elsize
+) {
+ size_t msz;
+ if (cx_szmul(nelem, elsize, &msz)) {
+ return NULL;
+ }
+ void *ptr = cx_mempool_malloc(p, msz);
+ if (ptr == NULL) {
+ return NULL;
+ }
+ memset(ptr, 0, nelem * elsize);
+ return ptr;
+}
+
+static void *cx_mempool_realloc(
+ void *p,
+ void *ptr,
+ size_t n
+) {
+ struct cx_mempool_s *pool = p;
+
+ struct cx_mempool_memory_s *mem, *newm;
+ mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func));
+ newm = realloc(mem, n + sizeof(cx_destructor_func));
+
+ if (newm == NULL) {
+ return NULL;
+ }
+ if (mem != newm) {
+ cx_for_n(i, pool->size) {
+ if (pool->data[i] == mem) {
+ pool->data[i] = newm;
+ return ((char*)newm) + sizeof(cx_destructor_func);
+ }
+ }
+ abort();
+ } else {
+ return ptr;
+ }
+}
+
+static void cx_mempool_free(
+ void *p,
+ void *ptr
+) {
+ struct cx_mempool_s *pool = p;
+
+ struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *)
+ ((char *) ptr - sizeof(cx_destructor_func));
+
+ cx_for_n(i, pool->size) {
+ if (mem == pool->data[i]) {
+ if (mem->destructor) {
+ mem->destructor(mem->c);
+ }
+ free(mem);
+ size_t last_index = pool->size - 1;
+ if (i != last_index) {
+ pool->data[i] = pool->data[last_index];
+ pool->data[last_index] = NULL;
+ }
+ pool->size--;
+ return;
+ }
+ }
+ abort();
+}
+
+void cxMempoolDestroy(CxMempool *pool) {
+ struct cx_mempool_memory_s *mem;
+ cx_for_n(i, pool->size) {
+ mem = pool->data[i];
+ if (mem->destructor) {
+ mem->destructor(mem->c);
+ }
+ free(mem);
+ }
+ free(pool->data);
+ free((void*) pool->allocator);
+ free(pool);
+}
+
+void cxMempoolSetDestructor(
+ void *ptr,
+ cx_destructor_func func
+) {
+ *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
+}
+
+struct cx_mempool_foreign_mem_s {
+ cx_destructor_func destr;
+ void* mem;
+};
+
+static void cx_mempool_destr_foreign_mem(void* ptr) {
+ struct cx_mempool_foreign_mem_s *fm = ptr;
+ fm->destr(fm->mem);
+}
+
+int cxMempoolRegister(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func destr
+) {
+ struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc(
+ pool,
+ sizeof(struct cx_mempool_foreign_mem_s)
+ );
+ if (fm == NULL) return 1;
+
+ fm->mem = memory;
+ fm->destr = destr;
+ *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem;
+
+ return 0;
+}
+
+static cx_allocator_class cx_mempool_allocator_class = {
+ cx_mempool_malloc,
+ cx_mempool_realloc,
+ cx_mempool_calloc,
+ cx_mempool_free
+};
+
+CxMempool *cxMempoolCreate(
+ size_t capacity,
+ cx_destructor_func destr
+) {
+ size_t poolsize;
+ if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) {
+ return NULL;
+ }
+
+ struct cx_mempool_s *pool =
+ malloc(sizeof(struct cx_mempool_s));
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
+ if (provided_allocator == NULL) {
+ free(pool);
+ return NULL;
+ }
+ provided_allocator->cl = &cx_mempool_allocator_class;
+ provided_allocator->data = pool;
+
+ pool->allocator = provided_allocator;
+
+ pool->data = malloc(poolsize);
+ if (pool->data == NULL) {
+ free(provided_allocator);
+ free(pool);
+ return NULL;
+ }
+
+ pool->size = 0;
+ pool->capacity = capacity;
+ pool->auto_destr = destr;
+
+ return (CxMempool *) pool;
+}
}
cxmutstr cx_asprintf_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
char const *fmt,
...
) {
}
cxmutstr cx_vasprintf_a(
- CxAllocator *a,
+ CxAllocator const *a,
char const *fmt,
va_list ap
) {
}
void cx_strfree_a(
- CxAllocator *alloc,
+ CxAllocator const *alloc,
cxmutstr *str
) {
cxFree(alloc, str->ptr);
return size;
}
-cxmutstr cx_strcat_a(
- CxAllocator *alloc,
+cxmutstr cx_strcat_ma(
+ CxAllocator const *alloc,
+ cxmutstr str,
size_t count,
...
) {
+ if (count == 0) return str;
+
cxstring *strings = calloc(count, sizeof(cxstring));
if (!strings) abort();
va_start(ap, count);
// get all args and overall length
- size_t slen = 0;
+ size_t slen = str.length;
cx_for_n(i, count) {
cxstring s = va_arg (ap, cxstring);
strings[i] = s;
slen += s.length;
}
+ va_end(ap);
- // create new string
- cxmutstr result;
- result.ptr = cxMalloc(alloc, slen + 1);
- result.length = slen;
- if (result.ptr == NULL) abort();
+ // reallocate or create new string
+ if (str.ptr == NULL) {
+ str.ptr = cxMalloc(alloc, slen + 1);
+ } else {
+ str.ptr = cxRealloc(alloc, str.ptr, slen + 1);
+ }
+ if (str.ptr == NULL) abort();
// concatenate strings
- size_t pos = 0;
+ size_t pos = str.length;
+ str.length = slen;
cx_for_n(i, count) {
cxstring s = strings[i];
- memcpy(result.ptr + pos, s.ptr, s.length);
+ memcpy(str.ptr + pos, s.ptr, s.length);
pos += s.length;
}
// terminate string
- result.ptr[result.length] = '\0';
+ str.ptr[str.length] = '\0';
// free temporary array
free(strings);
- return result;
+ return str;
}
cxstring cx_strsubs(
}
size_t cx_strsplit_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring string,
cxstring delim,
size_t limit,
}
size_t cx_strsplit_ma(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxmutstr string,
cxstring delim,
size_t limit,
}
cxmutstr cx_strdup_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring string
) {
cxmutstr result = {
}
cxmutstr cx_strreplacen_a(
- CxAllocator *allocator,
+ CxAllocator const *allocator,
cxstring str,
cxstring pattern,
cxstring replacement,
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "cx/tree.h"
-#include "cx/linked_list.h"
-
-#define CX_TR_PTR(cur, off) *((void**)(((char*)(cur))+(off)))
-
-void cx_tree_add_sibling(void *node, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_parent, void *new_node) {
- cx_linked_list_add(&node, NULL, loc_prev, loc_next, new_node);
-
- // optional parent link
- if (loc_parent >= 0) {
- CX_TR_PTR(new_node, loc_parent) = CX_TR_PTR(node, loc_parent);
- }
-}
-
-void cx_tree_add_child(void **children_begin, void **children_end,
- ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node,
- ptrdiff_t loc_parent, void *parent) {
- cx_linked_list_add(children_begin, children_end, loc_prev, loc_next, new_node);
-
- // optional parent link
- if (loc_parent >= 0) {
- CX_TR_PTR(new_node, loc_parent) = parent;
- }
-}
#include "cx/utils.h"
+#ifndef CX_STREAM_BCOPY_BUF_SIZE
#define CX_STREAM_BCOPY_BUF_SIZE 8192
+#endif
+
+#ifndef CX_STREAM_COPY_BUF_SIZE
#define CX_STREAM_COPY_BUF_SIZE 1024
+#endif
size_t cx_stream_bncopy(
void *src,