├── .gitignore ├── AsyncIntro.txt ├── LICENSE ├── Makefile ├── README ├── io ├── adns.c ├── adns.h ├── afile.c ├── afile.h ├── afile_posix.c ├── afile_windows.c ├── asock.c ├── asock.h ├── asock_posix.c ├── asock_windows.c ├── clock.c ├── clock.h ├── handle.h ├── ioq.c ├── ioq.h ├── ioq_linux.c ├── ioq_linux.h ├── ioq_windows.c ├── ioq_windows.h ├── mailbox.c ├── mailbox.h ├── net.c ├── net.h ├── runq.c ├── runq.h ├── syserr.h ├── thr.c ├── thr.h ├── waitq.c ├── waitq.h └── winapi.h ├── src ├── arena.c ├── arena.h ├── bint.c ├── bint.h ├── bytes.h ├── cbuf.c ├── cbuf.h ├── containers.h ├── hash.c ├── hash.h ├── istr.c ├── istr.h ├── list.c ├── list.h ├── protothread.h ├── rbt.c ├── rbt.h ├── rbt_iter.c ├── rbt_iter.h ├── rbt_range.c ├── rbt_range.h ├── slab.c ├── slab.h ├── slist.c ├── slist.h ├── strbuf.c ├── strbuf.h ├── strlcpy.c ├── strlcpy.h ├── vector.c └── vector.h └── tests ├── prng.h ├── test_adns.c ├── test_afile.c ├── test_arena.c ├── test_asock.c ├── test_bint.c ├── test_bytes.c ├── test_cbuf.c ├── test_clock.c ├── test_containers.c ├── test_hash.c ├── test_ioq.c ├── test_ioq_linux.c ├── test_ioq_windows.c ├── test_istr.c ├── test_list.c ├── test_mailbox.c ├── test_net.c ├── test_protothread.c ├── test_rbt.c ├── test_rbt_iter.c ├── test_rbt_range.c ├── test_runq.c ├── test_slab.c ├── test_slist.c ├── test_strbuf.c ├── test_strlcpy.c ├── test_syserr.c ├── test_thr.c ├── test_vector.c └── test_waitq.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.test 3 | .*.swp 4 | *.exe 5 | *~ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | libdlb - data structures and utilities library 2 | Copyright (C) 2011-2013 Daniel Beer 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | libdlb: data structures and utilities library 2 | Copyright (C) 2013 Daniel Beer 3 | 4 | This package is a library of (primarily intrusive) data structures and 5 | asynchronous IO facilities for C programs. The subdirectories in this 6 | directory are: 7 | 8 | * src: intrusive data structures, including: 9 | - bint: arbitrary precision integers 10 | - cbuf: circular byte-buffers 11 | - containers: utility macros for intrusive data structures 12 | - hash: partially intrusive hash table 13 | - istr: memory-efficient immutable string pool 14 | - list: intrusive doubly-linked circular list 15 | - rbt: intrusive red-black tree 16 | - rbt_iter: functions for iterating over red-black trees 17 | - rbt_range: functions for efficiently computing aggregates over 18 | augmented red-black trees. 19 | - slab: pooled object allocator 20 | - slist: intrusive singly-linked list 21 | - strbuf: resizable string-buffer 22 | - strlcpy: strlcpy() and strlcat() 23 | - vector: resizable array 24 | 25 | * io: portable asynchronous IO and system utilities: 26 | - afile: asynchronous file reading/writing 27 | - clock: portable interface to a monotonic millisecond clock 28 | - handle: portable file handle abstraction 29 | - ioq: asynchronous IO queue 30 | - mailbox: asynchronous IPC primitive 31 | - runq: thread pool 32 | - syserr: portable interface to system error codes 33 | - thr: portable interface to threading primitives 34 | - waitq: asynchronous timer schedule 35 | - net: portable network initialization 36 | - adns: asynchronous DNS 37 | - asock: asynchronous TCP/IP socket 38 | 39 | * tests: automated test suite 40 | 41 | To build the tests, type "make". Nothing is required except the standard 42 | C library and GCC. Type "make test" to run each test. 43 | 44 | All the files in this package are covered by the license in the file 45 | "LICENSE". If you want to use them in your own programs, just include 46 | the files you need in your source package -- there's no need to build a 47 | shared library, and you likely don't need every file (dependencies 48 | between files exist, but are easily identified). 49 | -------------------------------------------------------------------------------- /io/adns.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_ADNS_H_ 18 | #define IO_ADNS_H_ 19 | 20 | #include "net.h" 21 | #include "list.h" 22 | #include "thr.h" 23 | #include "runq.h" 24 | 25 | #ifndef __Windows__ 26 | #include 27 | #endif 28 | 29 | /* Asynchronous DNS resolver. One resolver can be shared by multiple 30 | * threads, but each must have its own adns_request object. 31 | */ 32 | struct adns_resolver { 33 | struct runq *runq; 34 | 35 | thr_thread_t worker; 36 | thr_event_t notify; 37 | 38 | thr_mutex_t lock; 39 | int flags; 40 | struct list_node requests; 41 | }; 42 | 43 | /* Initialize/destroy an asynchronous DNS resolver. 44 | * 45 | * NOTE: destruction of the resolver may block if the resolver is busy. 46 | * This can't be avoided, so you should try to defer destruction for as 47 | * long as possible. 48 | * 49 | * If initialization fails, syserr_last() will return a valid error 50 | * code. 51 | */ 52 | int adns_resolver_init(struct adns_resolver *r, struct runq *q); 53 | void adns_resolver_destroy(struct adns_resolver *r); 54 | 55 | /* DNS error codes */ 56 | #ifdef __Windows__ 57 | typedef DWORD adns_error_t; 58 | 59 | static inline void adns_error_format(adns_error_t e, 60 | char *buf, size_t max_size) 61 | { 62 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, 0, 63 | buf, max_size, NULL); 64 | } 65 | #else 66 | typedef int adns_error_t; 67 | 68 | static inline void adns_error_format(adns_error_t e, 69 | char *buf, size_t max_size) 70 | { 71 | strncpy(buf, gai_strerror(e), max_size); 72 | buf[max_size - 1] = 0; 73 | } 74 | #endif 75 | 76 | #define ADNS_ERROR_NONE 0 77 | 78 | /* Asynchronous DNS request. This must not be modified simultaneously by 79 | * multiple threads. 80 | */ 81 | struct adns_request { 82 | /* Callback -- must be first */ 83 | struct runq_task task; 84 | struct adns_resolver *owner; 85 | 86 | /* State: protected by owner lock */ 87 | struct list_node list; 88 | int flags; 89 | 90 | /* Request */ 91 | const char *hostname; 92 | const char *service; 93 | const struct addrinfo *hints; 94 | 95 | /* Result */ 96 | struct addrinfo *result; 97 | adns_error_t error; 98 | }; 99 | 100 | /* Initialize a request object */ 101 | void adns_request_init(struct adns_request *r, struct adns_resolver *v); 102 | 103 | /* Free any non-NULL result */ 104 | void adns_request_destroy(struct adns_request *r); 105 | 106 | typedef void (*adns_request_func_t)(struct adns_request *r); 107 | 108 | /* Begin an asynchronous DNS request. Arguments are the same as for 109 | * getaddrinfo(), except a callback is given instead of a return 110 | * pointer. 111 | * 112 | * Any result left over from a previous query will be freed first. 113 | */ 114 | void adns_request_ask(struct adns_request *r, 115 | const char *hostname, const char *service, 116 | const struct addrinfo *hints, 117 | adns_request_func_t func); 118 | 119 | /* Cancel a request. A cancelled request completes with a non-error code 120 | * and a NULL result. 121 | */ 122 | void adns_request_cancel(struct adns_request *r); 123 | 124 | /* Retrieve the result. If non-NULL, you must free this with 125 | * freeaddrinfo(). 126 | */ 127 | static inline struct addrinfo *adns_get_result(const struct adns_request *r) 128 | { 129 | return r->result; 130 | } 131 | 132 | /* Retrieve the error */ 133 | static inline adns_error_t adns_get_error(const struct adns_request *r) 134 | { 135 | return r->error; 136 | } 137 | 138 | /* Clear the result, if any */ 139 | void adns_clear_result(struct adns_request *r); 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /io/afile.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __Windows__ 18 | #include "afile_windows.c" 19 | #else 20 | #include "afile_posix.c" 21 | #endif 22 | -------------------------------------------------------------------------------- /io/afile.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_AFILE_H_ 18 | #define IO_AFILE_H_ 19 | 20 | #include 21 | #include "syserr.h" 22 | #include "ioq.h" 23 | #include "thr.h" 24 | #include "handle.h" 25 | 26 | /* Asynchronous file handle manager. This allows, independently, reads 27 | * and writes to be started on a file handle. The object may be 28 | * destroyed if no operations are outstanding. 29 | */ 30 | struct afile; 31 | typedef void (*afile_func_t)(struct afile *a); 32 | 33 | #ifdef __Windows__ 34 | struct afile_op { 35 | afile_func_t func; 36 | struct ioq_ovl ovl; 37 | DWORD size; 38 | syserr_t error; 39 | }; 40 | 41 | struct afile { 42 | handle_t handle; 43 | struct afile_op read; 44 | struct afile_op write; 45 | }; 46 | #else 47 | struct afile_op { 48 | afile_func_t func; 49 | void *buffer; 50 | size_t size; 51 | syserr_t error; 52 | }; 53 | 54 | struct afile { 55 | struct ioq_fd fd; /* Must be first */ 56 | 57 | struct afile_op read; 58 | struct afile_op write; 59 | 60 | thr_mutex_t lock; 61 | int flags; 62 | }; 63 | #endif 64 | 65 | /* Initialize an asynchronous file. Ownership of the file handle is not 66 | * taken -- it must be destroyed independently. 67 | */ 68 | void afile_init(struct afile *a, struct ioq *q, handle_t h); 69 | 70 | #ifdef __Windows__ 71 | static inline void afile_destroy(struct afile *a) { } 72 | #else 73 | void afile_destroy(struct afile *a); 74 | #endif 75 | 76 | /* Obtain or change the handle associated with the asynchronous file. 77 | * The handle can be changed only if there are no wait operations in 78 | * progress. 79 | */ 80 | #ifdef __Windows__ 81 | static inline handle_t afile_get_handle(const struct afile *a) 82 | { 83 | return a->handle; 84 | } 85 | 86 | static inline void afile_set_handle(struct afile *a, handle_t h) 87 | { 88 | a->handle = h; 89 | } 90 | #else 91 | static inline handle_t afile_get_handle(const struct afile *a) 92 | { 93 | return ioq_fd_get_fd(&a->fd); 94 | } 95 | 96 | static inline void afile_set_handle(struct afile *a, handle_t h) 97 | { 98 | ioq_fd_set_fd(&a->fd, h); 99 | } 100 | #endif 101 | 102 | /* Begin an asynchronous write operation */ 103 | void afile_write(struct afile *a, const void *data, size_t len, 104 | afile_func_t func); 105 | 106 | /* Obtain the error code and result of a write operation. If the write 107 | * was successful, the error will be SYSERR_NONE. 108 | */ 109 | static inline size_t afile_write_size(const struct afile *a) 110 | { 111 | return a->write.size; 112 | } 113 | 114 | static inline syserr_t afile_write_error(const struct afile *a) 115 | { 116 | return a->write.error; 117 | } 118 | 119 | /* Begin an asynchronous read operation */ 120 | void afile_read(struct afile *a, void *data, size_t len, 121 | afile_func_t func); 122 | 123 | /* Obtain the error code and result of a read operation. If the read 124 | * was successful, the error will be SYSERR_NONE. 125 | */ 126 | static inline size_t afile_read_size(const struct afile *a) 127 | { 128 | return a->read.size; 129 | } 130 | 131 | static inline syserr_t afile_read_error(const struct afile *a) 132 | { 133 | return a->read.error; 134 | } 135 | 136 | /* Cancel all outstanding operations. The result of any IO operations 137 | * will be undefined. 138 | */ 139 | #ifdef __Windows__ 140 | static inline void afile_cancel(struct afile *a) 141 | { 142 | CancelIo(a->handle); 143 | } 144 | #else 145 | void afile_cancel(struct afile *a); 146 | #endif 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /io/afile_posix.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "afile.h" 19 | 20 | #define F_WANT_READ 0x01 21 | #define F_WANT_WRITE 0x02 22 | #define F_WANT_CANCEL 0x04 23 | 24 | static void ioq_cb(struct ioq_fd *f); 25 | 26 | static int end_wait(struct afile *a, syserr_t *err) 27 | { 28 | ioq_fd_mask_t wait_mask = 0; 29 | int perform = 0; 30 | 31 | thr_mutex_lock(&a->lock); 32 | 33 | /* Check for an error */ 34 | if (ioq_fd_error(&a->fd)) { 35 | perform = a->flags | F_WANT_CANCEL; 36 | a->flags = 0; 37 | *err = ioq_fd_error(&a->fd); 38 | goto out; 39 | } 40 | 41 | *err = 0; 42 | 43 | /* Check for cancellation request */ 44 | if (a->flags & F_WANT_CANCEL) { 45 | perform = a->flags | F_WANT_CANCEL; 46 | a->flags = 0; 47 | goto out; 48 | } 49 | 50 | /* Choose actions to perform, and new things to wait for */ 51 | if (a->flags & F_WANT_WRITE) { 52 | if (ioq_fd_ready(&a->fd) & 53 | (IOQ_EVENT_OUT | IOQ_EVENT_ERR | IOQ_EVENT_HUP)) { 54 | perform |= F_WANT_WRITE; 55 | a->flags &= ~F_WANT_WRITE; 56 | } else { 57 | wait_mask |= IOQ_EVENT_OUT; 58 | } 59 | } 60 | 61 | if (a->flags & F_WANT_READ) { 62 | if (ioq_fd_ready(&a->fd) & 63 | (IOQ_EVENT_IN | IOQ_EVENT_ERR | IOQ_EVENT_HUP)) { 64 | perform |= F_WANT_READ; 65 | a->flags &= ~F_WANT_READ; 66 | } else { 67 | wait_mask |= IOQ_EVENT_IN; 68 | } 69 | } 70 | 71 | /* Wait for more IO events */ 72 | if (wait_mask) 73 | ioq_fd_wait(&a->fd, wait_mask, ioq_cb); 74 | 75 | out: 76 | thr_mutex_unlock(&a->lock); 77 | return perform; 78 | } 79 | 80 | static void ioq_cb(struct ioq_fd *f) 81 | { 82 | struct afile *a = (struct afile *)f; 83 | syserr_t error; 84 | int perform = end_wait(a, &error); 85 | 86 | /* Perform actions */ 87 | if (perform & F_WANT_READ) { 88 | if (perform & F_WANT_CANCEL) { 89 | a->read.size = 0; 90 | a->read.error = error; 91 | } else { 92 | int r = read(a->fd.fd, a->read.buffer, a->read.size); 93 | 94 | if (r < 0) { 95 | a->read.size = 0; 96 | a->read.error = syserr_last(); 97 | } else { 98 | a->read.size = r; 99 | a->read.error = SYSERR_NONE; 100 | } 101 | } 102 | 103 | a->read.func(a); 104 | } 105 | 106 | if (perform & F_WANT_WRITE) { 107 | if (perform & F_WANT_CANCEL) { 108 | a->write.size = 0; 109 | a->write.error = error; 110 | } else { 111 | int r = write(a->fd.fd, 112 | a->write.buffer, a->write.size); 113 | 114 | if (r < 0) { 115 | a->read.size = 0; 116 | a->read.error = syserr_last(); 117 | } else { 118 | a->read.size = r; 119 | a->read.error = SYSERR_NONE; 120 | } 121 | } 122 | 123 | a->write.func(a); 124 | } 125 | } 126 | 127 | void afile_init(struct afile *a, struct ioq *q, handle_t h) 128 | { 129 | ioq_fd_init(&a->fd, q, h); 130 | a->flags = 0; 131 | memset(&a->read, 0, sizeof(a->read)); 132 | memset(&a->write, 0, sizeof(a->write)); 133 | thr_mutex_init(&a->lock); 134 | } 135 | 136 | void afile_destroy(struct afile *a) 137 | { 138 | thr_mutex_destroy(&a->lock); 139 | } 140 | 141 | void afile_write(struct afile *a, const void *data, size_t len, 142 | afile_func_t func) 143 | { 144 | a->write.buffer = (void *)data; 145 | a->write.size = len; 146 | a->write.func = func; 147 | 148 | thr_mutex_lock(&a->lock); 149 | 150 | if (a->flags) 151 | ioq_fd_rewait(&a->fd, IOQ_EVENT_IN | IOQ_EVENT_OUT); 152 | else 153 | ioq_fd_wait(&a->fd, IOQ_EVENT_OUT, ioq_cb); 154 | 155 | a->flags |= F_WANT_WRITE; 156 | thr_mutex_unlock(&a->lock); 157 | } 158 | 159 | void afile_read(struct afile *a, void *data, size_t len, 160 | afile_func_t func) 161 | { 162 | a->read.buffer = data; 163 | a->read.size = len; 164 | a->read.func = func; 165 | 166 | thr_mutex_lock(&a->lock); 167 | 168 | if (a->flags) 169 | ioq_fd_rewait(&a->fd, IOQ_EVENT_IN | IOQ_EVENT_OUT); 170 | else 171 | ioq_fd_wait(&a->fd, IOQ_EVENT_IN, ioq_cb); 172 | 173 | a->flags |= F_WANT_READ; 174 | thr_mutex_unlock(&a->lock); 175 | } 176 | 177 | void afile_cancel(struct afile *a) 178 | { 179 | thr_mutex_lock(&a->lock); 180 | ioq_fd_cancel(&a->fd); 181 | a->flags |= F_WANT_CANCEL; 182 | thr_mutex_unlock(&a->lock); 183 | } 184 | -------------------------------------------------------------------------------- /io/afile_windows.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "afile.h" 18 | #include "containers.h" 19 | 20 | void afile_init(struct afile *a, struct ioq *q, handle_t h) 21 | { 22 | a->handle = h; 23 | ioq_ovl_init(&a->read.ovl, q); 24 | ioq_ovl_init(&a->write.ovl, q); 25 | } 26 | 27 | static void write_done(struct ioq_ovl *o) 28 | { 29 | struct afile_op *op = container_of(o, struct afile_op, ovl); 30 | struct afile *a = container_of(op, struct afile, write); 31 | 32 | if (a->write.error == ERROR_IO_PENDING) { 33 | if (GetOverlappedResult(a->handle, ioq_ovl_lpo(&a->write.ovl), 34 | &a->write.size, TRUE)) 35 | a->write.error = 0; 36 | else 37 | a->write.error = GetLastError(); 38 | } 39 | 40 | if (a->write.error) 41 | a->write.size = 0; 42 | 43 | a->write.func(a); 44 | } 45 | 46 | void afile_write(struct afile *a, const void *data, size_t len, 47 | afile_func_t func) 48 | { 49 | a->write.func = func; 50 | a->write.error = ERROR_IO_PENDING; 51 | 52 | ioq_ovl_wait(&a->write.ovl, write_done); 53 | if (!WriteFile(a->handle, data, len, &a->write.size, 54 | ioq_ovl_lpo(&a->write.ovl))) { 55 | const syserr_t e = GetLastError(); 56 | 57 | if (e != ERROR_IO_PENDING) { 58 | a->write.error = e; 59 | ioq_ovl_trigger(&a->write.ovl); 60 | } 61 | } else { 62 | a->write.error = 0; 63 | ioq_ovl_trigger(&a->write.ovl); 64 | } 65 | } 66 | 67 | static void read_done(struct ioq_ovl *o) 68 | { 69 | struct afile_op *op = container_of(o, struct afile_op, ovl); 70 | struct afile *a = container_of(op, struct afile, read); 71 | 72 | if (a->read.error == ERROR_IO_PENDING) { 73 | if (GetOverlappedResult(a->handle, ioq_ovl_lpo(&a->read.ovl), 74 | &a->read.size, TRUE)) 75 | a->read.error = 0; 76 | else 77 | a->read.error = GetLastError(); 78 | } 79 | 80 | if (a->read.error) 81 | a->read.size = 0; 82 | 83 | a->read.func(a); 84 | } 85 | 86 | void afile_read(struct afile *a, void *data, size_t len, 87 | afile_func_t func) 88 | { 89 | a->read.func = func; 90 | a->read.error = ERROR_IO_PENDING; 91 | 92 | ioq_ovl_wait(&a->read.ovl, read_done); 93 | if (!ReadFile(a->handle, data, len, &a->read.size, 94 | ioq_ovl_lpo(&a->read.ovl))) { 95 | const syserr_t e = GetLastError(); 96 | 97 | if (e != ERROR_IO_PENDING) { 98 | a->read.error = e; 99 | ioq_ovl_trigger(&a->read.ovl); 100 | } 101 | } else { 102 | a->read.error = 0; 103 | ioq_ovl_trigger(&a->read.ovl); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /io/asock.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __Windows__ 18 | #include "asock_windows.c" 19 | #else 20 | #include "asock_posix.c" 21 | #endif 22 | -------------------------------------------------------------------------------- /io/clock.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "clock.h" 18 | 19 | #ifdef __Windows__ 20 | #include "winapi.h" 21 | 22 | clock_ticks_t clock_now(void) 23 | { 24 | #if _WIN32_WINNT >= 0x0600 25 | return GetTickCount64(); 26 | #else 27 | return GetTickCount(); 28 | #endif 29 | } 30 | 31 | void clock_wait(clock_ticks_t delay) 32 | { 33 | Sleep(delay); 34 | } 35 | #else 36 | #include 37 | #include 38 | 39 | clock_ticks_t clock_now(void) 40 | { 41 | struct timespec tp; 42 | 43 | clock_gettime(CLOCK_MONOTONIC, &tp); 44 | return (((clock_ticks_t)tp.tv_sec) * 1000LL) + 45 | ((clock_ticks_t)(tp.tv_nsec / 1000000LL)); 46 | } 47 | 48 | void clock_wait(clock_ticks_t delay) 49 | { 50 | if (delay >= 1000) { 51 | sleep(delay / 1000); 52 | delay %= 1000; 53 | } 54 | 55 | usleep(delay * 1000); 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /io/clock.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_CLOCK_H_ 18 | #define IO_CLOCK_H_ 19 | 20 | #include 21 | 22 | /* Current time, in milliseconds, relative to some arbitrary starting 23 | * point. This clock is monotonic, but not tied to any wall clock. 24 | */ 25 | typedef uint64_t clock_ticks_t; 26 | 27 | #define CLOCK_PRI_TICKS PRIu64 28 | 29 | /* Obtain the latest time. This function is thread-safe. */ 30 | clock_ticks_t clock_now(void); 31 | 32 | /* Wait the specified number of milliseconds. This function is 33 | * thread-safe. 34 | */ 35 | void clock_wait(clock_ticks_t delay); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /io/handle.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_HANDLE_H_ 18 | #define IO_HANDLE_H_ 19 | 20 | /* File handle abstraction. */ 21 | #ifdef __Windows__ 22 | 23 | #include "winapi.h" 24 | 25 | typedef HANDLE handle_t; 26 | 27 | #define HANDLE_NONE NULL 28 | 29 | static inline int handle_is_valid(handle_t h) 30 | { 31 | return (h != NULL) && (h != INVALID_HANDLE_VALUE); 32 | } 33 | 34 | static inline void handle_close(handle_t h) 35 | { 36 | CloseHandle(h); 37 | } 38 | 39 | #else 40 | 41 | #include 42 | 43 | typedef int handle_t; 44 | 45 | #define HANDLE_NONE ((handle_t)(-1)) 46 | 47 | static inline int handle_is_valid(handle_t h) 48 | { 49 | return h >= 0; 50 | } 51 | 52 | static inline void handle_close(handle_t h) 53 | { 54 | close(h); 55 | } 56 | 57 | #endif 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /io/ioq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __Windows__ 18 | #include "ioq_windows.c" 19 | #else 20 | #include "ioq_linux.c" 21 | #endif 22 | -------------------------------------------------------------------------------- /io/ioq.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_IOQ_H_ 18 | #define IO_IOQ_H_ 19 | 20 | #include "runq.h" 21 | #include "waitq.h" 22 | 23 | #ifdef __Windows__ 24 | #include "ioq_windows.h" 25 | #else 26 | #include "ioq_linux.h" 27 | #endif 28 | 29 | /* Initialize an IO queue with the given number of background threads 30 | * (may be 0 for a single-threaded loop). 31 | * 32 | * Returns 0 on success or -1 if an error occurs. 33 | */ 34 | int ioq_init(struct ioq *q, unsigned int bg_threads); 35 | 36 | /* Destroy an IO queue. This does not clean up pending tasks. */ 37 | void ioq_destroy(struct ioq *q); 38 | 39 | /* Perform one iteration of wait/dispatch. The main loop of your program 40 | * will likely look something like: 41 | * 42 | * ioq_init(&my_queue); 43 | * ... start asynchronous tasks 44 | * while (!quit_condition_is_true()) 45 | * ioq_iterate(&my_queue); 46 | * ioq_destroy(&my_queue); 47 | * 48 | * Returns 0 on success or -1 if some critical error occurs. 49 | */ 50 | int ioq_iterate(struct ioq *q); 51 | 52 | /* Interrupt an IO queue waiter. This causes ioq_iterate() to return 0 53 | * if it's currently being called. 54 | */ 55 | void ioq_notify(struct ioq *q); 56 | 57 | /* Obtain a pointer to the IO queue's integrated run queue */ 58 | static inline struct runq *ioq_runq(struct ioq *q) 59 | { 60 | return &q->run; 61 | } 62 | 63 | /* Obtain a pointer to the IO queue's integrated wait queue */ 64 | static inline struct waitq *ioq_waitq(struct ioq *q) 65 | { 66 | return &q->wait; 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /io/ioq_linux.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* DO NOT INCLUDE THIS FILE DIRECTLY */ 18 | #include 19 | #include "syserr.h" 20 | #include "slist.h" 21 | 22 | struct ioq { 23 | struct runq run; 24 | struct waitq wait; 25 | 26 | thr_mutex_t lock; 27 | struct slist mod_list; 28 | 29 | /* Wakeup pipe */ 30 | int intr[2]; 31 | int intr_state; 32 | 33 | /* epoll file descriptor */ 34 | int epoll_fd; 35 | }; 36 | 37 | /* This is the set of POSIX file descriptor events which can be waited 38 | * for. These are level-triggered events. 39 | */ 40 | typedef uint32_t ioq_fd_mask_t; 41 | 42 | #define IOQ_EVENT_IN EPOLLIN 43 | #define IOQ_EVENT_OUT EPOLLOUT 44 | #define IOQ_EVENT_ERR EPOLLERR 45 | #define IOQ_EVENT_HUP EPOLLHUP 46 | 47 | /* Wait object for use with an IO queue. Each one of these objects is 48 | * associated with a single file descriptor and may have one outstanding 49 | * IO operation. 50 | * 51 | * No two ioq_fd objects may refer to the same file descriptor. 52 | * 53 | * Simultaneous access to the same ioq_fd by multiple threads is not 54 | * allowed. 55 | */ 56 | #define IOQ_FLAG_MOD_LIST 0x01 57 | #define IOQ_FLAG_EPOLL 0x02 58 | #define IOQ_FLAG_WAITING 0x04 59 | 60 | struct ioq_fd { 61 | /* This must be the first element */ 62 | struct runq_task task; 63 | 64 | /* This data never changes after init */ 65 | int fd; 66 | struct ioq *owner; 67 | 68 | /* This data is set when dispatching */ 69 | ioq_fd_mask_t ready; 70 | syserr_t err; 71 | 72 | /* If any changes must be made to this fd's epoll association, 73 | * it's done by placing the ioq_fd in the mod_list, and setting 74 | * the requested field. This data is shared between the client 75 | * strand and the dispatch loop. 76 | * 77 | * The flags field tells us which data structures this ioq_fd 78 | * belongs to (mod_list and kernel's internal epoll structures). 79 | */ 80 | int flags; 81 | struct slist_node mod_list; 82 | ioq_fd_mask_t requested; 83 | }; 84 | 85 | /* Type of asynchronous callback for an IO wait operation. */ 86 | typedef void (*ioq_fd_func_t)(struct ioq_fd *f); 87 | 88 | /* Initialize an ioq_fd object, associating it with an IO queue and a 89 | * file descriptor. 90 | * 91 | * The file descriptor is never modified, and the caller remains 92 | * responsible for closing it. 93 | */ 94 | void ioq_fd_init(struct ioq_fd *f, struct ioq *q, int fd); 95 | 96 | /* Get or change the file descriptor associated with this ioq_fd 97 | * structure. The file descriptor can be changed only if there is no 98 | * wait in progress. 99 | */ 100 | static inline int ioq_fd_get_fd(const struct ioq_fd *f) 101 | { 102 | return f->fd; 103 | } 104 | 105 | static inline void ioq_fd_set_fd(struct ioq_fd *f, int fd) 106 | { 107 | f->fd = fd; 108 | } 109 | 110 | /* Obtain the set of IO events which are ready (if any) */ 111 | static inline ioq_fd_mask_t ioq_fd_ready(const struct ioq_fd *f) 112 | { 113 | return f->ready; 114 | } 115 | 116 | /* Obtain the error code from the last wait operation (will be 0 if no 117 | * error occured). Note that a successful wait may still result in an 118 | * error *event*. This is different to an error encountered during the 119 | * wait operation itself. 120 | */ 121 | static inline syserr_t ioq_fd_error(const struct ioq_fd *f) 122 | { 123 | return f->err; 124 | } 125 | 126 | /* Begin a wait operation on an ioq_fd object. The given callback will 127 | * be invoked when any of the (level-triggered) events in the set occur. 128 | * The following rules must be observed: 129 | * 130 | * (1) only one wait operation may be in progress at any time 131 | * (2) the ioq_fd may not be modified/destroyed/reused while a wait 132 | * operation is in progress (except for cancel/rewait). 133 | * (3) the wait operation is ended as soon as the callback begins 134 | * execution, at which point it is safe to reuse/modify/destroy 135 | * the ioq_fd. 136 | */ 137 | void ioq_fd_wait(struct ioq_fd *f, ioq_fd_mask_t set, ioq_fd_func_t func); 138 | 139 | /* Attempt to alter the conditions of an IO operation which is in 140 | * progress. If successful, the set of events waited for will be 141 | * altered. If the wait has already terminated (or is in the process of 142 | * terminating), this operation will have no effect. 143 | */ 144 | void ioq_fd_rewait(struct ioq_fd *f, ioq_fd_mask_t set); 145 | 146 | /* Attempt to cancel an IO wait operation. */ 147 | static inline void ioq_fd_cancel(struct ioq_fd *f) 148 | { 149 | ioq_fd_rewait(f, 0); 150 | } 151 | -------------------------------------------------------------------------------- /io/ioq_windows.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "ioq.h" 18 | #include "containers.h" 19 | 20 | static void wakeup_runq(struct runq *q) 21 | { 22 | ioq_notify(container_of(q, struct ioq, run)); 23 | } 24 | 25 | static void wakeup_waitq(struct waitq *q) 26 | { 27 | ioq_notify(container_of(q, struct ioq, wait)); 28 | } 29 | 30 | int ioq_init(struct ioq *q, unsigned int bg_threads) 31 | { 32 | if (runq_init(&q->run, bg_threads) < 0) 33 | return -1; 34 | waitq_init(&q->wait, &q->run); 35 | 36 | q->run.wakeup = wakeup_runq; 37 | q->wait.wakeup = wakeup_waitq; 38 | 39 | q->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 40 | if (!q->iocp) { 41 | syserr_t err = syserr_last(); 42 | 43 | waitq_destroy(&q->wait); 44 | runq_destroy(&q->run); 45 | syserr_set(err); 46 | return -1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | void ioq_destroy(struct ioq *q) 53 | { 54 | CloseHandle(q->iocp); 55 | waitq_destroy(&q->wait); 56 | runq_destroy(&q->run); 57 | } 58 | 59 | int ioq_iterate(struct ioq *q) 60 | { 61 | const int timeout = waitq_next_deadline(&q->wait); 62 | DWORD number_of_bytes; 63 | ULONG_PTR comp_key; 64 | LPOVERLAPPED overlapped = NULL; 65 | 66 | GetQueuedCompletionStatus(q->iocp, &number_of_bytes, &comp_key, 67 | &overlapped, (timeout < 0) ? INFINITE : timeout); 68 | 69 | if (overlapped) { 70 | struct ioq_ovl *h = container_of(overlapped, 71 | struct ioq_ovl, overlapped); 72 | 73 | if (overlapped) 74 | runq_task_exec(&h->task, h->task.func); 75 | } 76 | 77 | waitq_dispatch(&q->wait, 0); 78 | runq_dispatch(&q->run, 0); 79 | return 0; 80 | } 81 | 82 | void ioq_notify(struct ioq *q) 83 | { 84 | PostQueuedCompletionStatus(q->iocp, 0, 0, NULL); 85 | } 86 | 87 | void ioq_ovl_wait(struct ioq_ovl *h, ioq_ovl_func_t f) 88 | { 89 | memset(&h->overlapped, 0, sizeof(h->overlapped)); 90 | h->task.func = (runq_task_func_t)f; 91 | } 92 | -------------------------------------------------------------------------------- /io/ioq_windows.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "winapi.h" 18 | #include "syserr.h" 19 | 20 | /* DO NOT INCLUDE THIS FILE DIRECTLY */ 21 | struct ioq { 22 | struct runq run; 23 | struct waitq wait; 24 | 25 | /* IO completion port */ 26 | HANDLE iocp; 27 | }; 28 | 29 | /* If you want to perform IO on a handle and use the IO queue for 30 | * notification, you first need to bind the handle to the queue's 31 | * completion port. This operation is irreversible. 32 | * 33 | * If you perform IO on a bound handle, you must *first* either wait for 34 | * it, or set the lower-order bit of the OVERLAPPED structure's hEvent 35 | * member, to disable IOCP notification. Failure to follow these rules 36 | * may result in wild-pointer dereference and/or crash. 37 | */ 38 | static inline int ioq_bind(struct ioq *q, HANDLE hnd) 39 | { 40 | return CreateIoCompletionPort(hnd, q->iocp, 0, 0) ? 0 : -1; 41 | } 42 | 43 | /* IO queue handle objects. 44 | * 45 | * To perform an asynchronous IO operation, do the following: 46 | * 47 | * 0. Bind the HANDLE to the IO queue, if not done already. 48 | * 1. Call ioq_ovl_wait() (*before* the operation is started). 49 | * 2. Start the operation, using the LPOVERLAPPED value returned by 50 | * ioq_ovl_lpo(). 51 | * 3. Wait for completion. 52 | * 4. Call GetOverlappedResult() to obtain completion status. 53 | * 54 | * If step (2) fails, no further action is necessary. Waiting for the 55 | * operation actually does nothing except fill out the OVERLAPPED 56 | * structure as necessary for the IO queue. 57 | * 58 | * Another option, if the operation fails immediately, is to call 59 | * ioq_ovl_trigger(), specifying the error code. 60 | */ 61 | struct ioq_ovl; 62 | typedef void (*ioq_ovl_func_t)(struct ioq_ovl *h); 63 | 64 | struct ioq_ovl { 65 | struct runq_task task; 66 | OVERLAPPED overlapped; 67 | }; 68 | 69 | static inline void ioq_ovl_init(struct ioq_ovl *h, struct ioq *q) 70 | { 71 | memset(&h->overlapped, 0, sizeof(h->overlapped)); 72 | runq_task_init(&h->task, &q->run); 73 | } 74 | 75 | void ioq_ovl_wait(struct ioq_ovl *h, ioq_ovl_func_t f); 76 | 77 | static inline void ioq_ovl_trigger(struct ioq_ovl *h) 78 | { 79 | runq_task_exec(&h->task, h->task.func); 80 | } 81 | 82 | static inline LPOVERLAPPED ioq_ovl_lpo(struct ioq_ovl *h) 83 | { 84 | return &h->overlapped; 85 | } 86 | -------------------------------------------------------------------------------- /io/mailbox.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "mailbox.h" 18 | 19 | void mailbox_init(struct mailbox *m, struct runq *q) 20 | { 21 | runq_task_init(&m->task, q); 22 | thr_mutex_init(&m->lock); 23 | m->state = 0; 24 | m->expected = 0; 25 | m->mode = MAILBOX_WAIT_NONE; 26 | } 27 | 28 | void mailbox_destroy(struct mailbox *m) 29 | { 30 | thr_mutex_destroy(&m->lock); 31 | } 32 | 33 | mailbox_flags_t mailbox_take(struct mailbox *m, mailbox_flags_t clear_mask) 34 | { 35 | mailbox_flags_t ret; 36 | 37 | thr_mutex_lock(&m->lock); 38 | ret = m->state; 39 | m->state &= ~clear_mask; 40 | thr_mutex_unlock(&m->lock); 41 | 42 | return ret; 43 | } 44 | 45 | void mailbox_raise(struct mailbox *m, mailbox_flags_t set_mask) 46 | { 47 | int fire = 0; 48 | 49 | thr_mutex_lock(&m->lock); 50 | m->state |= set_mask; 51 | 52 | switch (m->mode) { 53 | case MAILBOX_WAIT_NONE: 54 | break; 55 | 56 | case MAILBOX_WAIT_ANY: 57 | if (m->expected & m->state) 58 | fire = 1; 59 | break; 60 | 61 | case MAILBOX_WAIT_ALL: 62 | if ((m->expected & m->state) == m->expected) 63 | fire = 1; 64 | break; 65 | } 66 | 67 | if (fire) 68 | m->mode = MAILBOX_WAIT_NONE; 69 | thr_mutex_unlock(&m->lock); 70 | 71 | if (fire) 72 | runq_task_exec(&m->task, m->task.func); 73 | } 74 | 75 | void mailbox_wait(struct mailbox *m, mailbox_flags_t set, mailbox_func_t cb) 76 | { 77 | int fire = 0; 78 | 79 | thr_mutex_lock(&m->lock); 80 | m->task.func = (runq_task_func_t)cb; 81 | m->expected = set; 82 | 83 | if (m->state & set) 84 | fire = 1; 85 | else 86 | m->mode = MAILBOX_WAIT_ANY; 87 | thr_mutex_unlock(&m->lock); 88 | 89 | if (fire) 90 | runq_task_exec(&m->task, m->task.func); 91 | } 92 | 93 | void mailbox_wait_all(struct mailbox *m, mailbox_flags_t set, 94 | mailbox_func_t cb) 95 | { 96 | int fire = 0; 97 | 98 | thr_mutex_lock(&m->lock); 99 | m->task.func = (runq_task_func_t)cb; 100 | m->expected = set; 101 | 102 | if ((m->state & set) == set) 103 | fire = 1; 104 | else 105 | m->mode = MAILBOX_WAIT_ALL; 106 | thr_mutex_unlock(&m->lock); 107 | 108 | if (fire) 109 | runq_task_exec(&m->task, m->task.func); 110 | } 111 | -------------------------------------------------------------------------------- /io/mailbox.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_MAILBOX_H_ 18 | #define IO_MAILBOX_H_ 19 | 20 | #include 21 | #include "runq.h" 22 | #include "thr.h" 23 | 24 | /* A mailbox is an asynchronous IPC primitive. Associated with a mailbox 25 | * are a set of 32 flags, which can be either raised or cleared. 26 | * 27 | * Flags can be raised from any of multiple producer threads. A single 28 | * consumer thread can asynchronously wait for any subset of the flags 29 | * to become raised. 30 | * 31 | * Flag sets are represented using a bitmask. 32 | */ 33 | typedef uint32_t mailbox_flags_t; 34 | 35 | #define MAILBOX_NUM_FLAGS 32 36 | #define MAILBOX_ALL_FLAGS ((mailbox_flags_t)0xffffffff) 37 | #define MAILBOX_FLAG(n) (((mailbox_flags_t)1) << (n)) 38 | 39 | /* Mailbox data structure. A mailbox may be accessed from only a single 40 | * thread at a time, with the exception of mailbox_raise(), which may be 41 | * called from any number of threads on a valid mailbox. 42 | */ 43 | typedef enum { 44 | MAILBOX_WAIT_NONE = 0, 45 | MAILBOX_WAIT_ANY, 46 | MAILBOX_WAIT_ALL 47 | } mailbox_wait_mode_t; 48 | 49 | struct mailbox { 50 | /* Must be first element */ 51 | struct runq_task task; 52 | 53 | thr_mutex_t lock; 54 | mailbox_flags_t state; 55 | mailbox_flags_t expected; 56 | mailbox_wait_mode_t mode; 57 | }; 58 | 59 | /* Initialize a mailbox, linking it with a run queue */ 60 | void mailbox_init(struct mailbox *m, struct runq *q); 61 | 62 | /* Destroy a mailbox */ 63 | void mailbox_destroy(struct mailbox *m); 64 | 65 | /* Obtain the current mailbox state. The optional clear argument 66 | * specifies which flags should be cleared, if any. 67 | */ 68 | mailbox_flags_t mailbox_take(struct mailbox *m, mailbox_flags_t clear_mask); 69 | 70 | /* Raise the given set mailbox flags. Other flags are untouched. This 71 | * function may be called from any thread. 72 | */ 73 | void mailbox_raise(struct mailbox *m, mailbox_flags_t set_mask); 74 | 75 | /* Begin an asynchronous wait on a mailbox. The wait will be completed 76 | * when any of the flags in the set become raised. If any of the flags 77 | * are already raised, the wait completes immediately. Waiting for the 78 | * empty set will also complete immediately. 79 | * 80 | * You may not use/modify/destroy the mailbox until the wait is complete 81 | * (apart from calling mailbox_raise()). 82 | */ 83 | typedef void (*mailbox_func_t)(struct mailbox *m); 84 | 85 | void mailbox_wait(struct mailbox *m, mailbox_flags_t set, mailbox_func_t cb); 86 | 87 | /* Begin an asynchronous wait on a mailbox, but don't allow it to 88 | * complete until *all* of the flags in the waiting set become raised. 89 | */ 90 | void mailbox_wait_all(struct mailbox *m, mailbox_flags_t set, 91 | mailbox_func_t cb); 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /io/net.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __Windows__ 18 | #include "net.h" 19 | 20 | BOOL PASCAL (*net_ConnectEx)( 21 | SOCKET s, 22 | const struct sockaddr *name, 23 | int namelen, 24 | PVOID lpSendBuffer, 25 | DWORD dwSendDataLength, 26 | LPDWORD lpdwBytesSent, 27 | LPOVERLAPPED lpOverlapped 28 | ); 29 | 30 | BOOL (*net_AcceptEx)( 31 | SOCKET sListenSocket, 32 | SOCKET sAcceptSocket, 33 | PVOID lpOutputBuffer, 34 | DWORD dwReceiveDataLength, 35 | DWORD dwLocalAddressLength, 36 | DWORD dwRemoteAddressLength, 37 | LPDWORD lpdwBytesReceived, 38 | LPOVERLAPPED lpOverlapped 39 | ); 40 | 41 | static GUID id_AcceptEx = { 42 | 0xb5367df1, 0xcbac, 0x11cf, 43 | {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92} 44 | }; 45 | 46 | static GUID id_ConnectEx = { 47 | 0x25a207b9, 0xddf3, 0x4660, 48 | {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e} 49 | }; 50 | 51 | neterr_t net_start(void) 52 | { 53 | WSADATA data; 54 | DWORD count; 55 | neterr_t err; 56 | SOCKET s; 57 | 58 | err = WSAStartup(MAKEWORD(2, 2), &data); 59 | if (err) 60 | return err; 61 | 62 | s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0); 63 | if (s == INVALID_SOCKET) { 64 | neterr_t e = neterr_last(); 65 | 66 | WSACleanup(); 67 | return e; 68 | } 69 | 70 | if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 71 | &id_AcceptEx, sizeof(id_AcceptEx), 72 | &net_AcceptEx, sizeof(net_AcceptEx), 73 | &count, NULL, NULL) || 74 | WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 75 | &id_ConnectEx, sizeof(id_ConnectEx), 76 | &net_ConnectEx, sizeof(net_ConnectEx), 77 | &count, NULL, NULL)) { 78 | neterr_t e = neterr_last(); 79 | 80 | closesocket(s); 81 | WSACleanup(); 82 | return e; 83 | } 84 | 85 | closesocket(s); 86 | return 0; 87 | } 88 | #endif 89 | -------------------------------------------------------------------------------- /io/net.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_NET_H_ 18 | #define IO_NET_H_ 19 | 20 | #define NETERR_NONE ((neterr_t)0) 21 | 22 | #ifdef __Windows__ 23 | #include "winapi.h" 24 | #include 25 | #include 26 | 27 | typedef DWORD neterr_t; 28 | 29 | static inline neterr_t neterr_last(void) 30 | { 31 | return WSAGetLastError(); 32 | } 33 | 34 | static inline void neterr_format(neterr_t e, char *buf, size_t max_size) 35 | { 36 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, 0, 37 | buf, max_size, NULL); 38 | } 39 | 40 | typedef SOCKET net_sock_t; 41 | 42 | static inline int net_sock_is_valid(net_sock_t s) 43 | { 44 | return s != INVALID_SOCKET; 45 | } 46 | 47 | #define NET_SOCK_INVALID INVALID_SOCKET 48 | 49 | /* Start up/shut down the network stack. This pair of functions must be 50 | * called at startup before any network functions are used, and at exit 51 | * to clean up. 52 | * 53 | * net_start() returns an error code (NETERR_NONE if successful). 54 | */ 55 | neterr_t net_start(void); 56 | 57 | static inline void net_stop(void) 58 | { 59 | WSACleanup(); 60 | } 61 | 62 | /* These pointers are looked up by net_start() */ 63 | extern BOOL PASCAL (*net_ConnectEx)( 64 | SOCKET s, 65 | const struct sockaddr *name, 66 | int namelen, 67 | PVOID lpSendBuffer, 68 | DWORD dwSendDataLength, 69 | LPDWORD lpdwBytesSent, 70 | LPOVERLAPPED lpOverlapped 71 | ); 72 | 73 | extern BOOL (*net_AcceptEx)( 74 | SOCKET sListenSocket, 75 | SOCKET sAcceptSocket, 76 | PVOID lpOutputBuffer, 77 | DWORD dwReceiveDataLength, 78 | DWORD dwLocalAddressLength, 79 | DWORD dwRemoteAddressLength, 80 | LPDWORD lpdwBytesReceived, 81 | LPOVERLAPPED lpOverlapped 82 | ); 83 | #else 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | typedef int neterr_t; 92 | 93 | static inline neterr_t neterr_last(void) 94 | { 95 | return errno; 96 | } 97 | 98 | static inline void neterr_format(neterr_t e, char *buf, size_t max_size) 99 | { 100 | strncpy(buf, strerror(e), max_size); 101 | buf[max_size - 1] = 0; 102 | } 103 | 104 | typedef int net_sock_t; 105 | 106 | static inline int net_sock_is_valid(net_sock_t s) 107 | { 108 | return s >= 0; 109 | } 110 | 111 | #define NET_SOCK_INVALID ((net_sock_t)(-1)) 112 | 113 | static inline neterr_t net_start(void) 114 | { 115 | return NETERR_NONE; 116 | } 117 | 118 | static inline void net_stop(void) { } 119 | #endif 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /io/runq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "syserr.h" 18 | #include "runq.h" 19 | #include "containers.h" 20 | 21 | static int run_one(struct runq *r) 22 | { 23 | struct slist_node *n; 24 | struct runq_task *t; 25 | 26 | thr_mutex_lock(&r->lock); 27 | if (r->quit_request) { 28 | thr_mutex_unlock(&r->lock); 29 | return -1; 30 | } 31 | 32 | n = slist_pop(&r->job_list); 33 | thr_mutex_unlock(&r->lock); 34 | 35 | if (!n) 36 | return 0; 37 | 38 | t = container_of(n, struct runq_task, job_list); 39 | t->func(t); 40 | return 1; 41 | } 42 | 43 | static void worker_func(void *arg) 44 | { 45 | struct runq_worker *w = (struct runq_worker *)arg; 46 | 47 | for (;;) { 48 | thr_event_wait(&w->wakeup); 49 | thr_event_clear(&w->wakeup); 50 | 51 | for (;;) { 52 | int result = run_one(w->parent); 53 | 54 | if (result < 0) 55 | return; 56 | if (!result) 57 | break; 58 | } 59 | } 60 | } 61 | 62 | static void join_worker(struct runq_worker *w) 63 | { 64 | thr_event_raise(&w->wakeup); 65 | thr_join(w->thread); 66 | thr_event_destroy(&w->wakeup); 67 | } 68 | 69 | static void request_quit(struct runq *r) 70 | { 71 | thr_mutex_lock(&r->lock); 72 | r->quit_request = 1; 73 | thr_mutex_unlock(&r->lock); 74 | } 75 | 76 | static int init_worker(struct runq *r, struct runq_worker *w) 77 | { 78 | w->parent = r; 79 | 80 | if (thr_event_init(&w->wakeup) < 0) 81 | return -1; 82 | 83 | if (thr_start(&w->thread, worker_func, w) < 0) { 84 | thr_event_destroy(&w->wakeup); 85 | return -1; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | int runq_init(struct runq *r, unsigned int bg_workers) 92 | { 93 | int i; 94 | 95 | r->wakeup = NULL; 96 | r->num_workers = bg_workers; 97 | r->quit_request = 0; 98 | slist_init(&r->job_list); 99 | thr_mutex_init(&r->lock); 100 | 101 | if (!bg_workers) { 102 | r->workers = NULL; 103 | return 0; 104 | } 105 | 106 | r->workers = malloc(sizeof(r->workers[0]) * bg_workers); 107 | if (!r->workers) { 108 | thr_mutex_destroy(&r->lock); 109 | return -1; 110 | } 111 | 112 | for (i = 0; i < bg_workers; i++) 113 | if (init_worker(r, &r->workers[i]) < 0) { 114 | syserr_t err = syserr_last(); 115 | 116 | request_quit(r); 117 | 118 | while (i > 0) { 119 | i--; 120 | join_worker(&r->workers[i]); 121 | } 122 | 123 | free(r->workers); 124 | thr_mutex_destroy(&r->lock); 125 | syserr_set(err); 126 | return -1; 127 | } 128 | 129 | return 0; 130 | } 131 | 132 | void runq_destroy(struct runq *r) 133 | { 134 | int i; 135 | 136 | if (r->num_workers) { 137 | request_quit(r); 138 | 139 | for (i = 0; i < r->num_workers; i++) 140 | join_worker(&r->workers[i]); 141 | 142 | free(r->workers); 143 | } 144 | 145 | thr_mutex_destroy(&r->lock); 146 | } 147 | 148 | unsigned int runq_dispatch(struct runq *r, unsigned int limit) 149 | { 150 | int count = 0; 151 | 152 | while (!limit || (count < limit)) { 153 | if (run_one(r) <= 0) 154 | break; 155 | 156 | count++; 157 | } 158 | 159 | return count; 160 | } 161 | 162 | void runq_task_exec(struct runq_task *t, runq_task_func_t func) 163 | { 164 | struct runq *r = t->owner; 165 | int was_empty; 166 | 167 | t->func = func; 168 | 169 | thr_mutex_lock(&r->lock); 170 | was_empty = slist_is_empty(&r->job_list); 171 | slist_append(&r->job_list, &t->job_list); 172 | thr_mutex_unlock(&r->lock); 173 | 174 | if (was_empty) { 175 | int i; 176 | 177 | for (i = 0; i < r->num_workers; i++) 178 | thr_event_raise(&r->workers[i].wakeup); 179 | 180 | if (r->wakeup) 181 | r->wakeup(r); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /io/runq.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_RUNQ_H_ 18 | #define IO_RUNQ_H_ 19 | 20 | #include "thr.h" 21 | #include "slist.h" 22 | 23 | /* Asynchronous run-queue. This object manages a pool of worker threads, 24 | * to which tasks (functions) may be submitted for execution. 25 | */ 26 | struct runq; 27 | typedef void (*runq_wakeup_t)(struct runq *r); 28 | 29 | struct runq_worker { 30 | struct runq *parent; 31 | thr_thread_t thread; 32 | thr_event_t wakeup; 33 | }; 34 | 35 | struct runq { 36 | /* You can set this hook to a function to be called whenever the 37 | * queue goes from empty to non-empty. It must be configured 38 | * before submitting any jobs. 39 | */ 40 | runq_wakeup_t wakeup; 41 | 42 | unsigned int num_workers; 43 | struct runq_worker *workers; 44 | 45 | thr_mutex_t lock; 46 | struct slist job_list; 47 | int quit_request; 48 | }; 49 | 50 | /* Initialize a run-queue, specifying the number of background workers. 51 | * If this number is 0, no background workers will be created, and the 52 | * queue must be flushed periodically. 53 | */ 54 | int runq_init(struct runq *r, unsigned int bg_workers); 55 | 56 | /* Destroy the run-queue, and tear down any background workers. */ 57 | void runq_destroy(struct runq *r); 58 | 59 | /* If no background threads are available, you need to periodically call 60 | * this function to execute tasks. You may optionally specify a limit (0 61 | * means no limit), and the function will return the number of tasks 62 | * executed. 63 | */ 64 | unsigned int runq_dispatch(struct runq *r, unsigned int limit); 65 | 66 | /* A submittable job is represented by the following structure. */ 67 | struct runq_task; 68 | typedef void (*runq_task_func_t)(struct runq_task *t); 69 | 70 | struct runq_task { 71 | struct slist_node job_list; 72 | runq_task_func_t func; 73 | struct runq *owner; 74 | }; 75 | 76 | /* Initialize a task by associating it with a run-queue. */ 77 | static inline void runq_task_init(struct runq_task *t, struct runq *q) 78 | { 79 | t->owner = q; 80 | } 81 | 82 | /* Submit a job to the run-queue. The specified function will be 83 | * executed at some later date. Until that occurs, you must not modify 84 | * or move the runq_task structure. As soon as the function is invoked, 85 | * the runq_task structure is no longer referenced by the runq. 86 | * 87 | * This function can be called from any thread, but concurrent access to 88 | * the same task is forbidden. 89 | * 90 | * You may not re-use a runq_task structure which is still pending 91 | * execution. 92 | */ 93 | void runq_task_exec(struct runq_task *t, runq_task_func_t func); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /io/syserr.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_SYSERR_H_ 18 | #define IO_SYSERR_H_ 19 | 20 | #include 21 | 22 | /* System error type */ 23 | #ifdef __Windows__ 24 | #include "winapi.h" 25 | 26 | typedef DWORD syserr_t; 27 | 28 | static inline syserr_t syserr_last(void) 29 | { 30 | return GetLastError(); 31 | } 32 | 33 | static inline void syserr_set(syserr_t err) 34 | { 35 | SetLastError(err); 36 | } 37 | 38 | static inline void syserr_format(syserr_t err, char *buf, size_t max_size) 39 | { 40 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, 41 | buf, max_size, NULL); 42 | } 43 | #else 44 | #include 45 | 46 | typedef int syserr_t; 47 | 48 | static inline syserr_t syserr_last(void) 49 | { 50 | return errno; 51 | } 52 | 53 | static inline void syserr_set(syserr_t err) 54 | { 55 | errno = err; 56 | } 57 | 58 | static inline void syserr_format(syserr_t err, char *buf, size_t max_size) 59 | { 60 | strncpy(buf, strerror(err), max_size); 61 | buf[max_size - 1] = 0; 62 | } 63 | #endif 64 | 65 | #define SYSERR_NONE ((syserr_t)0) 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /io/thr.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "thr.h" 18 | 19 | #ifndef __Windows__ 20 | #include 21 | 22 | int thr_event_init(thr_event_t *e) 23 | { 24 | pthread_mutex_init(&e->lock, NULL); 25 | pthread_cond_init(&e->cond, NULL); 26 | e->state = 0; 27 | return 0; 28 | } 29 | 30 | void thr_event_destroy(thr_event_t *e) 31 | { 32 | pthread_cond_destroy(&e->cond); 33 | pthread_mutex_destroy(&e->lock); 34 | } 35 | 36 | void thr_event_raise(thr_event_t *e) 37 | { 38 | int old; 39 | 40 | pthread_mutex_lock(&e->lock); 41 | old = e->state; 42 | e->state = 1; 43 | pthread_mutex_unlock(&e->lock); 44 | 45 | if (!old) 46 | pthread_cond_signal(&e->cond); 47 | } 48 | 49 | void thr_event_clear(thr_event_t *e) 50 | { 51 | pthread_mutex_lock(&e->lock); 52 | e->state = 0; 53 | pthread_mutex_unlock(&e->lock); 54 | } 55 | 56 | void thr_event_wait(thr_event_t *e) 57 | { 58 | pthread_mutex_lock(&e->lock); 59 | while (!e->state) 60 | pthread_cond_wait(&e->cond, &e->lock); 61 | pthread_mutex_unlock(&e->lock); 62 | } 63 | 64 | int thr_event_wait_timeout(thr_event_t *e, int timeout_ms) 65 | { 66 | struct timeval now; 67 | struct timespec to; 68 | 69 | gettimeofday(&now, NULL); 70 | to.tv_sec = now.tv_sec + timeout_ms / 1000; 71 | to.tv_nsec = now.tv_usec * 1000LL + 72 | (timeout_ms % 1000) * 1000000LL; 73 | 74 | if (to.tv_nsec >= 1000000000LL) { 75 | to.tv_nsec -= 1000000000LL; 76 | to.tv_sec++; 77 | } 78 | 79 | pthread_mutex_lock(&e->lock); 80 | while (!e->state) 81 | if (pthread_cond_timedwait(&e->cond, &e->lock, &to)) { 82 | pthread_mutex_unlock(&e->lock); 83 | return -1; 84 | } 85 | pthread_mutex_unlock(&e->lock); 86 | 87 | return 0; 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /io/thr.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_THR_H_ 18 | #define IO_THR_H_ 19 | 20 | /* Thread start routine */ 21 | typedef void (*thr_func_t)(void *arg); 22 | 23 | #ifdef __Windows__ 24 | #include "winapi.h" 25 | 26 | /* Thread creation/join */ 27 | typedef HANDLE thr_thread_t; 28 | 29 | static inline int thr_start(thr_thread_t *thr, thr_func_t func, void *arg) 30 | { 31 | *thr = CreateThread(NULL, 0, 32 | (LPTHREAD_START_ROUTINE)func, arg, 0, NULL); 33 | 34 | return (*thr) ? 0 : -1; 35 | } 36 | 37 | static inline void thr_join(thr_thread_t thr) 38 | { 39 | WaitForSingleObject(thr, INFINITE); 40 | } 41 | 42 | /* Mutexes */ 43 | typedef CRITICAL_SECTION thr_mutex_t; 44 | 45 | static inline void thr_mutex_init(thr_mutex_t *m) 46 | { 47 | InitializeCriticalSection(m); 48 | } 49 | 50 | static inline void thr_mutex_destroy(thr_mutex_t *m) 51 | { 52 | DeleteCriticalSection(m); 53 | } 54 | 55 | static inline void thr_mutex_lock(thr_mutex_t *m) 56 | { 57 | EnterCriticalSection(m); 58 | } 59 | 60 | static inline void thr_mutex_unlock(thr_mutex_t *m) 61 | { 62 | LeaveCriticalSection(m); 63 | } 64 | 65 | /* Event variables */ 66 | typedef HANDLE thr_event_t; 67 | 68 | static inline int thr_event_init(thr_event_t *e) 69 | { 70 | *e = CreateEvent(NULL, TRUE, FALSE, NULL); 71 | 72 | return *e ? 0 : -1; 73 | } 74 | 75 | static inline void thr_event_destroy(thr_event_t *e) 76 | { 77 | CloseHandle(*e); 78 | } 79 | 80 | static inline void thr_event_raise(thr_event_t *e) 81 | { 82 | SetEvent(*e); 83 | } 84 | 85 | static inline void thr_event_clear(thr_event_t *e) 86 | { 87 | ResetEvent(*e); 88 | } 89 | 90 | static inline void thr_event_wait(thr_event_t *e) 91 | { 92 | WaitForSingleObject(*e, INFINITE); 93 | } 94 | 95 | static inline int thr_event_wait_timeout(thr_event_t *e, int timeout_ms) 96 | { 97 | return WaitForSingleObject(*e, timeout_ms); 98 | } 99 | #else 100 | #include 101 | 102 | /* Thread creation/join */ 103 | typedef pthread_t thr_thread_t; 104 | 105 | static inline int thr_start(thr_thread_t *thr, thr_func_t func, void *arg) 106 | { 107 | return pthread_create(thr, NULL, (void *(*)(void *))func, arg); 108 | } 109 | 110 | static inline void thr_join(thr_thread_t thr) 111 | { 112 | pthread_join(thr, NULL); 113 | } 114 | 115 | /* Mutexes */ 116 | typedef pthread_mutex_t thr_mutex_t; 117 | 118 | static inline void thr_mutex_init(thr_mutex_t *m) 119 | { 120 | pthread_mutex_init(m, NULL); 121 | } 122 | 123 | static inline void thr_mutex_destroy(thr_mutex_t *m) 124 | { 125 | pthread_mutex_destroy(m); 126 | } 127 | 128 | static inline void thr_mutex_lock(thr_mutex_t *m) 129 | { 130 | pthread_mutex_lock(m); 131 | } 132 | 133 | static inline void thr_mutex_unlock(thr_mutex_t *m) 134 | { 135 | pthread_mutex_unlock(m); 136 | } 137 | 138 | /* Event variables */ 139 | struct thr_pthread_event { 140 | pthread_mutex_t lock; 141 | pthread_cond_t cond; 142 | int state; 143 | }; 144 | 145 | typedef struct thr_pthread_event thr_event_t; 146 | 147 | int thr_event_init(thr_event_t *e); 148 | void thr_event_destroy(thr_event_t *e); 149 | void thr_event_raise(thr_event_t *e); 150 | void thr_event_clear(thr_event_t *e); 151 | void thr_event_wait(thr_event_t *e); 152 | int thr_event_wait_timeout(thr_event_t *e, int timeout_ms); 153 | #endif 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /io/waitq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "waitq.h" 18 | #include "rbt_iter.h" 19 | #include "containers.h" 20 | 21 | static int cmp_by_deadline(const void *key, const struct rbt_node *node) 22 | { 23 | const struct waitq_timer *kt = (const struct waitq_timer *)key; 24 | const struct waitq_timer *nt = 25 | container_of(node, const struct waitq_timer, waiting_set); 26 | 27 | if (kt->deadline < nt->deadline) 28 | return -1; 29 | if (kt->deadline > nt->deadline) 30 | return 1; 31 | if (kt < nt) 32 | return -1; 33 | if (kt > nt) 34 | return 1; 35 | 36 | return 0; 37 | } 38 | 39 | static struct waitq_timer *expire_one(struct waitq *wq, clock_ticks_t now) 40 | { 41 | struct rbt_node *n; 42 | struct waitq_timer *t = NULL; 43 | 44 | thr_mutex_lock(&wq->lock); 45 | n = rbt_iter_first(&wq->waiting_set); 46 | if (n) { 47 | t = container_of(n, struct waitq_timer, waiting_set); 48 | if (t->deadline > now) 49 | t = NULL; 50 | else 51 | rbt_remove(&wq->waiting_set, n); 52 | } 53 | thr_mutex_unlock(&wq->lock); 54 | 55 | return t; 56 | } 57 | 58 | void waitq_init(struct waitq *wq, struct runq *rq) 59 | { 60 | wq->run = rq; 61 | wq->wakeup = NULL; 62 | thr_mutex_init(&wq->lock); 63 | rbt_init(&wq->waiting_set, cmp_by_deadline); 64 | } 65 | 66 | void waitq_destroy(struct waitq *wq) 67 | { 68 | thr_mutex_destroy(&wq->lock); 69 | } 70 | 71 | int waitq_next_deadline(struct waitq *wq) 72 | { 73 | struct rbt_node *n; 74 | clock_ticks_t now = clock_now(); 75 | clock_ticks_t deadline; 76 | 77 | thr_mutex_lock(&wq->lock); 78 | n = rbt_iter_first(&wq->waiting_set); 79 | if (n) 80 | deadline = container_of(n, struct waitq_timer, 81 | waiting_set)->deadline; 82 | thr_mutex_unlock(&wq->lock); 83 | 84 | if (!n) 85 | return -1; 86 | if (deadline < now) 87 | return 0; 88 | 89 | return deadline - now; 90 | } 91 | 92 | unsigned int waitq_dispatch(struct waitq *wq, unsigned int limit) 93 | { 94 | unsigned int count = 0; 95 | clock_ticks_t now = clock_now(); 96 | 97 | while (!limit || count < limit) { 98 | struct waitq_timer *t = expire_one(wq, now); 99 | 100 | if (!t) 101 | break; 102 | 103 | runq_task_exec(&t->task, t->task.func); 104 | count++; 105 | } 106 | 107 | return count; 108 | } 109 | 110 | void waitq_timer_init(struct waitq_timer *t, struct waitq *q) 111 | { 112 | runq_task_init(&t->task, q->run); 113 | t->owner = q; 114 | } 115 | 116 | static void wset_add(struct waitq_timer *t) 117 | { 118 | struct waitq *wq = t->owner; 119 | int need_wakeup; 120 | 121 | thr_mutex_lock(&wq->lock); 122 | rbt_insert(&wq->waiting_set, t, &t->waiting_set); 123 | need_wakeup = !rbt_iter_prev(&t->waiting_set); 124 | thr_mutex_unlock(&wq->lock); 125 | 126 | if (need_wakeup && wq->wakeup) 127 | wq->wakeup(wq); 128 | } 129 | 130 | static int wset_remove(struct waitq_timer *t) 131 | { 132 | struct waitq *wq = t->owner; 133 | struct rbt_node *n; 134 | int need_wakeup = 0; 135 | 136 | thr_mutex_lock(&wq->lock); 137 | n = rbt_find(&wq->waiting_set, t); 138 | if (n) { 139 | rbt_remove(&wq->waiting_set, n); 140 | need_wakeup = !rbt_iter_prev(n); 141 | } 142 | thr_mutex_unlock(&wq->lock); 143 | 144 | if (need_wakeup && wq->wakeup) 145 | wq->wakeup(wq); 146 | 147 | return n != NULL; 148 | } 149 | 150 | void waitq_timer_wait(struct waitq_timer *t, 151 | int interval_ms, waitq_timer_func_t func) 152 | { 153 | t->task.func = (runq_task_func_t)func; 154 | t->deadline = clock_now() + interval_ms; 155 | wset_add(t); 156 | } 157 | 158 | void waitq_timer_cancel(struct waitq_timer *t) 159 | { 160 | if (wset_remove(t)) { 161 | t->deadline = 0; 162 | runq_task_exec(&t->task, t->task.func); 163 | } 164 | } 165 | 166 | void waitq_timer_reschedule(struct waitq_timer *t, int interval_ms) 167 | { 168 | if (wset_remove(t)) { 169 | t->deadline = clock_now() + interval_ms; 170 | wset_add(t); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /io/waitq.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_WAITQ_H_ 18 | #define IO_WAITQ_H_ 19 | 20 | #include "runq.h" 21 | #include "clock.h" 22 | #include "rbt.h" 23 | #include "thr.h" 24 | 25 | /* Wakeup hook. This can be used to run a function whenever the deadline 26 | * of the next-to-expire timer changes. 27 | */ 28 | struct waitq; 29 | typedef void (*waitq_wakeup_t)(struct waitq *q); 30 | 31 | /* Wait queue data structure */ 32 | struct waitq { 33 | waitq_wakeup_t wakeup; 34 | struct runq *run; 35 | 36 | thr_mutex_t lock; 37 | struct rbt waiting_set; 38 | }; 39 | 40 | /* Initialize/destroy a wait queue. Destroying a wait queue does not 41 | * cause any timers to expire. 42 | * 43 | * A waitq needs to be linked to a runq. Timers become runnable tasks on 44 | * expiry. 45 | */ 46 | void waitq_init(struct waitq *wq, struct runq *rq); 47 | void waitq_destroy(struct waitq *wq); 48 | 49 | /* Find the time, in milliseconds, to the next timer expiry. Returns 0 50 | * if there are timers expired already. Returns -1 if there are no 51 | * timers in the set. 52 | */ 53 | int waitq_next_deadline(struct waitq *wq); 54 | 55 | /* For each timer which has expired, enqueue the completion handler in 56 | * the linked run queue. A limit may be specified (0 means no limit). 57 | */ 58 | unsigned int waitq_dispatch(struct waitq *wq, unsigned int limit); 59 | 60 | /* Timer completion handler function. This is invoked after expiry or 61 | * cancellation of a timer. 62 | */ 63 | struct waitq_timer; 64 | typedef void (*waitq_timer_func_t)(struct waitq_timer *t); 65 | 66 | /* Timer data structure. This does not need to be initialized, provided 67 | * that no use of it is made before a call to waitq_wait(). If this is a 68 | * possibility, it should be set memset() to 0 first. 69 | */ 70 | struct waitq_timer { 71 | /* This must be the first element in the struct */ 72 | struct runq_task task; 73 | 74 | struct rbt_node waiting_set; 75 | clock_ticks_t deadline; 76 | 77 | struct waitq *owner; 78 | }; 79 | 80 | /* Initialize a timer by associating it with a wait queue. */ 81 | void waitq_timer_init(struct waitq_timer *t, struct waitq *q); 82 | 83 | /* Asynchronously wait for an interval. The given function will be 84 | * executed after the specified interval has elapsed. 85 | * 86 | * The waitq_timer structure may not be modified, reused or destroyed 87 | * until the callback function has been invoked. As soon as the callback 88 | * function starts executing, the timer is safe to modify/destroy. 89 | */ 90 | void waitq_timer_wait(struct waitq_timer *t, 91 | int interval_ms, waitq_timer_func_t func); 92 | 93 | /* Determine whether the given timer expired due to cancellation. This 94 | * function is not safe to use on an unexpired timer. 95 | */ 96 | static inline int waitq_timer_cancelled(struct waitq_timer *t) 97 | { 98 | return !t->deadline; 99 | } 100 | 101 | /* Attempt to cancel a timer. If the timer is already expired, this 102 | * function has no effect. If the timer has yet to expire, requesting 103 | * cancellation will hasten its expiry. 104 | */ 105 | void waitq_timer_cancel(struct waitq_timer *t); 106 | 107 | /* Attempt to reschedule a timer. If the timer has already expired, this 108 | * function has no effect. Otherwise, the timer's deadline is changed. 109 | */ 110 | void waitq_timer_reschedule(struct waitq_timer *t, int interval_ms); 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /io/winapi.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef IO_WINAPI_H_ 18 | #define IO_WINAPI_H_ 19 | 20 | /* Workaround for buggy windows.h header */ 21 | #ifdef __Windows__ 22 | 23 | /* MinGW doesn't define this correctly, which means we end up with some 24 | * functions hidden. 25 | */ 26 | #if _WIN32_WINNT < 0x0501 27 | #undef _WIN32_WINNT 28 | #define _WIN32_WINNT 0x0501 29 | #endif 30 | 31 | /* MinGW's windows.h includes winsock.h if we don't define certain 32 | * guards. This prevents us from later including winsock2.h and 33 | * ws2tcpip.h. 34 | */ 35 | #define __MSYS__ 36 | #include 37 | #undef __MSYS__ 38 | 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/arena.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2014 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef ARENA_H_ 18 | #define ARENA_H_ 19 | 20 | #include 21 | 22 | /* This object is a memory allocator for fixed-size heaps. Initialize it 23 | * with a large chunk of memory and it will provide implementations of 24 | * malloc, free and realloc. 25 | * 26 | * The algorithm used is buddy allocation. All operations are O(log N) 27 | * in the size of the heap. 28 | */ 29 | #define ARENA_ORDERS 32 30 | 31 | struct arena_block; 32 | 33 | struct arena { 34 | void *base; 35 | size_t size; 36 | 37 | struct arena_block *blocks[ARENA_ORDERS]; 38 | }; 39 | 40 | /* Initialize a new arena */ 41 | void arena_init(struct arena *a, void *base, size_t size); 42 | 43 | /* Allocate and free blocks */ 44 | void *arena_alloc(struct arena *a, size_t size); 45 | void arena_free(struct arena *a, void *ptr); 46 | 47 | /* Reallocate a block (if necessary) */ 48 | void *arena_realloc(struct arena *a, void *ptr, size_t new_size); 49 | 50 | /* Count up free space available in blocks */ 51 | size_t arena_count_free(const struct arena *a); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/bytes.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef BYTES_H_ 18 | #define BYTES_H_ 19 | 20 | #include 21 | 22 | /* Utility functions for dealing with endian-specific data of unknown 23 | * alignment. 24 | */ 25 | static inline uint16_t bytes_r16le(const uint8_t *data) 26 | { 27 | return ((uint16_t)data[0]) | (((uint16_t)data[1]) << 8); 28 | } 29 | 30 | static inline uint16_t bytes_r16net(const uint8_t *data) 31 | { 32 | return ((uint16_t)data[1]) | (((uint16_t)data[0]) << 8); 33 | } 34 | 35 | static inline void bytes_w16le(uint8_t *data, uint16_t v) 36 | { 37 | data[0] = v; 38 | data[1] = v >> 8; 39 | } 40 | 41 | static inline void bytes_w16net(uint8_t *data, uint16_t v) 42 | { 43 | data[1] = v; 44 | data[0] = v >> 8; 45 | } 46 | 47 | static inline uint32_t bytes_r32le(const uint8_t *data) 48 | { 49 | return ((uint32_t)data[0]) | 50 | (((uint32_t)data[1]) << 8) | 51 | (((uint32_t)data[2]) << 16) | 52 | (((uint32_t)data[3]) << 24); 53 | } 54 | 55 | static inline uint32_t bytes_r32net(const uint8_t *data) 56 | { 57 | return ((uint32_t)data[3]) | 58 | (((uint32_t)data[2]) << 8) | 59 | (((uint32_t)data[1]) << 16) | 60 | (((uint32_t)data[0]) << 24); 61 | } 62 | 63 | static inline void bytes_w32le(uint8_t *data, uint32_t v) 64 | { 65 | data[0] = v; 66 | data[1] = v >> 8; 67 | data[2] = v >> 16; 68 | data[3] = v >> 24; 69 | } 70 | 71 | static inline void bytes_w32net(uint8_t *data, uint32_t v) 72 | { 73 | data[3] = v; 74 | data[2] = v >> 8; 75 | data[1] = v >> 16; 76 | data[0] = v >> 24; 77 | } 78 | 79 | static inline uint64_t bytes_r64le(const uint8_t *data) 80 | { 81 | return ((uint64_t)data[0]) | 82 | (((uint64_t)data[1]) << 8) | 83 | (((uint64_t)data[2]) << 16) | 84 | (((uint64_t)data[3]) << 24) | 85 | (((uint64_t)data[4]) << 32) | 86 | (((uint64_t)data[5]) << 40) | 87 | (((uint64_t)data[6]) << 48) | 88 | (((uint64_t)data[7]) << 56); 89 | } 90 | 91 | static inline uint64_t bytes_r64net(const uint8_t *data) 92 | { 93 | return ((uint64_t)data[7]) | 94 | (((uint64_t)data[6]) << 8) | 95 | (((uint64_t)data[5]) << 16) | 96 | (((uint64_t)data[4]) << 24) | 97 | (((uint64_t)data[3]) << 32) | 98 | (((uint64_t)data[2]) << 40) | 99 | (((uint64_t)data[1]) << 48) | 100 | (((uint64_t)data[0]) << 56); 101 | } 102 | 103 | static inline void bytes_w64le(uint8_t *data, uint64_t v) 104 | { 105 | data[0] = v; 106 | data[1] = v >> 8; 107 | data[2] = v >> 16; 108 | data[3] = v >> 24; 109 | data[4] = v >> 32; 110 | data[5] = v >> 40; 111 | data[6] = v >> 48; 112 | data[7] = v >> 56; 113 | } 114 | 115 | static inline void bytes_w64net(uint8_t *data, uint64_t v) 116 | { 117 | data[7] = v; 118 | data[6] = v >> 8; 119 | data[5] = v >> 16; 120 | data[4] = v >> 24; 121 | data[3] = v >> 32; 122 | data[2] = v >> 40; 123 | data[1] = v >> 48; 124 | data[0] = v >> 56; 125 | } 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /src/cbuf.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "cbuf.h" 18 | 19 | void cbuf_init(struct cbuf *c, uint8_t *data, size_t capacity) 20 | { 21 | c->data = data; 22 | c->capacity = capacity; 23 | c->head = 0; 24 | c->size = 0; 25 | } 26 | 27 | void cbuf_clear(struct cbuf *c) 28 | { 29 | c->head = 0; 30 | c->size = 0; 31 | } 32 | 33 | void cbuf_head_advance(struct cbuf *c, size_t count) 34 | { 35 | if (count > c->size) 36 | count = c->size; 37 | 38 | c->size -= count; 39 | c->head = (c->head + count) % c->capacity; 40 | } 41 | 42 | void cbuf_tail_advance(struct cbuf *c, size_t count) 43 | { 44 | const size_t a = cbuf_avail(c); 45 | 46 | if (count > a) 47 | count = a; 48 | 49 | c->size += count; 50 | } 51 | 52 | size_t cbuf_move_in(struct cbuf *c, const uint8_t *data, size_t max_size) 53 | { 54 | size_t count = 0; 55 | 56 | for (;;) { 57 | const size_t tail_size = cbuf_tail_size(c); 58 | const size_t src_size = max_size - count; 59 | const size_t x = tail_size < src_size ? tail_size : src_size; 60 | 61 | if (!x) 62 | break; 63 | 64 | memcpy(cbuf_tail_data(c), data + count, x); 65 | cbuf_tail_advance(c, x); 66 | count += x; 67 | } 68 | 69 | return count; 70 | } 71 | 72 | size_t cbuf_move_out(struct cbuf *c, uint8_t *data, size_t max_size) 73 | { 74 | size_t count = 0; 75 | 76 | for (;;) { 77 | const size_t head_size = cbuf_head_size(c); 78 | const size_t dst_size = max_size - count; 79 | const size_t x = head_size < dst_size ? head_size : dst_size; 80 | 81 | if (!x) 82 | break; 83 | 84 | memcpy(data + count, cbuf_head_data(c), x); 85 | cbuf_head_advance(c, x); 86 | count += x; 87 | } 88 | 89 | return count; 90 | } 91 | 92 | size_t cbuf_move(struct cbuf *dst, struct cbuf *src, size_t max_size) 93 | { 94 | size_t count = 0; 95 | 96 | if (!max_size) 97 | max_size = src->size; 98 | 99 | for (;;) { 100 | const size_t head_size = cbuf_head_size(src); 101 | const size_t tail_size = cbuf_tail_size(dst); 102 | const size_t limit = max_size - count; 103 | const size_t x = head_size < tail_size ? 104 | head_size : tail_size; 105 | const size_t y = x < limit ? x : limit; 106 | 107 | if (!y) 108 | break; 109 | 110 | memcpy(cbuf_tail_data(dst), cbuf_head_data(src), y); 111 | cbuf_tail_advance(dst, y); 112 | cbuf_head_advance(src, y); 113 | count += y; 114 | } 115 | 116 | return count; 117 | } 118 | -------------------------------------------------------------------------------- /src/cbuf.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef CBUF_H_ 18 | #define CBUF_H_ 19 | 20 | #include 21 | #include 22 | 23 | /* Circular buffer library. This implements a circular buffer which is 24 | * using up to (and including) the allocated capacity. 25 | * 26 | * It provides functions for returning contiguous head/tail portions, 27 | * whose positions remain stable during modifications to the other side 28 | * of the queue. 29 | * 30 | * None of the API functions will touch the actual data pointed to -- 31 | * it's safe to make copies of the cbuf structure to use for iterating 32 | * over data. 33 | */ 34 | struct cbuf { 35 | uint8_t *data; 36 | size_t capacity; 37 | size_t head; 38 | size_t size; 39 | }; 40 | 41 | /* Initialize a circular buffer. */ 42 | void cbuf_init(struct cbuf *c, uint8_t *data, size_t capacity); 43 | 44 | /* Make a shallow copy of a circular buffer, without altering the 45 | * original or the referenced data. 46 | */ 47 | static inline void cbuf_copy(struct cbuf *dst, const struct cbuf *src) 48 | { 49 | memcpy(dst, src, sizeof(*dst)); 50 | } 51 | 52 | /* Clear a circular buffer, discarding all contents. This function 53 | * should not be used if there are active references to head/tail 54 | * regions. 55 | */ 56 | void cbuf_clear(struct cbuf *c); 57 | 58 | /* Return the total amount of space used/available. */ 59 | static inline size_t cbuf_used(const struct cbuf *c) 60 | { 61 | return c->size; 62 | } 63 | 64 | static inline size_t cbuf_avail(const struct cbuf *c) 65 | { 66 | return c->capacity - c->size; 67 | } 68 | 69 | /* Return the head/tail indices */ 70 | static inline size_t cbuf_head(const struct cbuf *c) 71 | { 72 | return c->head; 73 | } 74 | 75 | static inline size_t cbuf_tail(const struct cbuf *c) 76 | { 77 | return (c->head + c->size) % c->capacity; 78 | } 79 | 80 | /* Obtain a pointer and size for a contiguous consumer area (head). */ 81 | static inline uint8_t *cbuf_head_data(const struct cbuf *c) 82 | { 83 | return c->data + c->head; 84 | } 85 | 86 | static inline size_t cbuf_head_size(const struct cbuf *c) 87 | { 88 | const size_t r = c->capacity - c->head; 89 | 90 | return r < c->size ? r : c->size; 91 | } 92 | 93 | /* Obtain a pointer and size for a contiguous producer area (tail). */ 94 | static inline uint8_t *cbuf_tail_data(const struct cbuf *c) 95 | { 96 | return c->data + cbuf_tail(c); 97 | } 98 | 99 | static inline size_t cbuf_tail_size(const struct cbuf *c) 100 | { 101 | const size_t t = cbuf_tail(c); 102 | 103 | return c->capacity - (t > c->size ? t : c->size); 104 | } 105 | 106 | /* Advance producer/consumer indices */ 107 | void cbuf_head_advance(struct cbuf *c, size_t count); 108 | void cbuf_tail_advance(struct cbuf *c, size_t count); 109 | 110 | /* Helpers for transferring data to/from flat memory areas. Each of 111 | * these functions returns the number of bytes transferred. 112 | */ 113 | size_t cbuf_move_in(struct cbuf *c, const uint8_t *data, size_t max_size); 114 | size_t cbuf_move_out(struct cbuf *c, uint8_t *data, size_t max_size); 115 | 116 | /* Transfer data between two circular buffers. If max_size is 0, the 117 | * maximum possible amount will be transferred. 118 | */ 119 | size_t cbuf_move(struct cbuf *dst, struct cbuf *src, size_t max_size); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/containers.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef CONTAINERS_H_ 18 | #define CONTAINERS_H_ 19 | 20 | /* Return the number of elements in a statically allocated array. */ 21 | #define lengthof(a) (sizeof(a) / sizeof((a)[0])) 22 | 23 | /* Find the offset of a struct's member relative to that struct. */ 24 | #ifndef offsetof 25 | #define offsetof(type, member) __builtin_offsetof(type, member) 26 | #endif 27 | 28 | /* Given a pointer (ptr) to a member of a struct, produce a pointer 29 | * to the containing struct. 30 | */ 31 | #define container_of(ptr, type, member) \ 32 | ((type *)((char *)(ptr) - offsetof(type, member))) 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "hash.h" 21 | 22 | void hash_init(struct hash *h, hash_func_t f, hash_compare_t cmp) 23 | { 24 | memset(h, 0, sizeof(*h)); 25 | h->func = f; 26 | h->compare = cmp; 27 | } 28 | 29 | void hash_destroy(struct hash *h) 30 | { 31 | if (h->table) 32 | free(h->table); 33 | 34 | memset(h, 0, sizeof(*h)); 35 | } 36 | 37 | int hash_capacity_hint(struct hash *h, unsigned int hint) 38 | { 39 | struct hash_node **new_table; 40 | unsigned int i; 41 | unsigned int new_size = 32; 42 | 43 | if (hint < h->count) 44 | return 0; 45 | 46 | while (new_size < hint) 47 | new_size <<= 1; 48 | 49 | for (;;) { 50 | int d; 51 | 52 | for (d = 2; d * d <= new_size; d++) 53 | if (!(new_size % d)) 54 | goto not_prime; 55 | 56 | break; 57 | not_prime: 58 | new_size++; 59 | } 60 | 61 | if (h->size == new_size) 62 | return 0; 63 | 64 | new_table = malloc(sizeof(new_table[0]) * new_size); 65 | if (!new_table) 66 | return -1; 67 | 68 | memset(new_table, 0, sizeof(new_table[0]) * new_size); 69 | 70 | for (i = 0; i < h->size; i++) { 71 | struct hash_node *n = h->table[i]; 72 | 73 | while (n) { 74 | struct hash_node *next = n->next; 75 | unsigned int new_index = n->code % new_size; 76 | 77 | n->next = new_table[new_index]; 78 | new_table[new_index] = n; 79 | 80 | n = next; 81 | } 82 | } 83 | 84 | if (h->table) 85 | free(h->table); 86 | 87 | h->table = new_table; 88 | h->size = new_size; 89 | 90 | return 0; 91 | } 92 | 93 | struct hash_node *hash_find(const struct hash *h, const void *key) 94 | { 95 | if (h->size) { 96 | const hash_code_t code = h->func(key); 97 | const unsigned int index = code % h->size; 98 | struct hash_node *n = h->table[index]; 99 | 100 | while (n) { 101 | if (!h->compare(key, n)) 102 | return n; 103 | 104 | n = n->next; 105 | } 106 | } 107 | 108 | return NULL; 109 | } 110 | 111 | int hash_insert(struct hash *h, const void *key, struct hash_node *ins, 112 | struct hash_node **old_ret, int flags) 113 | { 114 | unsigned int index; 115 | struct hash_node *n; 116 | struct hash_node *old = NULL; 117 | 118 | if (h->count >= h->size && 119 | hash_capacity_hint(h, h->count + 1) < 0) 120 | return -1; 121 | 122 | ins->next = NULL; 123 | 124 | if (!(flags & HASH_INSERT_PREHASHED)) 125 | ins->code = h->func(key); 126 | 127 | index = ins->code % h->size; 128 | 129 | if (flags & HASH_INSERT_UNIQUE) { 130 | ins->next = h->table[index]; 131 | h->table[index] = ins; 132 | h->count++; 133 | return 0; 134 | } 135 | 136 | n = h->table[index]; 137 | 138 | while (n) { 139 | struct hash_node *next = n->next; 140 | 141 | if (h->compare(key, n)) { 142 | n->next = ins; 143 | ins = n; 144 | } else { 145 | old = n; 146 | } 147 | 148 | n = next; 149 | } 150 | 151 | h->table[index] = ins; 152 | if (!old) 153 | h->count++; 154 | 155 | if (old_ret) 156 | *old_ret = old; 157 | 158 | return 0; 159 | } 160 | 161 | void hash_remove(struct hash *h, struct hash_node *n) 162 | { 163 | const unsigned int index = n->code % h->size; 164 | 165 | if (h->table[index] == n) { 166 | h->table[index] = n->next; 167 | h->count--; 168 | } else { 169 | struct hash_node *s = h->table[index]; 170 | 171 | while (s && s->next != n) 172 | s = s->next; 173 | 174 | if (s) { 175 | s->next = n->next; 176 | h->count--; 177 | } 178 | } 179 | 180 | if (h->count * 4 < h->size) 181 | hash_capacity_hint(h, h->count); 182 | } 183 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef HASH_H_ 18 | #define HASH_H_ 19 | 20 | /* This is a dynamically sized hash table. To implement a hash table for 21 | * a particular type, you must specify and hash and a compare function. 22 | * 23 | * The hash function should, when given a pointer to a node key, return 24 | * a value of type hash_code_t. For best results, the full range of the 25 | * type should be used. 26 | * 27 | * The comparison function should return 0 if the given key matches the 28 | * given hash_node. Otherwise, it should return non-zero. 29 | */ 30 | typedef unsigned int hash_code_t; 31 | 32 | struct hash_node { 33 | hash_code_t code; 34 | struct hash_node *next; 35 | }; 36 | 37 | typedef hash_code_t (*hash_func_t)(const void *key); 38 | typedef int (*hash_compare_t)(const void *key, const struct hash_node *n); 39 | 40 | /* A hash table should be initialized by calling hash_init with a pointer 41 | * to hashing and comparison functions. hash_destroy() must be called when 42 | * the hash table is no longer necessary to free memory used by the table 43 | * index. 44 | */ 45 | struct hash { 46 | hash_func_t func; 47 | hash_compare_t compare; 48 | unsigned int size; 49 | unsigned int count; 50 | struct hash_node **table; 51 | }; 52 | 53 | void hash_init(struct hash *h, hash_func_t f, hash_compare_t cmp); 54 | void hash_destroy(struct hash *h); 55 | 56 | /* Search for a key in a hash table. Returns a pointer to the given hash 57 | * node if found, or NULL if the key doesn't exist. 58 | */ 59 | struct hash_node *hash_find(const struct hash *h, const void *key); 60 | 61 | /* Insert a new item into a hash table. Returns 0 on success or -1 if 62 | * memory couldn't be allocated. If a node already exists with the given 63 | * key, it can be optionally returned via the "old" argument. 64 | * 65 | * Some flags can be specified to speed up the operation if additional 66 | * information is known: 67 | * 68 | * HASH_INSERT_PREHASHED: the code field is already computed 69 | * HASH_INSERT_UNIQUE: the key is known to be unique 70 | * 71 | * Note that if HASH_INSERT_UNIQUE is specified, the old argument is not 72 | * used. If both flags are specified, then the key field is not required. 73 | */ 74 | #define HASH_INSERT_PREHASHED 0x01 75 | #define HASH_INSERT_UNIQUE 0x02 76 | 77 | int hash_insert(struct hash *h, const void *key, struct hash_node *n, 78 | struct hash_node **old, int flags); 79 | 80 | /* Remove an item from a hash table. This function never fails. */ 81 | void hash_remove(struct hash *h, struct hash_node *n); 82 | 83 | /* The hash table resizes dynamically, but if you know in advance how 84 | * many items you'll need to store, you can call this function to preallocate 85 | * memory. Returns 0 on success or -1 if memory couldn't be allocated. 86 | */ 87 | int hash_capacity_hint(struct hash *h, unsigned int hint); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/istr.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "istr.h" 19 | 20 | void istr_pool_init(struct istr_pool *p) 21 | { 22 | strbuf_init(&p->text); 23 | slab_init(&p->descs, sizeof(struct istr_desc)); 24 | 25 | p->all = NULL; 26 | p->desc_count = 0; 27 | p->gc_threshold = 128; 28 | } 29 | 30 | void istr_pool_destroy(struct istr_pool *p) 31 | { 32 | strbuf_destroy(&p->text); 33 | slab_free_all(&p->descs); 34 | } 35 | 36 | istr_t istr_pool_alloc(struct istr_pool *p, const char *text, int length) 37 | { 38 | struct istr_desc *d; 39 | 40 | if (length < 0) 41 | length = strlen(text); 42 | 43 | /* Allocate a descriptor */ 44 | d = slab_alloc(&p->descs); 45 | if (!d) 46 | return NULL; 47 | 48 | d->owner = p; 49 | d->offset = strbuf_len(&p->text); 50 | d->length = length; 51 | d->refcnt = 1; 52 | 53 | /* Allocate string data */ 54 | if (strbuf_add_string(&p->text, text, length) < 0 || 55 | strbuf_add_char(&p->text, 0) < 0) { 56 | slab_free(&p->descs, d); 57 | return NULL; 58 | } 59 | 60 | /* Add the descriptor to the list */ 61 | p->desc_count++; 62 | d->next = p->all; 63 | p->all = d; 64 | 65 | /* Give the garbage collector a chance to run */ 66 | if (p->desc_count >= p->gc_threshold) { 67 | istr_pool_gc(p); 68 | 69 | p->gc_threshold = p->desc_count * 4; 70 | if (p->gc_threshold < 128) 71 | p->gc_threshold = 128; 72 | } 73 | 74 | return d; 75 | } 76 | 77 | static void gc_desc(struct istr_pool *p) 78 | { 79 | struct istr_desc *new_list = NULL; 80 | 81 | /* Filter the descriptor list, throwing away those with no 82 | * references. 83 | */ 84 | while (p->all) { 85 | struct istr_desc *d = p->all; 86 | 87 | p->all = d->next; 88 | 89 | if (d->refcnt) { 90 | d->next = new_list; 91 | new_list = d; 92 | } else { 93 | slab_free(&p->descs, d); 94 | } 95 | } 96 | 97 | /* Reverse and count the filtered list */ 98 | p->desc_count = 0; 99 | while (new_list) { 100 | struct istr_desc *d = new_list; 101 | 102 | new_list = d->next; 103 | 104 | d->next = p->all; 105 | p->all = d; 106 | 107 | p->desc_count++; 108 | } 109 | } 110 | 111 | static int gc_text(struct istr_pool *p) 112 | { 113 | struct istr_desc *d; 114 | struct strbuf new_buf; 115 | 116 | strbuf_init(&new_buf); 117 | 118 | if (strbuf_capacity_hint(&new_buf, p->text.length) < 0) { 119 | strbuf_destroy(&new_buf); 120 | return -1; 121 | } 122 | 123 | for (d = p->all; d; d = d->next) { 124 | int new_offset = new_buf.length; 125 | 126 | strbuf_add_string(&new_buf, istr_text(d), 127 | d->length + 1); 128 | d->offset = new_offset; 129 | } 130 | 131 | strbuf_capacity_hint(&new_buf, new_buf.length); 132 | strbuf_destroy(&p->text); 133 | memcpy(&p->text, &new_buf, sizeof(p->text)); 134 | 135 | return 0; 136 | } 137 | 138 | int istr_pool_gc(struct istr_pool *p) 139 | { 140 | gc_desc(p); 141 | 142 | return gc_text(p); 143 | } 144 | 145 | int istr_equal(istr_t a, istr_t b) 146 | { 147 | if (a->length != b->length) 148 | return 0; 149 | 150 | return !memcmp(istr_text(a), istr_text(b), a->length); 151 | } 152 | 153 | int istr_compare(istr_t a, istr_t b) 154 | { 155 | const char *ta = istr_text(a); 156 | const char *tb = istr_text(b); 157 | const int la = istr_length(a); 158 | const int lb = istr_length(b); 159 | 160 | if (ta == tb) 161 | return 0; 162 | 163 | if (la < lb) { 164 | int r = memcmp(ta, tb, la); 165 | 166 | if (!r) 167 | return -1; 168 | 169 | return r; 170 | } 171 | 172 | if (la > lb) { 173 | int r = memcmp(ta, tb, lb); 174 | 175 | if (!r) 176 | return 1; 177 | 178 | return r; 179 | } 180 | 181 | return memcmp(ta, tb, la); 182 | } 183 | -------------------------------------------------------------------------------- /src/istr.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef ISTR_H_ 18 | #define ISTR_H_ 19 | 20 | #include "strbuf.h" 21 | #include "slab.h" 22 | 23 | /* This structure is an immutable reference-counted string pool. It allows 24 | * for reduced memory usage when dealing with large numbers of strings. 25 | */ 26 | struct istr_pool { 27 | struct strbuf text; 28 | struct slab descs; 29 | 30 | struct istr_desc *all; 31 | 32 | unsigned int desc_count; 33 | unsigned int gc_threshold; 34 | }; 35 | 36 | struct istr_desc { 37 | struct istr_desc *next; 38 | struct istr_pool *owner; 39 | size_t offset; 40 | size_t length; 41 | unsigned int refcnt; 42 | }; 43 | 44 | /* This is the type of immutable string references. */ 45 | typedef const struct istr_desc *istr_t; 46 | 47 | /* Initialize and destroy a string pool. */ 48 | void istr_pool_init(struct istr_pool *p); 49 | void istr_pool_destroy(struct istr_pool *p); 50 | 51 | /* Allocate a string from the pool. Strings may have an explicit length, 52 | * in which case they can contain embedded nuls. Otherwise, if the length 53 | * given is negative, it will be calculated automatically with strlen(). 54 | * 55 | * This function returns a reference to an immutable string on success, or 56 | * NULL if memory allocation failed. istr_unref() should be called to 57 | * release the allocated string when it's no longer needed by the caller. 58 | */ 59 | istr_t istr_pool_alloc(struct istr_pool *p, const char *text, int length); 60 | 61 | /* Garbage-collect the pool. This is done automatically when necessary on 62 | * allocated, but can also be performed manually. 63 | * 64 | * Returns 0 on success, or -1 if memory couldn't be allocated (not a fatal 65 | * error). 66 | */ 67 | int istr_pool_gc(struct istr_pool *p); 68 | 69 | /* Increment and decrement string reference counts. */ 70 | static inline void istr_ref(istr_t s) 71 | { 72 | ((struct istr_desc *)s)->refcnt++; 73 | } 74 | 75 | static inline void istr_unref(istr_t s) 76 | { 77 | ((struct istr_desc *)s)->refcnt--; 78 | } 79 | 80 | /* Compare two strings for equality. This can be more efficient than 81 | * istr_compare, because it first tests for length equality. 82 | */ 83 | int istr_equal(istr_t a, istr_t b); 84 | 85 | /* Compare two strings, returning negative/zero/positive. */ 86 | int istr_compare(istr_t a, istr_t b); 87 | 88 | /* Obtain length and text from an immutable string reference. Note that the 89 | * text pointer returned is valid only up to the next pool allocation or 90 | * garbage collection. 91 | */ 92 | static inline const char *istr_text(istr_t s) 93 | { 94 | return strbuf_text(&s->owner->text) + s->offset; 95 | } 96 | 97 | static inline unsigned int istr_length(istr_t s) 98 | { 99 | return s->length; 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "list.h" 19 | 20 | void list_init(struct list_node *head) 21 | { 22 | head->next = head; 23 | head->prev = head; 24 | } 25 | 26 | void list_insert(struct list_node *item, struct list_node *after) 27 | { 28 | item->next = after; 29 | item->prev = after->prev; 30 | 31 | after->prev->next = item; 32 | after->prev = item; 33 | } 34 | 35 | void list_remove(struct list_node *item) 36 | { 37 | item->next->prev = item->prev; 38 | item->prev->next = item->next; 39 | 40 | item->prev = NULL; 41 | item->next = NULL; 42 | } 43 | 44 | void list_move(struct list_node *dst, struct list_node *src) 45 | { 46 | if (list_is_empty(src)) { 47 | dst->next = dst->prev = dst; 48 | } else { 49 | dst->next = src->next; 50 | dst->prev = src->prev; 51 | 52 | dst->next->prev = dst; 53 | dst->prev->next = dst; 54 | 55 | src->next = src->prev = src; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef LIST_H_ 18 | #define LIST_H_ 19 | 20 | /* This is the list node definition. It's intended to be embedded within 21 | * another data structure. 22 | * 23 | * All lists are circular and doubly-linked. So, to iterate over the 24 | * members of a list, do something like this: 25 | * 26 | * struct list_node *n; 27 | * 28 | * for (n = list->next; n != list; n = n->next) { 29 | * ... 30 | * } 31 | * 32 | * An empty list must be created first with list_init(). This sets the 33 | * next and prev pointers to the list head itself. 34 | */ 35 | struct list_node { 36 | struct list_node *next; 37 | struct list_node *prev; 38 | }; 39 | 40 | /* Create an empty list */ 41 | void list_init(struct list_node *head); 42 | 43 | /* Check to see if a list contains anything. */ 44 | static inline int list_is_empty(const struct list_node *lst) 45 | { 46 | return lst->next == lst; 47 | } 48 | 49 | /* Add an item to a list. The item will appear before the item 50 | * specified as after. 51 | */ 52 | void list_insert(struct list_node *item, struct list_node *after); 53 | 54 | /* Remove a node from its containing list. */ 55 | void list_remove(struct list_node *item); 56 | 57 | /* Move the contents of one list to another in constant time. */ 58 | void list_move(struct list_node *dst, struct list_node *src); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/protothread.h: -------------------------------------------------------------------------------- 1 | /* Simple protothread implementation 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PROTOTHREAD_H_ 18 | #define PROTOTHREAD_H_ 19 | 20 | /* Protothreads are a scheme for describing state machines using the 21 | * higher-level constructs provided by C. They by using a feature of the 22 | * switch statement which allows case labels to be placed in sub-scopes. 23 | * 24 | * This idea is described in detail by Simon Tatham here: 25 | * 26 | * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 27 | * 28 | * This particular implementation is slightly different in that it makes 29 | * the context variable explicit, which suits embedded systems and other 30 | * environments where you might not want to dynamically allocate memory. 31 | * 32 | * To use this system, you must declare a protothread_state_t, 33 | * initialized to PROTOTHREAD_INIT (== 0). The protothread body must be 34 | * enclosed in some function and wrapped between the BEGIN/END macros. 35 | * When control enters the protothread body, execution resumes at the 36 | * position of the last YIELD (or at the start, if the state is 0). 37 | * 38 | * Issuing a YIELD causes the function to return. On the next call, the 39 | * protothread resumes execution immediately following the YIELD 40 | * statement. 41 | * 42 | * Limitations: 43 | * 44 | * - you can't have two YIELD statements on the same line 45 | * - you can't have a YIELD within a switch statement within a 46 | * protothread body 47 | * - lexically scoped variables don't retain their state during a 48 | * YIELD 49 | */ 50 | 51 | /* This is the type of the variable which is used to store protothread 52 | * state for suspend and resume. If your thread is single instance, you 53 | * might declare a static variable in the protothread function. 54 | * Otherwise, this might be a member of a context structure. 55 | */ 56 | typedef int protothread_state_t; 57 | 58 | /* Initial value for protothread state */ 59 | #define PROTOTHREAD_INIT ((protothread_state_t)0) 60 | 61 | /* The protothread body should be wrapped with this pair of macros. When 62 | * the protothread body is entered, execution resumes at the point where 63 | * the thread last yielded. 64 | */ 65 | #define PROTOTHREAD_BEGIN(state) switch (state) { case PROTOTHREAD_INIT: 66 | #define PROTOTHREAD_END } 67 | 68 | /* Suspend the protothread and return. When the protothread body is next 69 | * entered, execution resumes immediately following the YIELD statement. 70 | */ 71 | #define PROTOTHREAD_YIELD(state, ...) \ 72 | do { state = __LINE__; return __VA_ARGS__; case __LINE__:; } while (0); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/rbt.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef RBT_H_ 18 | #define RBT_H_ 19 | 20 | #include 21 | 22 | /* This is the base of a tree node. It should be embedded within a larger 23 | * data structure containing a key and optionally some data. 24 | * 25 | * The RBT_FLAG_MODIFIED field is set whenever the set comprising the 26 | * node's descendants has changed. 27 | */ 28 | #define RBT_FLAG_RED 0x01 29 | #define RBT_FLAG_MODIFIED 0x02 30 | 31 | #define RBT_IS_RED(n) ((n) && (n)->flags & RBT_FLAG_RED) 32 | #define RBT_IS_BLACK(n) (!(n) || !((n)->flags & RBT_FLAG_RED)) 33 | 34 | struct rbt_node { 35 | int flags; 36 | struct rbt_node *left; 37 | struct rbt_node *right; 38 | struct rbt_node *parent; 39 | }; 40 | 41 | /* This function should compare the key contained within a tree node to 42 | * the new key given by a. 43 | */ 44 | typedef int (*rbt_compare_t)(const void *a, const struct rbt_node *b); 45 | 46 | /* A tree should be initialized by filling in the compare member and 47 | * setting root to NULL. 48 | */ 49 | struct rbt { 50 | rbt_compare_t compare; 51 | struct rbt_node *root; 52 | }; 53 | 54 | static inline void rbt_init(struct rbt *t, rbt_compare_t cmp) 55 | { 56 | t->root = NULL; 57 | t->compare = cmp; 58 | } 59 | 60 | struct rbt_node *rbt_find(const struct rbt *t, const void *key); 61 | 62 | /* Insert a node into a tree. If there was already a node with the 63 | * given key, the old node is returned. 64 | */ 65 | struct rbt_node *rbt_insert(struct rbt *t, const void *key, 66 | struct rbt_node *n); 67 | 68 | /* Remove a node from a tree. If a matching node was found, it's 69 | * returned after being removed. 70 | */ 71 | void rbt_remove(struct rbt *t, struct rbt_node *n); 72 | 73 | /* Mark a node and its ancestors as modified. */ 74 | void rbt_mark_modified(struct rbt_node *n); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/rbt_iter.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "rbt_iter.h" 19 | 20 | struct rbt_node *rbt_iter_next(struct rbt_node *n) 21 | { 22 | if (n->right) { 23 | n = n->right; 24 | while (n->left) 25 | n = n->left; 26 | 27 | return n; 28 | } 29 | 30 | while (n->parent && n->parent->right == n) 31 | n = n->parent; 32 | 33 | return n->parent; 34 | } 35 | 36 | struct rbt_node *rbt_iter_prev(struct rbt_node *n) 37 | { 38 | if (n->left) { 39 | n = n->left; 40 | while (n->right) 41 | n = n->right; 42 | 43 | return n; 44 | } 45 | 46 | while (n->parent && n->parent->left == n) 47 | n = n->parent; 48 | 49 | return n->parent; 50 | } 51 | 52 | struct rbt_node *rbt_iter_first(struct rbt *t) 53 | { 54 | struct rbt_node *n = t->root; 55 | 56 | while (n && n->left) 57 | n = n->left; 58 | 59 | return n; 60 | } 61 | 62 | struct rbt_node *rbt_iter_last(struct rbt *t) 63 | { 64 | struct rbt_node *n = t->root; 65 | 66 | while (n && n->right) 67 | n = n->right; 68 | 69 | return n; 70 | } 71 | 72 | struct rbt_node *rbt_iter_le(struct rbt *t, const void *key) 73 | { 74 | struct rbt_node *n = t->root; 75 | struct rbt_node *m = NULL; 76 | 77 | while (n) { 78 | int r = t->compare(key, n); 79 | 80 | if (!r) 81 | return n; 82 | 83 | if (r < 0) { 84 | n = n->left; 85 | } else { 86 | m = n; 87 | n = n->right; 88 | } 89 | } 90 | 91 | return m; 92 | } 93 | 94 | struct rbt_node *rbt_iter_ge(struct rbt *t, const void *key) 95 | { 96 | struct rbt_node *n = t->root; 97 | struct rbt_node *m = NULL; 98 | 99 | while (n) { 100 | int r = t->compare(key, n); 101 | 102 | if (!r) 103 | return n; 104 | 105 | if (r < 0) { 106 | m = n; 107 | n = n->left; 108 | } else { 109 | n = n->right; 110 | } 111 | } 112 | 113 | return m; 114 | } 115 | 116 | struct rbt_node *rbt_iter_lt(struct rbt *t, const void *key) 117 | { 118 | struct rbt_node *n = t->root; 119 | struct rbt_node *m = NULL; 120 | 121 | while (n) { 122 | int r = t->compare(key, n); 123 | 124 | if (r <= 0) { 125 | n = n->left; 126 | } else { 127 | m = n; 128 | n = n->right; 129 | } 130 | } 131 | 132 | return m; 133 | } 134 | 135 | struct rbt_node *rbt_iter_gt(struct rbt *t, const void *key) 136 | { 137 | struct rbt_node *n = t->root; 138 | struct rbt_node *m = NULL; 139 | 140 | while (n) { 141 | int r = t->compare(key, n); 142 | 143 | if (r < 0) { 144 | m = n; 145 | n = n->left; 146 | } else { 147 | n = n->right; 148 | } 149 | } 150 | 151 | return m; 152 | } 153 | -------------------------------------------------------------------------------- /src/rbt_iter.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef RBT_ITER_H_ 18 | #define RBT_ITER_H_ 19 | 20 | #include "rbt.h" 21 | 22 | /* Given a node within a red-black tree, find the in-order successor and 23 | * predecessor. 24 | */ 25 | struct rbt_node *rbt_iter_next(struct rbt_node *n); 26 | struct rbt_node *rbt_iter_prev(struct rbt_node *n); 27 | 28 | /* Retrieve the lexicographically smallest and largest nodes in a tree. */ 29 | struct rbt_node *rbt_iter_first(struct rbt *t); 30 | struct rbt_node *rbt_iter_last(struct rbt *t); 31 | 32 | /* Find nodes which are: 33 | * - le: the largest node not greater than the given key 34 | * - ge: the smallest node not less than the given key 35 | * - lt: the largest node less than the given key 36 | * - gt: the smallest node greater than the given key 37 | */ 38 | struct rbt_node *rbt_iter_le(struct rbt *t, const void *key); 39 | struct rbt_node *rbt_iter_ge(struct rbt *t, const void *key); 40 | struct rbt_node *rbt_iter_lt(struct rbt *t, const void *key); 41 | struct rbt_node *rbt_iter_gt(struct rbt *t, const void *key); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/rbt_range.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "rbt_range.h" 18 | 19 | static void query_low(struct rbt_node *n, void *query_data, 20 | rbt_range_cmp_t cmp, rbt_range_add_t cb) 21 | { 22 | while (n) { 23 | int r = cmp(query_data, n); 24 | 25 | if (r < 0) { 26 | n = n->right; 27 | } else { 28 | query_low(n->left, query_data, cmp, cb); 29 | cb(query_data, n, RBT_RANGE_ADD_NODE); 30 | if (n->right) 31 | cb(query_data, n->right, RBT_RANGE_ADD_TREE); 32 | break; 33 | } 34 | } 35 | } 36 | 37 | static void query_high(struct rbt_node *n, void *query_data, 38 | rbt_range_cmp_t cmp, rbt_range_add_t cb) 39 | { 40 | while (n) { 41 | int r = cmp(query_data, n); 42 | 43 | if (r > 0) { 44 | n = n->left; 45 | } else { 46 | if (n->left) 47 | cb(query_data, n->left, RBT_RANGE_ADD_TREE); 48 | cb(query_data, n, RBT_RANGE_ADD_NODE); 49 | query_high(n->right, query_data, cmp, cb); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | static void query_over(struct rbt_node *n, void *query_data, 56 | rbt_range_cmp_t cmp, rbt_range_add_t cb) 57 | { 58 | while (n) { 59 | int r = cmp(query_data, n); 60 | 61 | if (r < 0) { 62 | n = n->right; 63 | } else if (r > 0) { 64 | n = n->left; 65 | } else { 66 | query_low(n->left, query_data, cmp, cb); 67 | cb(query_data, n, RBT_RANGE_ADD_NODE); 68 | query_high(n->right, query_data, cmp, cb); 69 | break; 70 | } 71 | } 72 | } 73 | 74 | void rbt_range_query(struct rbt *tree, void *query_data, 75 | rbt_range_cmp_t cmp, rbt_range_add_t cb) 76 | { 77 | query_over(tree->root, query_data, cmp, cb); 78 | } 79 | -------------------------------------------------------------------------------- /src/rbt_range.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef RBT_RANGE_H_ 18 | #define RBT_RANGE_H_ 19 | 20 | /* These functions can be used to implement efficient range queries on 21 | * augmented RBTs. 22 | * 23 | * When a range query is performed, a callback is invoked for a O(log n) 24 | * nodes and subtrees which are contained in the range. The callback is 25 | * responsible for collecting the necessary data and compiling the result. 26 | */ 27 | 28 | #include "rbt.h" 29 | 30 | /* This is the type of range selection functions. The function, given a 31 | * node, should test the key to see whether its inside the required range. 32 | * 33 | * It should return 0 if the key is inside, -1 if it's before the range 34 | * and 1 if it's after. The range must be contiguous (but it may be 35 | * open-ended). 36 | */ 37 | typedef int (*rbt_range_cmp_t)(void *query_data, const struct rbt_node *n); 38 | 39 | /* This callback is invoked for each node or subtree which is inside the 40 | * range. The rbt_range_addtype_t parameter specifies whether it's the 41 | * node or the entire subtree which lies inside the range. 42 | * 43 | * To implement an efficient range query, the callback should do the 44 | * following: 45 | * 46 | * if type == RBT_RANGE_ADD_NODE, add data from only the node itself 47 | * to the query result. 48 | * if type == RBT_RANGE_ADD_TREE, call update_subtree() to compile 49 | * summary data for the subtree, and then add the summary data 50 | * to the query result. 51 | * 52 | * update_subtree() should be a recursive function which does the following: 53 | * 54 | * - if the node doesn't have RBT_FLAG_MODIFIED set, then return 55 | * - if the node is a non-leaf, call update_subtree() on the left and 56 | * right subtrees. 57 | * - add the summary from the left subtree to the summary for this node 58 | * - add the node's own data to the summary 59 | * - add the summary from the right subtree to the summary for this node 60 | * - clear RBT_FLAG_MODIFIED 61 | * 62 | * The trees and nodes presented to the callback function will be given 63 | * in-order. 64 | */ 65 | typedef enum { 66 | RBT_RANGE_ADD_NODE, 67 | RBT_RANGE_ADD_TREE 68 | } rbt_range_addtype_t; 69 | 70 | typedef void (*rbt_range_add_t)(void *query_data, struct rbt_node *n, 71 | rbt_range_addtype_t type); 72 | 73 | /* This function performs the actual range query. The given query_data 74 | * argument will be passed to both the comparison function and the result 75 | * compilation callback. 76 | */ 77 | void rbt_range_query(struct rbt *tree, void *query_data, 78 | rbt_range_cmp_t cmp, rbt_range_add_t cb); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/slab.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef SLAB_H_ 18 | #define SLAB_H_ 19 | 20 | #include "list.h" 21 | 22 | struct slab { 23 | int objsize; 24 | int slabsize; 25 | int count; 26 | int info_offset; 27 | 28 | struct list_node full; 29 | struct list_node partial; 30 | }; 31 | 32 | /* Set up a slab allocator */ 33 | void slab_init(struct slab *s, int objsize); 34 | 35 | /* Free all objects */ 36 | void slab_free_all(struct slab *s); 37 | 38 | /* Allocate an object from the slab cache */ 39 | void *slab_alloc(struct slab *s); 40 | 41 | /* Return an object to the slab cache */ 42 | void slab_free(struct slab *s, void *obj); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/slist.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "slist.h" 18 | 19 | void slist_init(struct slist *s) 20 | { 21 | s->start = NULL; 22 | s->end = NULL; 23 | } 24 | 25 | void slist_push(struct slist *s, struct slist_node *n) 26 | { 27 | n->next = s->start; 28 | 29 | s->start = n; 30 | if (!s->end) 31 | s->end = n; 32 | } 33 | 34 | struct slist_node *slist_pop(struct slist *s) 35 | { 36 | struct slist_node *r = s->start; 37 | 38 | if (!r) 39 | return NULL; 40 | 41 | s->start = r->next; 42 | if (!s->start) 43 | s->end = NULL; 44 | 45 | return r; 46 | } 47 | 48 | void slist_append(struct slist *s, struct slist_node *n) 49 | { 50 | n->next = NULL; 51 | 52 | if (s->end) 53 | s->end->next = n; 54 | else 55 | s->start = n; 56 | 57 | s->end = n; 58 | } 59 | -------------------------------------------------------------------------------- /src/slist.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef SLIST_H_ 18 | #define SLIST_H_ 19 | 20 | #include 21 | 22 | /* This data structure is a singly-linked list. It allows efficient 23 | * insertion at both ends, and efficient removal from the beginning of 24 | * the list. 25 | */ 26 | struct slist_node { 27 | struct slist_node *next; 28 | }; 29 | 30 | struct slist { 31 | struct slist_node *start; 32 | struct slist_node *end; 33 | }; 34 | 35 | /* Check to see if a list contains anything. */ 36 | static inline int slist_is_empty(const struct slist *s) 37 | { 38 | return !s->start; 39 | } 40 | 41 | /* Create an empty list */ 42 | void slist_init(struct slist *s); 43 | 44 | /* Add an item to the beginning of a list */ 45 | void slist_push(struct slist *s, struct slist_node *n); 46 | 47 | /* Remove the first item from the beginning of a list */ 48 | struct slist_node *slist_pop(struct slist *s); 49 | 50 | /* Add an item to the end of a list. */ 51 | void slist_append(struct slist *s, struct slist_node *n); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/strbuf.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "strbuf.h" 22 | 23 | static const char strbuf_blank[1] = {0}; 24 | 25 | void strbuf_init(struct strbuf *buf) 26 | { 27 | memset(buf, 0, sizeof(*buf)); 28 | buf->text = (char *)strbuf_blank; 29 | } 30 | 31 | void strbuf_destroy(struct strbuf *buf) 32 | { 33 | if (buf->text != strbuf_blank) 34 | free(buf->text); 35 | 36 | memset(buf, 0, sizeof(*buf)); 37 | } 38 | 39 | void strbuf_clear(struct strbuf *buf) 40 | { 41 | if (buf->text != strbuf_blank) 42 | free(buf->text); 43 | 44 | memset(buf, 0, sizeof(*buf)); 45 | buf->text = (char *)strbuf_blank; 46 | } 47 | 48 | int strbuf_resize(struct strbuf *buf, size_t new_length) 49 | { 50 | if (new_length + 1 > buf->capacity && 51 | strbuf_capacity_hint(buf, new_length) < 0) { 52 | buf->failed = 1; 53 | return -1; 54 | } 55 | 56 | buf->length = new_length; 57 | buf->text[buf->length] = 0; 58 | 59 | if ((new_length + 1) * 4 < buf->capacity) 60 | strbuf_capacity_hint(buf, new_length + 1); 61 | 62 | return 0; 63 | } 64 | 65 | int strbuf_capacity_hint(struct strbuf *buf, size_t length) 66 | { 67 | size_t new_cap = 32; 68 | char *new_text; 69 | 70 | if (length < buf->length) 71 | return 0; 72 | 73 | while (new_cap < length + 1) 74 | new_cap <<= 1; 75 | 76 | if (new_cap == buf->capacity) 77 | return 0; 78 | 79 | if (buf->text == strbuf_blank) 80 | new_text = malloc(new_cap); 81 | else 82 | new_text = realloc(buf->text, new_cap); 83 | 84 | if (!new_text) 85 | return -1; 86 | 87 | buf->text = new_text; 88 | buf->capacity = new_cap; 89 | 90 | return 0; 91 | } 92 | 93 | int strbuf_add_char(struct strbuf *buf, int c) 94 | { 95 | char ch = c; 96 | 97 | return strbuf_add_string(buf, &ch, 1); 98 | } 99 | 100 | int strbuf_add_string(struct strbuf *buf, const char *text, int length) 101 | { 102 | if (length < 0) 103 | length = strlen(text); 104 | 105 | if (strbuf_capacity_hint(buf, buf->length + length) < 0) 106 | return -1; 107 | 108 | memcpy(buf->text + buf->length, text, length); 109 | buf->length += length; 110 | buf->text[buf->length] = 0; 111 | 112 | return length; 113 | } 114 | 115 | int strbuf_vprintf(struct strbuf *buf, const char *fmt, va_list ap) 116 | { 117 | if (strbuf_capacity_hint(buf, 1) < 0) 118 | return -1; 119 | 120 | for (;;) { 121 | va_list map; 122 | int room = buf->capacity - buf->length; 123 | int r; 124 | 125 | va_copy(map, ap); 126 | r = vsnprintf(buf->text + buf->length, room, fmt, map); 127 | va_end(map); 128 | 129 | if (r + 1 < room) { 130 | buf->length += r; 131 | return r; 132 | } 133 | 134 | if (strbuf_capacity_hint(buf, buf->capacity + 1) < 0) { 135 | buf->failed = 1; 136 | return -1; 137 | } 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | int strbuf_printf(struct strbuf *buf, const char *fmt, ...) 144 | { 145 | va_list ap; 146 | int r; 147 | 148 | va_start(ap, fmt); 149 | r = strbuf_vprintf(buf, fmt, ap); 150 | va_end(ap); 151 | 152 | return r; 153 | } 154 | -------------------------------------------------------------------------------- /src/strbuf.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef STRBUF_H_ 18 | #define STRBUF_H_ 19 | 20 | #include 21 | #include 22 | 23 | /* This structure is a resizable text buffer. It allows easy construction 24 | * and editing of dynamically sized strings. Strings kept in the buffer, 25 | * are always nul terminated (including those of zero length). 26 | * 27 | * Errors are reported in two ways: the failing function with return an 28 | * error code, and the buffer's failed flag is set. This allows easy error 29 | * handling when constructing long strings with many calls. 30 | * 31 | * The user is allowed to modify the string directly, but only the first 32 | * ``length`` bytes of the text. 33 | */ 34 | struct strbuf { 35 | char *text; 36 | size_t length; 37 | size_t capacity; 38 | int failed; 39 | }; 40 | 41 | /* Initialize and destroy the string buffer. Upon initialization, the 42 | * string is empty and text points to a constant zero-length C string. 43 | */ 44 | void strbuf_init(struct strbuf *buf); 45 | void strbuf_destroy(struct strbuf *buf); 46 | 47 | /* Inlines for obtaining length, text */ 48 | static inline const char *strbuf_text(const struct strbuf *buf) 49 | { 50 | return buf->text; 51 | } 52 | 53 | static inline size_t strbuf_len(const struct strbuf *buf) 54 | { 55 | return buf->length; 56 | } 57 | 58 | /* Clear a string, and reset the failed flag. */ 59 | void strbuf_clear(struct strbuf *buf); 60 | 61 | /* Resize the string. If the new size is smaller than the old size, 62 | * then the string is truncated. If the new size is larger, the extra 63 | * characters are uninitialized (but a nul terminator is added at 64 | * the correct offset). 65 | * 66 | * Returns 0 on success or -1 if memory couldn't be allocated (and 67 | * the failed flag will be set in this case). 68 | */ 69 | int strbuf_resize(struct strbuf *buf, size_t new_length); 70 | 71 | /* This function may be called to preallocate memory. If successful, 72 | * 0 is returned and further calls to increase the string size or 73 | * append data are guaranteed to succeed, provided they don't overrun 74 | * the preallocation. 75 | * 76 | * If the function fails, -1 is returned (but the failed flag is _not_ 77 | * set). 78 | */ 79 | int strbuf_capacity_hint(struct strbuf *buf, size_t length); 80 | 81 | /* Append characters and strings to the buffer. For strings containing 82 | * embedded nuls, a length may be explicitly specified. Otherwise, if 83 | * this length is -1, it will be calculated using strlen(). 84 | * 85 | * On success, add_char will return 0, and add_string will return the 86 | * number of characters added. On failure, both functions return -1 and 87 | * set the failed flag. 88 | */ 89 | int strbuf_add_char(struct strbuf *buf, int c); 90 | int strbuf_add_string(struct strbuf *buf, const char *text, int length); 91 | 92 | /* Add text to the buffer with a format string. The buffer will be 93 | * automatically resized as necessary to accommodate the text. 94 | * 95 | * Returns the number of characters added on success, or -1 on failure 96 | * (in which case the failed flag is also set). 97 | */ 98 | int strbuf_printf(struct strbuf *buf, const char *fmt, ...) 99 | __attribute__ ((format (printf, 2, 3))); 100 | 101 | int strbuf_vprintf(struct strbuf *buf, const char *fmt, va_list ap); 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* strlcpy - safe string copy 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "strlcpy.h" 19 | 20 | size_t strlcpy(char *dst, const char *src, size_t size) 21 | { 22 | const size_t src_len = strlen(src); 23 | const size_t max_len = size - 1; 24 | const size_t len = src_len > max_len ? max_len : src_len; 25 | 26 | if (!size) 27 | return 0; 28 | 29 | memcpy(dst, src, len); 30 | dst[len] = 0; 31 | 32 | return src_len; 33 | } 34 | 35 | size_t strlcat(char *dst, const char *src, size_t size) 36 | { 37 | const size_t dst_len = strlen(dst); 38 | const size_t src_len = strlen(src); 39 | const size_t res_len = dst_len + src_len; 40 | const size_t max_len = size - dst_len - 1; 41 | const size_t copy = src_len > max_len ? max_len : src_len; 42 | 43 | if (!size || (size - 1) < dst_len) 44 | return 0; 45 | 46 | memcpy(dst + dst_len, src, copy); 47 | dst[dst_len + copy] = 0; 48 | 49 | return res_len; 50 | } 51 | -------------------------------------------------------------------------------- /src/strlcpy.h: -------------------------------------------------------------------------------- 1 | /* strlcpy - safe string copy 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef STRLCPY_H_ 18 | #define STRLCPY_H_ 19 | 20 | #include 21 | 22 | /* As per OpenBSD man page */ 23 | size_t strlcpy(char *dst, const char *src, size_t size); 24 | size_t strlcat(char *dst, const char *src, size_t size); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/vector.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "vector.h" 20 | 21 | void vector_init(struct vector *v, unsigned int elemsize) 22 | { 23 | memset(v, 0, sizeof(*v)); 24 | v->elemsize = elemsize; 25 | } 26 | 27 | void vector_clear(struct vector *v) 28 | { 29 | if (v->ptr) 30 | free(v->ptr); 31 | 32 | v->ptr = NULL; 33 | v->capacity = 0; 34 | v->size = 0; 35 | } 36 | 37 | void vector_destroy(struct vector *v) 38 | { 39 | vector_clear(v); 40 | memset(v, 0, sizeof(*v)); 41 | } 42 | 43 | int size_for(struct vector *v, unsigned int needed) 44 | { 45 | int cap = needed; 46 | void *new_ptr; 47 | 48 | /* Find the smallest power of 2 which is greater than the 49 | * necessary capacity. 50 | */ 51 | while (cap & (cap - 1)) 52 | cap &= (cap - 1); 53 | if (cap < needed) 54 | cap <<= 1; 55 | 56 | /* Don't allocate fewer than 8 elements */ 57 | if (cap < 8) 58 | cap = 8; 59 | 60 | /* If we're already big enough, don't reallocate. 61 | * 62 | * Similarly, don't reallocate to a smaller size unless we're 63 | * using far too much more space than is necessary. 64 | */ 65 | if (v->capacity >= cap && v->capacity <= cap * 2) 66 | return 0; 67 | 68 | new_ptr = realloc(v->ptr, cap * v->elemsize); 69 | if (!new_ptr) { 70 | if (v->capacity >= needed) 71 | return 0; 72 | return -1; 73 | } 74 | 75 | v->ptr = new_ptr; 76 | v->capacity = cap; 77 | 78 | return 0; 79 | } 80 | 81 | int vector_resize(struct vector *v, unsigned int new_size) 82 | { 83 | if (size_for(v, new_size) < 0) 84 | return -1; 85 | 86 | v->size = new_size; 87 | return 0; 88 | } 89 | 90 | int vector_push(struct vector *v, const void *data, unsigned int count) 91 | { 92 | int needed = v->size + count; 93 | 94 | if (size_for(v, needed) < 0) 95 | return -1; 96 | 97 | memcpy((char *)v->ptr + v->size * v->elemsize, 98 | data, 99 | count * v->elemsize); 100 | v->size += count; 101 | 102 | return 0; 103 | } 104 | 105 | void vector_pop(struct vector *v, unsigned int count) 106 | { 107 | if (count > v->size) 108 | count = v->size; 109 | 110 | vector_resize(v, v->size - count); 111 | } 112 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef VECTOR_H_ 18 | #define VECTOR_H_ 19 | 20 | /* A vector is a flexible array type. It can be used to hold elements of 21 | * any type. 22 | * 23 | * elemsize: size in bytes of each element 24 | * capacity: maximum number of elements that ptr can hold 25 | * size: number of elements currently held 26 | */ 27 | struct vector { 28 | void *ptr; 29 | unsigned int elemsize; 30 | unsigned int capacity; 31 | unsigned int size; 32 | }; 33 | 34 | /* Create and destroy vectors */ 35 | void vector_init(struct vector *v, unsigned int elemsize); 36 | void vector_destroy(struct vector *v); 37 | 38 | /* Remove all elements */ 39 | void vector_clear(struct vector *v); 40 | 41 | /* Alter the size of a vector. If the new size is greater than the 42 | * current size, the new elements will be uninitialized. 43 | * 44 | * Returns 0 on success or -1 if memory could not be allocated. 45 | */ 46 | int vector_resize(struct vector *v, unsigned int new_size); 47 | 48 | /* Append any number of elements to the end of a vector, reallocating if 49 | * necessary. Returns 0 on success or -1 if memory could not be allocated. 50 | */ 51 | int vector_push(struct vector *v, const void *data, unsigned int count); 52 | 53 | /* Remove the last element from a vector. */ 54 | void vector_pop(struct vector *v, unsigned int count); 55 | 56 | /* Dereference a vector, giving an expression for the element of type t at 57 | * position i in vector v. Use as follows: 58 | * 59 | * struct vector v; 60 | * 61 | * VECTOR_AT(v, 3, int) = 57; 62 | * *VECTOR_PTR(v, 3, int) = 57; 63 | */ 64 | #define VECTOR_AT(v, i, t) (*VECTOR_PTR(v, i, t)) 65 | #define VECTOR_PTR(v, i, t) \ 66 | ((t *)((char *)(v).ptr + (i) * (v).elemsize)) 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /tests/prng.h: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PRNG_H_ 18 | #define PRNG_H_ 19 | 20 | #include 21 | 22 | /* Quick-and-dirty PRNG for testing. */ 23 | typedef uint32_t prng_t; 24 | 25 | static inline void prng_init(prng_t *p, uint32_t seed) 26 | { 27 | *p = seed ? seed : 1; 28 | } 29 | 30 | static inline uint32_t prng_next(prng_t *p) 31 | { 32 | *p = (((uint64_t)(*p)) * 742938285LL) % 0xffffffff; 33 | return *p; 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /tests/test_adns.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "adns.h" 20 | #include "net.h" 21 | 22 | static int count; 23 | 24 | const struct addrinfo hints = { 25 | .ai_family = AF_INET 26 | }; 27 | 28 | static void handler(struct adns_request *r) 29 | { 30 | struct addrinfo *inf = adns_get_result(r); 31 | 32 | printf("%s => ", r->hostname); 33 | if (inf) { 34 | const struct sockaddr_in *in = 35 | (const struct sockaddr_in *)inf->ai_addr; 36 | 37 | printf("%s\n", inet_ntoa(in->sin_addr)); 38 | } else { 39 | char buf[128]; 40 | 41 | adns_error_format(adns_get_error(r), buf, sizeof(buf)); 42 | puts(buf); 43 | } 44 | 45 | count--; 46 | adns_request_destroy(r); 47 | } 48 | 49 | int main(int argc, char **argv) 50 | { 51 | struct runq run; 52 | struct adns_resolver res; 53 | struct adns_request reqs[argc]; 54 | int i; 55 | 56 | i = net_start(); 57 | assert(i == NETERR_NONE); 58 | 59 | i = runq_init(&run, 0); 60 | assert(i >= 0); 61 | 62 | i = adns_resolver_init(&res, &run); 63 | assert(i >= 0); 64 | 65 | for (i = 0; i < argc; i++) 66 | adns_request_init(&reqs[i], &res); 67 | 68 | adns_request_ask(&reqs[0], "localhost", NULL, &hints, handler); 69 | for (i = 1; i < argc; i++) 70 | adns_request_ask(&reqs[i], argv[i], NULL, &hints, handler); 71 | count = argc; 72 | 73 | while (count) 74 | runq_dispatch(&run, 0); 75 | 76 | adns_resolver_destroy(&res); 77 | runq_destroy(&run); 78 | net_stop(); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /tests/test_arena.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2014 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "arena.h" 22 | 23 | #define TEST_SIZE (100 * 1048576) 24 | #define TEST_COUNT 128 25 | 26 | struct test { 27 | char *ptr; 28 | size_t size; 29 | }; 30 | 31 | static char *base; 32 | static struct test tests[TEST_COUNT]; 33 | static struct arena arena; 34 | static size_t total_available; 35 | 36 | static int cmp_by_ptr(const void *a, const void *b) 37 | { 38 | struct test *ta = (struct test *)a; 39 | struct test *tb = (struct test *)b; 40 | 41 | if (ta->ptr < tb->ptr) 42 | return -1; 43 | if (ta->ptr > tb->ptr) 44 | return 1; 45 | 46 | return 0; 47 | } 48 | 49 | static void shuffle(void) 50 | { 51 | int i; 52 | 53 | for (i = TEST_COUNT - 1; i >= 0; i--) { 54 | const int j = random() % (i + 1); 55 | struct test tmp; 56 | 57 | memcpy(&tmp, &tests[i], sizeof(tmp)); 58 | memcpy(&tests[i], &tests[j], sizeof(tests[i])); 59 | memcpy(&tests[j], &tmp, sizeof(tests[j])); 60 | } 61 | } 62 | 63 | static void test_alloc(void) 64 | { 65 | int i; 66 | 67 | for (i = 0; i < TEST_COUNT; i++) { 68 | struct test *t = &tests[i]; 69 | 70 | t->size = (random() % 256) << (random() % 10); 71 | t->ptr = arena_alloc(&arena, t->size); 72 | assert(t->ptr); 73 | 74 | printf(" %3d. alloc %d -> %p\n", i, (int)t->size, t->ptr); 75 | } 76 | } 77 | 78 | static void test_realloc(void) 79 | { 80 | int i; 81 | 82 | for (i = 0; i < TEST_COUNT; i++) { 83 | struct test *t = &tests[i]; 84 | 85 | printf(" %3d. realloc %p/%d", i, t->ptr, (int)t->size); 86 | 87 | t->size = (random() % 256) << (random() % 15); 88 | t->ptr = arena_realloc(&arena, t->ptr, t->size); 89 | assert(t->ptr); 90 | 91 | printf(" %d -> %p\n", (int)t->size, t->ptr); 92 | } 93 | } 94 | 95 | static void test_free(void) 96 | { 97 | int i; 98 | 99 | for (i = 0; i < TEST_COUNT; i++) 100 | arena_free(&arena, tests[i].ptr); 101 | } 102 | 103 | static void check(void) 104 | { 105 | size_t used = 0; 106 | size_t free = arena_count_free(&arena); 107 | size_t wasted; 108 | int i; 109 | 110 | for (i = 0; i < TEST_COUNT; i++) 111 | used += tests[i].size; 112 | 113 | wasted = total_available - used - free; 114 | 115 | printf("Used: %d\n", (int)used); 116 | printf("Free: %d\n", (int)free); 117 | printf("Total: %d\n", (int)total_available); 118 | printf("Wasted: %d (%.02f%%)\n", (int)wasted, 119 | (double)wasted * 100.0 / (double)total_available); 120 | printf("\n"); 121 | 122 | assert(used + free <= total_available); 123 | 124 | qsort(tests, TEST_COUNT, sizeof(tests[0]), cmp_by_ptr); 125 | 126 | for (i = 0; i + 1 < TEST_COUNT; i++) { 127 | struct test *t = &tests[i]; 128 | struct test *n = &tests[i + 1]; 129 | 130 | assert(t->ptr >= base); 131 | assert(t->ptr < base + TEST_SIZE); 132 | assert(t->ptr + t->size <= n->ptr); 133 | } 134 | } 135 | 136 | int main(void) 137 | { 138 | int i; 139 | 140 | srandom(0); 141 | 142 | base = calloc(1, TEST_SIZE); 143 | assert(base); 144 | 145 | arena_init(&arena, base, TEST_SIZE); 146 | total_available = arena_count_free(&arena); 147 | 148 | printf("Alloc...\n"); 149 | test_alloc(); 150 | check(); 151 | 152 | for (i = 0; i < 10; i++) { 153 | printf("Realloc...\n"); 154 | shuffle(); 155 | test_realloc(); 156 | check(); 157 | } 158 | 159 | printf("Free...\n"); 160 | shuffle(); 161 | test_free(); 162 | assert(arena_count_free(&arena) == total_available); 163 | 164 | free(base); 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /tests/test_asock.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "prng.h" 21 | #include "asock.h" 22 | 23 | #define N 65535 24 | #define MAX_WRITE 8192 25 | #define MAX_READ 3172 26 | 27 | static uint8_t pattern[N]; 28 | static int is_done; 29 | 30 | /************************************************************************ 31 | * Reading strand 32 | */ 33 | 34 | static struct asock server; 35 | static struct asock reader; 36 | static int read_ptr; 37 | static uint8_t read_buf[MAX_READ]; 38 | 39 | static void do_receive(void); 40 | 41 | static void recv_done(struct asock *a) 42 | { 43 | const int len = asock_get_recv_size(&reader); 44 | 45 | assert(!asock_get_recv_error(&reader)); 46 | 47 | if (!len) { 48 | printf("server: EOF\n"); 49 | assert(read_ptr == N); 50 | asock_close(&reader); 51 | is_done = 1; 52 | return; 53 | } 54 | 55 | printf("server: read %d bytes\n", len); 56 | 57 | assert(len <= N - read_ptr); 58 | assert(!memcmp(pattern + read_ptr, read_buf, len)); 59 | read_ptr += len; 60 | 61 | do_receive(); 62 | } 63 | 64 | static void do_receive(void) 65 | { 66 | asock_recv(&reader, read_buf, sizeof(read_buf), recv_done); 67 | } 68 | 69 | static void accept_done(struct asock *a) 70 | { 71 | assert(!asock_get_error(&server)); 72 | printf("server: Connected\n"); 73 | do_receive(); 74 | } 75 | 76 | static void reader_init(struct ioq *q) 77 | { 78 | struct sockaddr_in addr; 79 | int i; 80 | 81 | asock_init(&server, q); 82 | asock_init(&reader, q); 83 | 84 | addr.sin_family = AF_INET; 85 | addr.sin_addr.s_addr = INADDR_ANY; 86 | addr.sin_port = htons(50999); 87 | 88 | i = asock_listen(&server, (struct sockaddr *)&addr, sizeof(addr)); 89 | assert(i >= 0); 90 | 91 | printf("server: Accepting...\n"); 92 | asock_accept(&server, &reader, accept_done); 93 | } 94 | 95 | static void reader_exit(void) 96 | { 97 | asock_destroy(&server); 98 | asock_destroy(&reader); 99 | } 100 | 101 | /************************************************************************ 102 | * Writing strand 103 | */ 104 | 105 | static struct asock writer; 106 | static int write_ptr; 107 | static struct sockaddr_in peer; 108 | 109 | static void begin_write(void); 110 | 111 | static void write_done(struct asock *a) 112 | { 113 | const int len = asock_get_send_size(&writer); 114 | 115 | assert(!asock_get_send_error(&writer)); 116 | 117 | printf("client: Sent %d bytes\n", len); 118 | write_ptr += len; 119 | begin_write(); 120 | } 121 | 122 | static void begin_write(void) 123 | { 124 | int len = N - write_ptr; 125 | 126 | if (len > MAX_WRITE) 127 | len = MAX_WRITE; 128 | 129 | assert(len >= 0); 130 | 131 | if (len) { 132 | printf("client: Sending %d bytes\n", len); 133 | asock_send(&writer, pattern + write_ptr, len, write_done); 134 | } else { 135 | printf("client: Close\n"); 136 | asock_close(&writer); 137 | } 138 | } 139 | 140 | static void connect_done(struct asock *a) 141 | { 142 | assert(!asock_get_error(&writer)); 143 | printf("client: Connected\n"); 144 | 145 | begin_write(); 146 | } 147 | 148 | static void writer_init(struct ioq *q) 149 | { 150 | asock_init(&writer, q); 151 | printf("client: Connecting...\n"); 152 | 153 | peer.sin_family = AF_INET; 154 | peer.sin_addr.s_addr = inet_addr("127.0.0.1"); 155 | peer.sin_port = htons(50999); 156 | 157 | asock_connect(&writer, (struct sockaddr *)&peer, sizeof(peer), 158 | connect_done); 159 | } 160 | 161 | static void writer_exit(void) 162 | { 163 | asock_destroy(&writer); 164 | } 165 | 166 | /************************************************************************ 167 | * Main thread/test 168 | */ 169 | 170 | static void init_pattern(void) 171 | { 172 | int i; 173 | prng_t prng; 174 | 175 | prng_init(&prng, 1); 176 | for (i = 0; i < sizeof(pattern); i++) 177 | pattern[i] = prng_next(&prng); 178 | } 179 | 180 | int main(void) 181 | { 182 | struct ioq q; 183 | int r; 184 | 185 | init_pattern(); 186 | 187 | r = net_start(); 188 | assert(r >= 0); 189 | 190 | r = ioq_init(&q, 0); 191 | assert(r >= 0); 192 | 193 | reader_init(&q); 194 | writer_init(&q); 195 | 196 | while (!is_done) { 197 | const int r = ioq_iterate(&q); 198 | 199 | assert(r >= 0); 200 | } 201 | 202 | writer_exit(); 203 | reader_exit(); 204 | ioq_destroy(&q); 205 | net_stop(); 206 | return 0; 207 | } 208 | -------------------------------------------------------------------------------- /tests/test_bytes.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "bytes.h" 19 | 20 | #define WRITE_TEST(format, value) do { \ 21 | bytes_w##format(buffer, value); \ 22 | assert(bytes_r##format(buffer) == value); \ 23 | } while (0); 24 | 25 | int main(void) 26 | { 27 | uint8_t buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; 28 | 29 | assert(bytes_r16le(buffer) == 0x0201); 30 | assert(bytes_r16net(buffer) == 0x0102); 31 | assert(bytes_r32le(buffer) == 0x04030201); 32 | assert(bytes_r32net(buffer) == 0x01020304); 33 | assert(bytes_r64le(buffer) == 0x0807060504030201LL); 34 | assert(bytes_r64net(buffer) == 0x0102030405060708LL); 35 | 36 | WRITE_TEST(16le, 0xabcd); 37 | WRITE_TEST(16net, 0xabcd); 38 | WRITE_TEST(32le, 0xdeadbeef); 39 | WRITE_TEST(32net, 0xdeadbeef); 40 | WRITE_TEST(64le, 0x2468abcd789a0101LL); 41 | WRITE_TEST(64net, 0x2468abcd789a0101LL); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /tests/test_cbuf.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "prng.h" 20 | #include "cbuf.h" 21 | 22 | #define N 65536 23 | #define MAX_XFER 8192 24 | 25 | static uint8_t pattern[N]; 26 | static uint8_t out[N]; 27 | 28 | static uint8_t data_a[4096]; 29 | static uint8_t data_b[4096]; 30 | 31 | static struct cbuf buf_a; 32 | static struct cbuf buf_b; 33 | 34 | static prng_t prng; 35 | 36 | static void init_pattern(void) 37 | { 38 | int i; 39 | 40 | for (i = 0; i < sizeof(pattern); i++) 41 | pattern[i] = prng_next(&prng); 42 | } 43 | 44 | static size_t choose(size_t n, size_t suggest) 45 | { 46 | return n < suggest ? n : suggest; 47 | } 48 | 49 | static void check_empty(struct cbuf *c) 50 | { 51 | assert(!cbuf_used(&buf_a)); 52 | assert(cbuf_avail(&buf_a) == c->capacity); 53 | } 54 | 55 | static void stream_pattern(size_t is, size_t ms, size_t os) 56 | { 57 | size_t pat_ptr = 0; 58 | size_t out_ptr = 0; 59 | 60 | check_empty(&buf_a); 61 | check_empty(&buf_b); 62 | memset(out, 0, N); 63 | 64 | while (out_ptr < N) { 65 | /* Transfer a random amount to buf_a */ 66 | pat_ptr += cbuf_move_in(&buf_a, 67 | pattern + pat_ptr, choose(N - pat_ptr, is)); 68 | 69 | /* Transfer a random amount between buf_a and buf_b */ 70 | cbuf_move(&buf_b, &buf_a, choose(cbuf_used(&buf_a), ms)); 71 | 72 | /* Transfer a random amount out again */ 73 | out_ptr += cbuf_move_out(&buf_b, 74 | out + out_ptr, choose(N - out_ptr, os)); 75 | } 76 | 77 | check_empty(&buf_a); 78 | check_empty(&buf_b); 79 | assert(pat_ptr == N); 80 | assert(!memcmp(out, pattern, N)); 81 | } 82 | 83 | int main(void) 84 | { 85 | prng_init(&prng, 1); 86 | init_pattern(); 87 | 88 | cbuf_init(&buf_a, data_a, sizeof(data_a)); 89 | cbuf_init(&buf_b, data_b, sizeof(data_b)); 90 | 91 | stream_pattern(4096, 4096, 4096); 92 | stream_pattern(4096, 256, 4096); 93 | stream_pattern(11, 13, 7); 94 | 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /tests/test_clock.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "clock.h" 20 | 21 | int main(void) 22 | { 23 | clock_ticks_t before, after; 24 | 25 | before = clock_now(); 26 | printf("Current time: %" CLOCK_PRI_TICKS "\n", before); 27 | clock_wait(500); 28 | after = clock_now(); 29 | printf("After 500 ms: %" CLOCK_PRI_TICKS "\n", after); 30 | 31 | assert((after >= before + 450) && (after <= before + 550)); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/test_containers.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "containers.h" 19 | 20 | #define N 99 21 | 22 | static int array[N]; 23 | static int loa = lengthof(array); 24 | 25 | struct foo { 26 | int x; 27 | int y; 28 | int z; 29 | }; 30 | 31 | static struct foo foo_s; 32 | static struct foo *foo_ptr = &foo_s; 33 | static int *z_ptr = &foo_s.z; 34 | 35 | int main(void) 36 | { 37 | assert(loa == N); 38 | assert((char *)z_ptr - (char *)foo_ptr == offsetof(struct foo, z)); 39 | assert(container_of(z_ptr, struct foo, z) == foo_ptr); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /tests/test_hash.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "hash.h" 22 | 23 | struct record { 24 | struct hash_node node; 25 | char text[64]; 26 | }; 27 | 28 | #define N 16384 29 | 30 | static struct record words[N]; 31 | static struct hash hsh; 32 | 33 | static int word_compare(const void *k, const struct hash_node *n) 34 | { 35 | const struct record *r = (const struct record *)n; 36 | const char *text = (const char *)k; 37 | 38 | return strcmp(text, r->text); 39 | } 40 | 41 | static hash_code_t word_hash(const void *k) 42 | { 43 | const char *text = (const char *)k; 44 | hash_code_t code = 0; 45 | 46 | while (*text) 47 | code = (code * 33) + *(text++); 48 | 49 | return code; 50 | } 51 | 52 | static void init_words(void) 53 | { 54 | int i; 55 | 56 | for (i = 0; i < N; i++) 57 | snprintf(words[i].text, sizeof(words[i].text), "%x", i); 58 | } 59 | 60 | static void add_half(int start, int flags) 61 | { 62 | int i; 63 | 64 | for (i = start; i < N; i += 2) { 65 | struct hash_node *old = NULL; 66 | int r = hash_insert(&hsh, words[i].text, &words[i].node, 67 | &old, flags); 68 | 69 | assert(!r); 70 | assert(!old); 71 | } 72 | } 73 | 74 | static void remove_half(int start) 75 | { 76 | int i; 77 | 78 | for (i = start; i < N; i += 2) 79 | hash_remove(&hsh, &words[i].node); 80 | } 81 | 82 | static void check_present(int start) 83 | { 84 | int i; 85 | 86 | for (i = start; i < N; i += 2) { 87 | struct hash_node *n = hash_find(&hsh, words[i].text); 88 | 89 | assert(n == &words[i].node); 90 | } 91 | } 92 | 93 | static void check_not_present(int start) 94 | { 95 | int i; 96 | 97 | for (i = start; i < N; i += 2) { 98 | struct hash_node *n = hash_find(&hsh, words[i].text); 99 | 100 | assert(!n); 101 | } 102 | } 103 | 104 | static void test_add_remove(int flags) 105 | { 106 | add_half(0, flags); 107 | assert(hsh.count == N / 2); 108 | assert(hsh.size >= N / 2); 109 | check_present(0); 110 | check_not_present(1); 111 | 112 | hash_capacity_hint(&hsh, N); 113 | assert(hsh.size >= N); 114 | 115 | add_half(1, flags | HASH_INSERT_UNIQUE); 116 | assert(hsh.count == N); 117 | assert(hsh.size >= N); 118 | check_present(0); 119 | check_present(1); 120 | 121 | remove_half(0); 122 | assert(hsh.count == N / 2); 123 | assert(hsh.size >= N / 2); 124 | check_not_present(0); 125 | check_present(1); 126 | 127 | remove_half(1); 128 | check_not_present(0); 129 | check_not_present(1); 130 | assert(hsh.count == 0); 131 | } 132 | 133 | int main(void) 134 | { 135 | init_words(); 136 | 137 | hash_init(&hsh, word_hash, word_compare); 138 | test_add_remove(0); 139 | test_add_remove(HASH_INSERT_PREHASHED); 140 | hash_destroy(&hsh); 141 | 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /tests/test_ioq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifdef __Windows__ 18 | #include "test_ioq_windows.c" 19 | #else 20 | #include "test_ioq_linux.c" 21 | #endif 22 | -------------------------------------------------------------------------------- /tests/test_ioq_linux.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "ioq.h" 21 | #include "containers.h" 22 | 23 | #define N 65536 24 | #define MAX_WRITE 8192 25 | #define MAX_READ 3172 26 | 27 | static uint8_t pattern[N]; 28 | static uint8_t out[N]; 29 | 30 | /************************************************************************ 31 | * Writer strand 32 | */ 33 | struct writer_proc { 34 | struct ioq_fd writer; 35 | struct waitq_timer timer; 36 | int ptr; 37 | }; 38 | 39 | static void write_ready(struct ioq_fd *f); 40 | static void wait_done(struct waitq_timer *timer); 41 | 42 | static void begin_wait(struct writer_proc *w) 43 | { 44 | waitq_timer_wait(&w->timer, 50, wait_done); 45 | 46 | /* Deliberately wait for the wrong thing here so that we can try 47 | * rewait() later. 48 | */ 49 | ioq_fd_wait(&w->writer, IOQ_EVENT_IN, write_ready); 50 | } 51 | 52 | static void write_ready(struct ioq_fd *f) 53 | { 54 | struct writer_proc *w = container_of(f, struct writer_proc, writer); 55 | int xfer = sizeof(pattern) - w->ptr; 56 | int ret; 57 | 58 | assert(!ioq_fd_error(f)); 59 | assert(ioq_fd_ready(f) == IOQ_EVENT_OUT); 60 | 61 | if (xfer > MAX_WRITE) 62 | xfer = MAX_WRITE; 63 | 64 | ret = write(f->fd, pattern + w->ptr, xfer); 65 | assert(ret > 0); 66 | 67 | printf("-- wrote %d bytes\n", ret); 68 | w->ptr += ret; 69 | 70 | if (w->ptr >= sizeof(pattern)) { 71 | printf("Writer done\n"); 72 | close(f->fd); 73 | } else { 74 | begin_wait(w); 75 | } 76 | } 77 | 78 | static void wait_done(struct waitq_timer *timer) 79 | { 80 | struct writer_proc *w = container_of(timer, struct writer_proc, timer); 81 | 82 | ioq_fd_rewait(&w->writer, IOQ_EVENT_OUT); 83 | } 84 | 85 | static void writer_start(struct writer_proc *w, struct ioq *q, int fd) 86 | { 87 | ioq_fd_init(&w->writer, q, fd); 88 | waitq_timer_init(&w->timer, ioq_waitq(q)); 89 | w->ptr = 0; 90 | 91 | begin_wait(w); 92 | } 93 | 94 | /************************************************************************ 95 | * Reader strand 96 | */ 97 | struct reader_proc { 98 | struct ioq_fd reader; 99 | int ptr; 100 | int eof; 101 | }; 102 | 103 | static void read_ready(struct ioq_fd *f) 104 | { 105 | struct reader_proc *r = container_of(f, struct reader_proc, reader); 106 | int xfer = sizeof(out) - r->ptr; 107 | int ret; 108 | 109 | if (xfer > MAX_READ) 110 | xfer = MAX_READ; 111 | 112 | ret = read(f->fd, out + r->ptr, xfer); 113 | assert(ret >= 0); 114 | 115 | if (!ret) { 116 | r->eof = 1; 117 | close(f->fd); 118 | printf("End of read stream\n"); 119 | } else { 120 | printf("-- read %d bytes\n", ret); 121 | r->ptr += ret; 122 | ioq_fd_wait(&r->reader, IOQ_EVENT_IN, read_ready); 123 | } 124 | } 125 | 126 | static void reader_start(struct reader_proc *r, struct ioq *q, int fd) 127 | { 128 | ioq_fd_init(&r->reader, q, fd); 129 | r->ptr = 0; 130 | r->eof = 0; 131 | 132 | ioq_fd_wait(&r->reader, IOQ_EVENT_IN, read_ready); 133 | } 134 | 135 | /************************************************************************ 136 | * Main thread/test 137 | */ 138 | 139 | static void init_pattern(void) 140 | { 141 | int i; 142 | 143 | for (i = 0; i < sizeof(pattern); i++) 144 | pattern[i] = random(); 145 | } 146 | 147 | int main(void) 148 | { 149 | struct ioq ioq; 150 | struct writer_proc writer; 151 | struct reader_proc reader; 152 | int pfd[2]; 153 | int r; 154 | 155 | init_pattern(); 156 | 157 | r = pipe(pfd); 158 | assert(r >= 0); 159 | 160 | r = ioq_init(&ioq, 0); 161 | assert(r >= 0); 162 | 163 | writer_start(&writer, &ioq, pfd[1]); 164 | reader_start(&reader, &ioq, pfd[0]); 165 | 166 | while (!reader.eof) { 167 | r = ioq_iterate(&ioq); 168 | assert(r >= 0); 169 | } 170 | 171 | ioq_destroy(&ioq); 172 | 173 | assert(!memcmp(pattern, out, N)); 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /tests/test_istr.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "istr.h" 21 | 22 | static struct istr_pool pool; 23 | 24 | #define JUNK_N 1024 25 | 26 | static istr_t junk[JUNK_N]; 27 | 28 | static void test_equality(void) 29 | { 30 | istr_t a = istr_pool_alloc(&pool, "hello", -1); 31 | istr_t b = istr_pool_alloc(&pool, "foo", -1); 32 | istr_t c = istr_pool_alloc(&pool, "hello", -1); 33 | 34 | printf("a = \"%s\", b = \"%s\", c = \"%s\"\n", 35 | istr_text(a), istr_text(b), istr_text(c)); 36 | 37 | assert(!istr_equal(a, b)); 38 | assert(!istr_equal(b, c)); 39 | assert(istr_equal(a, c)); 40 | 41 | assert(istr_compare(a, b) > 0); 42 | assert(istr_compare(b, c) < 0); 43 | assert(!istr_compare(a, c)); 44 | 45 | istr_unref(a); 46 | istr_unref(b); 47 | istr_unref(c); 48 | } 49 | 50 | static void add_junk(void) 51 | { 52 | int i; 53 | 54 | for (i = 0; i < JUNK_N; i++) { 55 | char buf[64]; 56 | 57 | snprintf(buf, sizeof(buf), "%d", (i + 1) * 57); 58 | junk[i] = istr_pool_alloc(&pool, buf, -1); 59 | } 60 | } 61 | 62 | static void remove_junk(void) 63 | { 64 | int i; 65 | 66 | for (i = 0; i < JUNK_N; i++) 67 | istr_unref(junk[i]); 68 | } 69 | 70 | static void test_gc(void) 71 | { 72 | istr_t a; 73 | int old_offset; 74 | 75 | add_junk(); 76 | a = istr_pool_alloc(&pool, "test", -1); 77 | old_offset = a->offset; 78 | remove_junk(); 79 | istr_pool_gc(&pool); 80 | 81 | assert(!strcmp(istr_text(a), "test")); 82 | assert(a->offset < old_offset); 83 | 84 | istr_unref(a); 85 | } 86 | 87 | int main(void) 88 | { 89 | istr_pool_init(&pool); 90 | 91 | test_equality(); 92 | test_gc(); 93 | 94 | add_junk(); 95 | remove_junk(); 96 | 97 | istr_pool_gc(&pool); 98 | assert(!pool.desc_count); 99 | assert(!pool.all); 100 | assert(!pool.text.length); 101 | assert(list_is_empty(&pool.descs.full)); 102 | assert(list_is_empty(&pool.descs.partial)); 103 | 104 | istr_pool_destroy(&pool); 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /tests/test_list.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "list.h" 19 | 20 | #define N 1024 21 | 22 | struct record { 23 | struct list_node node; 24 | int v; 25 | }; 26 | 27 | static struct record recs[N]; 28 | static struct list_node lst; 29 | 30 | static void test_init(void) 31 | { 32 | int i; 33 | 34 | for (i = 0; i < N; i++) 35 | recs[i].v = i; 36 | 37 | list_init(&lst); 38 | } 39 | 40 | static void test_start_odd(void) 41 | { 42 | int i; 43 | 44 | for (i = 1; i < N; i += 2) 45 | list_insert(&recs[i].node, &lst); 46 | } 47 | 48 | static void test_add_evens(void) 49 | { 50 | int i; 51 | 52 | for (i = 0; i < N; i += 2) 53 | list_insert(&recs[i].node, &recs[i + 1].node); 54 | } 55 | 56 | static void test_remove(int mask) 57 | { 58 | int i; 59 | 60 | for (i = 0; i < N; i++) 61 | if (((i & 1) && (mask & 1)) || 62 | (!(i & 1) && (mask & 2))) 63 | list_remove(&recs[i].node); 64 | } 65 | 66 | static void test_verify(struct list_node *l, int mask) 67 | { 68 | int even_count = (mask & 2) ? (N / 2) : 0; 69 | int odd_count = (mask & 1) ? (N / 2) : 0; 70 | struct list_node *n; 71 | 72 | for (n = l->next; n != l; n = n->next) { 73 | struct record *r = (struct record *)n; 74 | 75 | if (r->v & 1) 76 | odd_count--; 77 | else 78 | even_count--; 79 | } 80 | 81 | assert(!even_count); 82 | assert(!odd_count); 83 | } 84 | 85 | int main(void) 86 | { 87 | struct list_node copy; 88 | 89 | test_init(); 90 | 91 | test_start_odd(); 92 | test_verify(&lst, 1); 93 | test_add_evens(); 94 | test_verify(&lst, 3); 95 | 96 | list_move(©, &lst); 97 | test_verify(©, 3); 98 | assert(list_is_empty(&lst)); 99 | 100 | list_move(&lst, ©); 101 | test_verify(&lst, 3); 102 | assert(list_is_empty(©)); 103 | 104 | test_remove(1); 105 | test_verify(&lst, 2); 106 | 107 | test_remove(2); 108 | test_verify(&lst, 0); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /tests/test_mailbox.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "mailbox.h" 20 | #include "containers.h" 21 | 22 | #define N 10 23 | 24 | #define MSG_FORWARD MAILBOX_FLAG(0) 25 | #define MSG_QUIT MAILBOX_FLAG(1) 26 | 27 | struct waiter { 28 | struct mailbox mb; 29 | }; 30 | 31 | static int terminate; 32 | static struct waiter waiters[N]; 33 | 34 | static void wait_done(struct mailbox *m) 35 | { 36 | struct waiter *w = container_of(m, struct waiter, mb); 37 | mailbox_flags_t f = mailbox_take(m, MAILBOX_ALL_FLAGS); 38 | int n = w - waiters; 39 | 40 | printf("%d: [f = %x]: ", n, f); 41 | 42 | if (f & MSG_QUIT) { 43 | printf("received MSG_QUIT\n"); 44 | if (n) 45 | mailbox_raise(&waiters[n - 1].mb, MSG_QUIT); 46 | else 47 | terminate = 1; 48 | return; 49 | } 50 | 51 | if (f & MSG_FORWARD) { 52 | printf("received MSG_FORWARD: "); 53 | 54 | if (n + 1 < N) { 55 | printf("forwarding\n"); 56 | mailbox_raise(&waiters[n + 1].mb, MSG_FORWARD); 57 | } else { 58 | printf("self-terminating\n"); 59 | mailbox_raise(&waiters[n].mb, MSG_QUIT); 60 | } 61 | 62 | mailbox_wait(&waiters[n].mb, MAILBOX_ALL_FLAGS, wait_done); 63 | } 64 | } 65 | 66 | static void begin_waiter(struct waiter *w, struct runq *q) 67 | { 68 | int n = w - waiters; 69 | 70 | mailbox_init(&w->mb, q); 71 | printf("%d: start\n", n); 72 | mailbox_wait(&w->mb, MAILBOX_ALL_FLAGS, wait_done); 73 | } 74 | 75 | int main(void) 76 | { 77 | struct runq rq; 78 | int i; 79 | 80 | runq_init(&rq, 0); 81 | for (i = 0; i < N; i++) 82 | begin_waiter(&waiters[i], &rq); 83 | 84 | mailbox_raise(&waiters[0].mb, MSG_FORWARD); 85 | while (!terminate) 86 | runq_dispatch(&rq, 1); 87 | 88 | for (i = 0; i < N; i++) { 89 | assert(waiters[i].mb.mode == MAILBOX_WAIT_NONE); 90 | mailbox_destroy(&waiters[i].mb); 91 | } 92 | runq_destroy(&rq); 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /tests/test_net.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "net.h" 20 | 21 | int main(void) 22 | { 23 | neterr_t e; 24 | 25 | e = net_start(); 26 | if (e != NETERR_NONE) { 27 | char buf[128]; 28 | 29 | neterr_format(e, buf, sizeof(buf)); 30 | fprintf(stderr, "%s\n", buf); 31 | abort(); 32 | } 33 | 34 | net_stop(); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/test_protothread.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "protothread.h" 20 | #include "containers.h" 21 | 22 | static int produce(void) 23 | { 24 | static protothread_state_t state; 25 | static int i; 26 | 27 | PROTOTHREAD_BEGIN(state); 28 | 29 | for (i = 0; i < 5; i++) 30 | PROTOTHREAD_YIELD(state, i); 31 | 32 | PROTOTHREAD_YIELD(state, i); 33 | PROTOTHREAD_YIELD(state, i); 34 | 35 | for (; i >= 0; i--) 36 | PROTOTHREAD_YIELD(state, i); 37 | 38 | PROTOTHREAD_END; 39 | return 0; 40 | } 41 | 42 | int main(void) 43 | { 44 | static const int expect[] = { 45 | 0, 1, 2, 3, 4, 5, 5, 5, 4, 3, 2, 1, 0 46 | }; 47 | int i; 48 | 49 | for (i = 0; i < lengthof(expect); i++) { 50 | const int v = produce(); 51 | 52 | printf("%d\n", v); 53 | assert(v == expect[i]); 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/test_rbt_iter.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "prng.h" 22 | #include "rbt.h" 23 | #include "rbt_iter.h" 24 | 25 | struct record { 26 | struct rbt_node node; 27 | int key; 28 | }; 29 | 30 | #define N 1024 31 | 32 | static int cmp_record(const void *k, const struct rbt_node *n) 33 | { 34 | const int *key = (const int *)k; 35 | const struct record *r = (const struct record *)n; 36 | 37 | if (*key < r->key) 38 | return -1; 39 | if (*key > r->key) 40 | return 1; 41 | 42 | return 0; 43 | } 44 | 45 | static struct record recs[N]; 46 | static struct record *ordering[N]; 47 | static struct rbt tree; 48 | static prng_t prng; 49 | 50 | static void test_init(void) 51 | { 52 | int i; 53 | 54 | for (i = 0; i < N; i++) { 55 | recs[i].key = i * 2; 56 | ordering[i] = &recs[i]; 57 | } 58 | 59 | rbt_init(&tree, cmp_record); 60 | } 61 | 62 | static void test_shuffle(void) 63 | { 64 | int i; 65 | 66 | for (i = N - 1; i > 0; i--) { 67 | int j = prng_next(&prng) % i; 68 | 69 | if (i != j) { 70 | struct record *t = ordering[i]; 71 | 72 | ordering[i] = ordering[j]; 73 | ordering[j] = t; 74 | } 75 | } 76 | } 77 | 78 | static void test_insert(void) 79 | { 80 | int i; 81 | 82 | for (i = 0; i < N; i++) { 83 | struct record *r = ordering[i]; 84 | 85 | rbt_insert(&tree, &r->key, &r->node); 86 | } 87 | } 88 | 89 | static void test_edges(void) 90 | { 91 | struct rbt_node *n; 92 | 93 | n = rbt_iter_next(&recs[N - 1].node); 94 | assert(!n); 95 | 96 | n = rbt_iter_prev(&recs[0].node); 97 | assert(!n); 98 | 99 | n = rbt_iter_lt(&tree, &recs[0].key); 100 | assert(!n); 101 | 102 | n = rbt_iter_gt(&tree, &recs[N - 1].key); 103 | assert(!n); 104 | 105 | n = rbt_iter_first(&tree); 106 | assert(n == &recs[0].node); 107 | 108 | n = rbt_iter_last(&tree); 109 | assert(n == &recs[N - 1].node); 110 | } 111 | 112 | static void test_middle(void) 113 | { 114 | int i; 115 | 116 | for (i = 0; i < N; i++) { 117 | struct record *r = &recs[i]; 118 | struct rbt_node *n; 119 | int key; 120 | 121 | if (i > 0) { 122 | n = rbt_iter_next(&recs[i - 1].node); 123 | assert(n == &r->node); 124 | 125 | n = rbt_iter_lt(&tree, &recs[i].key); 126 | assert(n == &recs[i - 1].node); 127 | } 128 | 129 | if (i + 1 < N) { 130 | n = rbt_iter_prev(&recs[i + 1].node); 131 | assert(n == &r->node); 132 | 133 | n = rbt_iter_gt(&tree, &recs[i].key); 134 | assert(n == &recs[i + 1].node); 135 | } 136 | 137 | key = recs[i].key; 138 | n = rbt_iter_le(&tree, &key); 139 | assert (n == &r->node); 140 | n = rbt_iter_ge(&tree, &key); 141 | assert (n == &r->node); 142 | 143 | key = recs[i].key + 1; 144 | n = rbt_iter_le(&tree, &key); 145 | assert (n == &r->node); 146 | n = rbt_iter_lt(&tree, &key); 147 | assert (n == &r->node); 148 | 149 | key = recs[i].key - 1; 150 | n = rbt_iter_ge(&tree, &key); 151 | assert (n == &r->node); 152 | n = rbt_iter_gt(&tree, &key); 153 | assert (n == &r->node); 154 | } 155 | } 156 | 157 | int main(void) 158 | { 159 | int i; 160 | 161 | prng_init(&prng, time(NULL)); 162 | 163 | for (i = 0; i < 100; i++) { 164 | test_init(); 165 | test_shuffle(); 166 | test_insert(); 167 | 168 | test_edges(); 169 | test_middle(); 170 | } 171 | 172 | return 0; 173 | } 174 | -------------------------------------------------------------------------------- /tests/test_runq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "clock.h" 20 | #include "runq.h" 21 | 22 | #define N_TASKS 10 23 | 24 | static struct runq_task tests[N_TASKS]; 25 | static struct runq queue; 26 | 27 | static int counter; 28 | static thr_mutex_t counter_lock; 29 | static thr_event_t counter_event; 30 | 31 | static void task_func(struct runq_task *t) 32 | { 33 | int n = t - tests; 34 | 35 | printf(" - task func %d dispatched\n", n); 36 | 37 | thr_mutex_lock(&counter_lock); 38 | counter++; 39 | thr_mutex_unlock(&counter_lock); 40 | thr_event_raise(&counter_event); 41 | } 42 | 43 | static int read_counter(void) 44 | { 45 | int r; 46 | 47 | thr_mutex_lock(&counter_lock); 48 | r = counter; 49 | thr_mutex_unlock(&counter_lock); 50 | 51 | return r; 52 | } 53 | 54 | static void wait_counter(unsigned int bg_threads) 55 | { 56 | if (bg_threads) { 57 | thr_event_wait(&counter_event); 58 | thr_event_clear(&counter_event); 59 | } else { 60 | runq_dispatch(&queue, 0); 61 | } 62 | } 63 | 64 | static void test_tasks(unsigned int bg_threads) 65 | { 66 | int i; 67 | 68 | printf("Test with %d background threads\n", bg_threads); 69 | counter = 0; 70 | i = runq_init(&queue, bg_threads); 71 | assert(i >= 0); 72 | 73 | for (i = 0; i < N_TASKS; i++) { 74 | runq_task_init(&tests[i], &queue); 75 | runq_task_exec(&tests[i], task_func); 76 | } 77 | 78 | while (read_counter() != N_TASKS) 79 | wait_counter(bg_threads); 80 | 81 | printf("...\n"); 82 | clock_wait(100); 83 | i = read_counter(); 84 | assert(i == N_TASKS); 85 | 86 | for (i = 0; i < N_TASKS; i++) 87 | runq_task_exec(&tests[i], task_func); 88 | 89 | while (read_counter() != N_TASKS * 2) 90 | wait_counter(bg_threads); 91 | 92 | clock_wait(100); 93 | i = read_counter(); 94 | assert(i == N_TASKS * 2); 95 | 96 | runq_destroy(&queue); 97 | printf("\n"); 98 | } 99 | 100 | int main(void) 101 | { 102 | int r; 103 | 104 | thr_mutex_init(&counter_lock); 105 | r = thr_event_init(&counter_event); 106 | assert(r >= 0); 107 | 108 | test_tasks(0); 109 | test_tasks(4); 110 | 111 | thr_mutex_destroy(&counter_lock); 112 | thr_event_destroy(&counter_event); 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /tests/test_slab.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "slab.h" 20 | 21 | #define N 131072 22 | 23 | struct item { 24 | int x; 25 | int y; 26 | }; 27 | 28 | static struct item *objects[N]; 29 | static struct slab slb; 30 | 31 | static void test_alloc_all(void) 32 | { 33 | int i; 34 | 35 | for (i = 0; i < N; i++) { 36 | objects[i] = slab_alloc(&slb); 37 | objects[i]->x = i; 38 | objects[i]->y = i * 2; 39 | } 40 | } 41 | 42 | static void test_check(void) 43 | { 44 | int i; 45 | 46 | for (i = 0; i < N; i++) { 47 | assert(objects[i]->x == i); 48 | assert(objects[i]->y == i * 2); 49 | } 50 | } 51 | 52 | static void test_free_all(void) 53 | { 54 | int i; 55 | 56 | for (i = 0; i < N; i++) 57 | slab_free(&slb, objects[i]); 58 | } 59 | 60 | int main(void) 61 | { 62 | slab_init(&slb, sizeof(struct item)); 63 | 64 | printf("objsize = %d (original %d)\n", 65 | slb.objsize, (int)sizeof(struct item)); 66 | printf("count = %d\n", slb.count); 67 | printf("info_offset = %d\n", slb.info_offset); 68 | printf("slabsize = %d\n", slb.slabsize); 69 | 70 | assert(slb.objsize >= sizeof(struct item)); 71 | assert(slb.count * slb.objsize <= slb.info_offset); 72 | assert(slb.info_offset < slb.slabsize); 73 | 74 | test_alloc_all(); 75 | test_check(); 76 | test_free_all(); 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /tests/test_slist.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "slist.h" 19 | 20 | #define N 1024 21 | 22 | static struct slist_node recs[N]; 23 | static struct slist lst; 24 | 25 | /* Add the nodes to an empty list in forward order using 26 | * slist_append(). 27 | */ 28 | static void test_append(void) 29 | { 30 | int i; 31 | 32 | for (i = 0; i < N; i++) 33 | slist_append(&lst, &recs[i]); 34 | } 35 | 36 | /* Add the nodes to an empty list in reverse order using 37 | * slist_push(). 38 | */ 39 | static void test_push(void) 40 | { 41 | int i; 42 | 43 | for (i = N - 1; i >= 0; i--) 44 | slist_push(&lst, &recs[i]); 45 | } 46 | 47 | /* Verify that the list contains all nodes in forward order. */ 48 | static void test_verify(void) 49 | { 50 | struct slist_node *n = lst.start; 51 | int i; 52 | 53 | assert(lst.start == &recs[0]); 54 | assert(lst.end == &recs[N - 1]); 55 | 56 | for (i = 0; i < N; i++) { 57 | assert(n == &recs[i]); 58 | n = n->next; 59 | } 60 | 61 | assert(!n); 62 | } 63 | 64 | /* Remove the nodes from a full list in forward order using slist_pop(). 65 | */ 66 | static void test_pop(void) 67 | { 68 | struct slist_node *n; 69 | int i; 70 | 71 | for (i = 0; i < N; i++) { 72 | n = slist_pop(&lst); 73 | assert(n == &recs[i]); 74 | } 75 | 76 | assert(!lst.start); 77 | assert(!lst.end); 78 | 79 | n = slist_pop(&lst); 80 | assert(!n); 81 | } 82 | 83 | int main(void) 84 | { 85 | slist_init(&lst); 86 | assert(slist_is_empty(&lst)); 87 | 88 | test_append(); 89 | test_verify(); 90 | test_pop(); 91 | assert(slist_is_empty(&lst)); 92 | 93 | test_push(); 94 | test_verify(); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_strbuf.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "strbuf.h" 21 | 22 | static void check(const struct strbuf *buf) 23 | { 24 | assert(!buf->length || buf->length + 1 <= buf->capacity); 25 | assert(buf->text); 26 | assert(!buf->text[buf->length]); 27 | assert(!buf->failed); 28 | } 29 | 30 | static void big_test(struct strbuf *buf) 31 | { 32 | int i; 33 | 34 | strbuf_clear(buf); 35 | check(buf); 36 | 37 | for (i = 0; i < 100; i++) { 38 | strbuf_add_string(buf, "Hello", -1); 39 | check(buf); 40 | } 41 | 42 | assert(buf->length == 500); 43 | 44 | for (i = 0; i < 100; i++) { 45 | int r = memcmp(buf->text + i * 5, "Hello", 5); 46 | 47 | assert(!r); 48 | } 49 | 50 | strbuf_resize(buf, 250); 51 | check(buf); 52 | 53 | for (i = 0; i < 50; i++) { 54 | int r = memcmp(buf->text + i * 5, "Hello", 5); 55 | 56 | assert(!r); 57 | } 58 | } 59 | 60 | static void compare(const struct strbuf *buf, const char *text) 61 | { 62 | int r = strcmp(buf->text, text); 63 | 64 | assert(!r); 65 | } 66 | 67 | int main(void) 68 | { 69 | struct strbuf buf; 70 | 71 | strbuf_init(&buf); 72 | check(&buf); 73 | 74 | strbuf_add_char(&buf, 'x'); 75 | check(&buf); 76 | 77 | strbuf_add_string(&buf, "Hello World", -1); 78 | check(&buf); 79 | 80 | strbuf_add_char(&buf, 'y'); 81 | check(&buf); 82 | 83 | compare(&buf, "xHello Worldy"); 84 | strbuf_clear(&buf); 85 | check(&buf); 86 | 87 | strbuf_printf(&buf, "%d %d %d %s", 88 | 1, 2, 3, "foo"); 89 | check(&buf); 90 | strbuf_printf(&buf, "%x", 0xdeadbeef); 91 | check(&buf); 92 | compare(&buf, "1 2 3 foodeadbeef"); 93 | 94 | strbuf_resize(&buf, 3); 95 | check(&buf); 96 | compare(&buf, "1 2"); 97 | 98 | big_test(&buf); 99 | 100 | strbuf_destroy(&buf); 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /tests/test_strlcpy.c: -------------------------------------------------------------------------------- 1 | /* strlcpy - safe string copy 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "strlcpy.h" 20 | 21 | int main(void) 22 | { 23 | char buf[16]; 24 | int i; 25 | 26 | buf[0] = 0; 27 | i = strlcpy(buf, "Foo", sizeof(buf)); 28 | assert(i == 3); 29 | 30 | buf[0] = 0; 31 | i = strlcpy(buf, "This is a long string", sizeof(buf)); 32 | assert(i >= sizeof(buf)); 33 | assert(!strcmp(buf, "This is a long ")); 34 | 35 | buf[0] = 0; 36 | i = strlcat(buf, "This is", sizeof(buf)); 37 | assert(i == 7); 38 | assert(!strcmp(buf, "This is")); 39 | 40 | i = strlcat(buf, " a ", sizeof(buf)); 41 | assert(i == 10); 42 | assert(!strcmp(buf, "This is a ")); 43 | 44 | i = strlcat(buf, "long string", sizeof(buf)); 45 | assert(i >= sizeof(buf)); 46 | assert(!strcmp(buf, "This is a long ")); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/test_syserr.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "syserr.h" 20 | 21 | int main(void) 22 | { 23 | FILE *f = fopen("/this/file/does/not/exist", "r"); 24 | syserr_t err; 25 | char msg[128]; 26 | 27 | assert(!f); 28 | 29 | err = syserr_last(); 30 | assert(err != SYSERR_NONE); 31 | 32 | syserr_format(err, msg, sizeof(msg)); 33 | assert(msg[0]); 34 | assert(strlen(msg) < 128); 35 | 36 | printf("%d: %s\n", (int)err, msg); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /tests/test_thr.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "thr.h" 20 | #include "syserr.h" 21 | #include "clock.h" 22 | 23 | static int counter; 24 | static thr_thread_t worker; 25 | static thr_mutex_t mutex; 26 | static thr_event_t event; 27 | 28 | static void report_syserr(const char *prefix) 29 | { 30 | const syserr_t err = syserr_last(); 31 | char msg[128]; 32 | 33 | syserr_format(err, msg, sizeof(msg)); 34 | fprintf(stderr, "%s: %s\n", prefix, msg); 35 | } 36 | 37 | static void work_func(void *arg) 38 | { 39 | int my_count = 0; 40 | 41 | while (my_count < 5) { 42 | thr_event_wait(&event); 43 | thr_event_clear(&event); 44 | thr_mutex_lock(&mutex); 45 | while (counter) { 46 | counter--; 47 | my_count++; 48 | printf("consumer: counter--\n"); 49 | } 50 | thr_mutex_unlock(&mutex); 51 | } 52 | } 53 | 54 | static void test_timedwait(void) 55 | { 56 | clock_ticks_t before, after; 57 | int r; 58 | 59 | thr_event_clear(&event); 60 | printf("With event clear...\n"); 61 | 62 | before = clock_now(); 63 | printf(" current time: %" CLOCK_PRI_TICKS "\n", before); 64 | r = thr_event_wait_timeout(&event, 500); 65 | assert(r); 66 | after = clock_now(); 67 | printf(" after 500 ms wait: %" CLOCK_PRI_TICKS "\n", after); 68 | 69 | assert((after >= before + 450) && (after <= before + 550)); 70 | 71 | thr_event_raise(&event); 72 | printf("With event set...\n"); 73 | 74 | before = clock_now(); 75 | printf(" current time: %" CLOCK_PRI_TICKS "\n", before); 76 | r = thr_event_wait_timeout(&event, 500); 77 | assert(!r); 78 | after = clock_now(); 79 | printf(" after 500 ms wait: %" CLOCK_PRI_TICKS "\n", after); 80 | 81 | assert(after <= before + 50); 82 | } 83 | 84 | int main(void) 85 | { 86 | int my_count = 5; 87 | 88 | thr_mutex_init(&mutex); 89 | if (thr_event_init(&event) < 0) { 90 | report_syserr("thr_event_init"); 91 | return -1; 92 | } 93 | 94 | thr_start(&worker, work_func, NULL); 95 | while (my_count) { 96 | thr_mutex_lock(&mutex); 97 | counter++; 98 | my_count--; 99 | printf("producer: counter++\n"); 100 | thr_mutex_unlock(&mutex); 101 | thr_event_raise(&event); 102 | clock_wait(10); 103 | } 104 | thr_join(worker); 105 | 106 | test_timedwait(); 107 | 108 | thr_event_destroy(&event); 109 | thr_mutex_destroy(&mutex); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /tests/test_vector.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2011 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "vector.h" 19 | 20 | #define N 131072 21 | #define ALLOC_EXPECT 17 22 | 23 | static int last_capacity; 24 | static int realloc_count; 25 | 26 | static struct vector vec; 27 | 28 | static void realloc_check(void) 29 | { 30 | assert(vec.capacity >= vec.size); 31 | 32 | if (vec.capacity != last_capacity) { 33 | last_capacity = vec.capacity; 34 | realloc_count++; 35 | } 36 | } 37 | 38 | static void test_push(void) 39 | { 40 | int i; 41 | 42 | realloc_count = 0; 43 | 44 | for (i = 0; i < N; i++) { 45 | vector_push(&vec, &i, 1); 46 | assert(vec.size == i + 1); 47 | realloc_check(); 48 | } 49 | 50 | assert(realloc_count <= ALLOC_EXPECT); 51 | } 52 | 53 | static void test_check(int max) 54 | { 55 | int i; 56 | 57 | assert(vec.size >= max); 58 | 59 | for (i = 0; i < max; i++) 60 | assert(VECTOR_AT(vec, i, int) == i); 61 | } 62 | 63 | static void test_pop(void) 64 | { 65 | int i; 66 | 67 | realloc_count = 0; 68 | 69 | for (i = 0; i < N; i++) { 70 | vector_pop(&vec, 1); 71 | assert(vec.size == N - i - 1); 72 | realloc_check(); 73 | } 74 | 75 | assert(realloc_count <= ALLOC_EXPECT); 76 | assert(vec.capacity < 1024); 77 | } 78 | 79 | static void test_wiggle(void) 80 | { 81 | int i; 82 | 83 | realloc_count = 0; 84 | 85 | for (i = 0; i < N; i++) { 86 | vector_push(&vec, &i, 1); 87 | vector_push(&vec, &i, 1); 88 | vector_push(&vec, &i, 1); 89 | vector_pop(&vec, 1); 90 | vector_pop(&vec, 1); 91 | vector_push(&vec, &i, 1); 92 | vector_pop(&vec, 1); 93 | assert(vec.size == i + 1); 94 | realloc_check(); 95 | } 96 | 97 | assert(realloc_count <= ALLOC_EXPECT); 98 | } 99 | 100 | static void test_bulk(void) 101 | { 102 | int bulk[1024]; 103 | int i; 104 | 105 | for (i = 0; i < 1024; i++) 106 | bulk[i] = i; 107 | 108 | realloc_count = 0; 109 | 110 | vector_push(&vec, bulk, 1024); 111 | realloc_check(); 112 | 113 | assert(vec.size == 1024); 114 | assert(realloc_count <= 1); 115 | test_check(1024); 116 | 117 | vector_resize(&vec, 2048); 118 | realloc_check(); 119 | 120 | assert(vec.size == 2048); 121 | assert(realloc_count <= 2); 122 | test_check(1024); 123 | 124 | vector_clear(&vec); 125 | } 126 | 127 | int main(void) 128 | { 129 | vector_init(&vec, sizeof(int)); 130 | test_bulk(); 131 | test_push(); 132 | test_check(N); 133 | test_pop(); 134 | test_wiggle(); 135 | test_check(N); 136 | vector_destroy(&vec); 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /tests/test_waitq.c: -------------------------------------------------------------------------------- 1 | /* libdlb - data structures and utilities library 2 | * Copyright (C) 2013 Daniel Beer 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "waitq.h" 20 | #include "clock.h" 21 | 22 | #define N_TIMERS 10 23 | 24 | static struct waitq waitq; 25 | static struct runq runq; 26 | static struct waitq_timer test_timers[N_TIMERS]; 27 | static int counter; 28 | 29 | static void test_timeout(struct waitq_timer *t) 30 | { 31 | int n = t - test_timers; 32 | 33 | printf(" -- expired %d%s\n", n, 34 | waitq_timer_cancelled(t) ? " (cancelled)" : ""); 35 | counter++; 36 | } 37 | 38 | int main(void) 39 | { 40 | clock_ticks_t before = clock_now(); 41 | clock_ticks_t after; 42 | int i; 43 | 44 | runq_init(&runq, 0); 45 | waitq_init(&waitq, &runq); 46 | 47 | /* Schedule some timers */ 48 | for (i = 0; i < N_TIMERS; i++) { 49 | waitq_timer_init(&test_timers[i], &waitq); 50 | waitq_timer_wait(&test_timers[i], i * 50 + 50, 51 | test_timeout); 52 | } 53 | 54 | waitq_timer_cancel(&test_timers[5]); 55 | 56 | /* Keep running until they all expire */ 57 | while (counter < N_TIMERS) { 58 | clock_wait(waitq_next_deadline(&waitq)); 59 | waitq_dispatch(&waitq, 0); 60 | runq_dispatch(&runq, 0); 61 | } 62 | 63 | after = clock_now(); 64 | waitq_destroy(&waitq); 65 | runq_destroy(&runq); 66 | 67 | printf("Running time: %" CLOCK_PRI_TICKS "\n", after - before); 68 | assert(after >= before + 400); 69 | assert(after <= before + 600); 70 | return 0; 71 | } 72 | --------------------------------------------------------------------------------