#include "dynar.h" #include "mem.h" #include #include #define ARRAY_ROOM 16 /** * Dynar is a DYNamic ARray of size elements each of elsz size. * For simplicity, dynarr is exactly sized, that is, * it does not allocate any extra elements but the amount requested. * In other words it's capacity is equal to it's size. */ struct Dynar_ { char * data; // use a char * pointer since that makes pointer arithmetic easier int size; int elsz; }; /** Gets the array's size. Returns 0 if self is NULL. */ int dynar_size(Dynar * self) { if(!self) return 0; return self->size; } /** Gets the amount of elements in the vector. Returns 0 if self is NULL. * Alias for dynar_size(self). **/ int dynar_amount(Dynar * self) { if(!self) return 0; return self->size; } /** Gets the array's element size. Returns 0 if self is NULL. */ int dynar_elementsize(Dynar * self) { if(!self) return 0; return self->elsz; } /** Frees the contents of an array. Has the same effect as emptying the array. Does not call a desctructor on any elements contained! */ Dynar * dynar_done(Dynar * self) { if(!self) return NULL; mem_free(self->data); self->data = NULL; self->size = 0; return self; } /** Calls a destructor on the contents of the array if it is not null. The contents are considered to be pointers. Does not call dynar_free()!!! */ Dynar * dynar_destroy(Dynar * self, MemDestructor * destroy) { int index; int size = dynar_size(self); if(!self) return self; if(!destroy) return NULL; for(index = 0; index < size ; index++) { void * ptr = dynar_getptr(self, index); destroy(ptr); } return self; } /** Calls a destructor on the contents of the array if it is not null. The contents are considered to be structs. Does not call dynar_free()!!! */ Dynar * dynar_destroy_structs(Dynar * self, MemDestructor * destroy) { int index; int size = dynar_size(self); if(!self) return self; if(!destroy) return NULL; for(index = 0; index < size ; index++) { void * ptr = dynar_getdata(self, index); destroy(ptr); } return self; } /** Frees an array. Returns NULL. */ Dynar * dynar_free(Dynar * self) { dynar_done(self); return mem_free(self); } /** Calls a destructor on the elements of the array and then frees the array */ Dynar * dynar_free_destroy(Dynar * self, MemDestructor * destroy) { dynar_destroy(self, destroy); return dynar_free(self); } /** Calls a destructor on the elements of the array and then frees the array */ Dynar * dynar_destroy_structs_and_free(Dynar * self, MemDestructor * destroy) { dynar_destroy_structs(self, destroy); return dynar_free(self); } /** Allocates a new empty array. */ Dynar * dynar_alloc() { return STRUCT_ALLOC(Dynar); } /** Initializes an empty array with 0 elements of size elsz each. */ Dynar * dynar_initempty(Dynar * self, int elsz) { if(!self) return NULL; if(elsz < 0) return NULL; self->size = 0; self->elsz = elsz; return self; } /** Allocates a new empty array for elements of size elsz each. */ Dynar * dynar_newempty(int elsz) { Dynar * res = dynar_alloc(); if(!dynar_initempty(res, elsz)) { return dynar_free(res); } return res; } /** Changes the size of the dynamic array. Newsize must be >= 1. */ Dynar * dynar_size_(Dynar* self, int newsize) { int elsz = dynar_elementsize(self); void * newd = NULL; int delta; if(!self) return NULL; // don't allow a newsize of 0, since that will make realloc call // free on self->data, which we don't want. if(newsize < 1) return NULL; newd = mem_realloc(self->data, newsize * elsz); if(!newd) return NULL; // if we get here realloc was successful, so it should be safe to reassign // self->data self->data = newd; // now, empty the unused new data, but only if growing! (XXX: is this ok???) delta = newsize - self->size; if(delta > 0) { memset(self->data + (self->size * self->elsz), 0, (delta * self->elsz)); } self->size = newsize; return self; } /** Initializes a new array with a amount elements of size elsz each. */ Dynar * dynar_init(Dynar * self, int amount, int elsz) { Dynar * aid = dynar_initempty(self, elsz); if(!aid) return NULL; if(!dynar_size_(self, amount)) return NULL; return self; } /** Initializes a new array with a capacity to store amount void* pointers.*/ Dynar * dynar_initptr(Dynar * self, int amount) { return dynar_init(self, amount, sizeof(void *)); } /** Allocates a new array with amount elements of size elsz each. */ Dynar * dynar_new(int amount, int elsz) { Dynar * res = dynar_alloc(); if(!dynar_init(res, amount, elsz)) { return dynar_free(res); } return res; } /** Allocates a new array that will be able to contain amount void * pointers.*/ Dynar * dynar_newptr(int amount) { return dynar_new(amount, sizeof(void *)); } /** Sets the amount of elements of the the array, but ony if * amount bigger than the bigger than the current size. * Returns NULL if the array was not grown, otherwise returns self. */ Dynar * dynar_grow(Dynar * self, int amount) { int mysize = dynar_size(self); if (mysize >= amount) return NULL; return dynar_size_(self, amount); } /** Checks if the index is smaller than the array's available room . */ int dynar_sizeindex_ok(Dynar * self, int index) { if(!self) return FALSE; return index < dynar_size(self); } /** Checks if the int index is smaller than the array's available room. */ int dynar_index_ok(Dynar * self, int index) { if(!self) return FALSE; if (index < 0) return FALSE; return dynar_sizeindex_ok(self, (int)(index)); } /** Returns a pointer to the index-th element of the array. Does no bounds checking! */ void * dynar_getraw_unsafe(Dynar * self, int index) { return self->data + (index * self->elsz); } /** Copies dynar_elementsize(self) of bytes from the index-th element of the array to out, which must be pointing to a bufer of at least dynar_elementsize(self). Does no bounds checking! Returns NULL on failure, out on success. */ void * dynar_getcopy_unsafe(Dynar * self, int index, void * out) { char * cptr = (char *) dynar_getraw_unsafe(self, index); int size = dynar_elementsize(self); if((!self) || (!out) || (!cptr)) return NULL; mem_move(out, cptr, size); return out; } /** Copies dynar_elementsize(self) of bytes from the data pointed to * by ptr into the location pointed to by index. * Does no bounds checking! */ Dynar * dynar_putraw_unsafe(Dynar * self, int index, void * ptr) { char * cptr = (char *) dynar_getraw_unsafe(self, index); int size = dynar_elementsize(self); if((!self) || (!ptr) || (!cptr)) return NULL; mem_move(cptr, ptr, size); return self; } /** Returns a pointer to the index-th element of the array. Does bounds checking and return NULL if out of bounds */ void * dynar_getraw(Dynar * self, int index) { // Bounds check if(!dynar_index_ok(self, index)) { return NULL; } return dynar_getraw_unsafe(self, index); } /** Returns a pointer to the index-th element of the array. Does bounds checking and return NULL if out of bounds */ void * dynar_getcopy(Dynar * self, int index, void * ptr) { // Bounds check if(!dynar_index_ok(self, index)) { return NULL; } return dynar_getcopy_unsafe(self, index, ptr); } /** Copies the dynar_elementsize(self) of bytes from the data pointed to * by ptr into the location pointed to by index. * Does bounds checking and return NULL if ouut of bounds. */ Dynar * dynar_putraw(Dynar * self, int index, void * ptr) { // Bounds check if(!dynar_index_ok(self, index)) { return NULL; } return dynar_putraw_unsafe(self, index, ptr); } /** Stores a pointer at the index of the array. * Does bounds checking. dynar_elementsize(self) sould have been * initialized as sizeof(void *) by using dynar_newptr */ void * dynar_putptr(Dynar * self, int index, void * ptr) { return dynar_putraw(self, index, &ptr); // use &ptr because we want to put the contents of the pointer, // not the pointer itself. } /** Returns a pointer that was stored at the index index of the array. */ void * dynar_getptr(Dynar * self, int index) { void * res = NULL; if(!dynar_getcopy(self, index, &res)) return NULL; // use &ptr because we want to fetch the contents of the pointer, // which will be copied from the element of the array. return res; } /** Copies the element that *ptr points to into this array at position index */ void * dynar_putdata(Dynar * self, int index, void * ptr) { return dynar_putraw(self, index, ptr); } /** Returns a pointer to the index-th element of the array. */ void * dynar_getdata(Dynar * self, int index) { return dynar_getraw(self, index); } /* Applies quick sort to the array using the given comparator. */ Dynar * dynar_qsort(Dynar * self, DynarCompare * compare) { void * base; int nmemb; size_t size; if(!self) return NULL; base = self->data; nmemb = self->size; size = self->elsz; qsort(base, nmemb, size, compare); return self; } /* Applies a binary search to the array using the given comparator. User must call dynar_qsort first. */ void * dynar_bsearch(Dynar * self, const void * key, DynarCompare * compare) { void * base; int nmemb; size_t size; if (!self) return NULL; base = self->data; nmemb = self->size; size = self->elsz; return bsearch(key, base, nmemb, size, compare); } /* Puts NULL in every element of this array using dynar_putptr */ Dynar * dynar_putnullall(Dynar * self) { int stop = dynar_size(self); int index; for (index = 0; index < stop; index++) { dynar_putptr(self, index, NULL); } return self; } /* Iterator helper: fill in every->now as data. */ Every * dynar_everynow_data(Every * every) { every->now = dynar_getdata(every->on, every->index); if(every->now) return every; return NULL; } /* Iterator helper: fill in every->now as pointer. */ Every * dynar_everynow_ptr(Every * every) { every->now = dynar_getptr(every->on, every->index); if(every->now) return every; return NULL; } /* Iterator helpers: init */ Every * dynar_everyinit_data(Every * every) { every->index = 0; return dynar_everynow_data(every); } /* Iterator helpers: next */ Every * dynar_everynext_data(Every * every) { every->index++; return dynar_everynow_data(every); } /* Iterator helpers: put. */ void * dynar_everyput_data(Every * every, void * data) { return dynar_putdata(every->on, every->index, data); } /* Iterator helpers: done. */ void * dynar_everydone(Every * every) { return every; } /* Iterator helpers: init pointers */ Every * dynar_everyinit_ptr(Every * every) { every->index = 0; return dynar_everynow_ptr(every); } /* Iterator helpers: next pointers */ Every * dynar_everynext_ptr(Every * every) { every->index++; return dynar_everynow_ptr(every); } /* Iterator helpers: put pointers. */ void * dynar_everyput_ptr(Every * every, void * data) { return dynar_putptr(every->on, every->index, data); } /* Iterator helper table. */ static EveryActs dynar_every_data_acts_ = { dynar_everydone, dynar_everyinit_data, dynar_everynext_data, dynar_everyput_data }; /* Iterator helper table. */ static EveryActs dynar_every_ptr_acts_ = { dynar_everydone, dynar_everyinit_ptr, dynar_everynext_ptr, dynar_everyput_ptr }; /** Iterates over the data. Call every_free when done. */ Every * dynar_every_data(Dynar * dynar) { return every_new(&dynar_every_data_acts_); } /** Iterates over the pointers in this array. Call every_free when done. */ Every * dynar_every_ptr(Dynar * dynar) { return every_new(&dynar_every_ptr_acts_); } /* Walks over the array using the each interface, accessing the data as pointers. eachdo should return non-null to break the iteration, the data thus found is returned bu this function. */ void * dynar_each_ptr(Dynar * self, EachDo * eachdo, void * extra) { Each each; int index; int size = dynar_size(self); each_init(&each, self, extra); for(index = 0; index < size ; index++) { void * aid; void * ptr = dynar_getptr(self, index); each_next(&each, ptr); aid = eachdo(&each); if (aid) return aid; } return NULL; } /* Walks over the array using the Each interface, accessing the data as stored structs. */ void * dynar_each_data(Dynar * self, EachDo * eachdo, void * extra) { Each each; int index; int size = dynar_size(self); each_init(&each, self, extra); for(index = 0; index < size ; index++) { void * aid; void * ptr = dynar_getdata(self, index); each_next(&each, ptr); aid = eachdo(&each); if (aid) return aid; } return NULL; } /* Walks over the array using the walker interface, accessing the data as stored pointers. */ void * dynar_walkptrbetween(Dynar * self, int low, int high, Walker * walker, void * extra) { int index; int size = dynar_size(self); low = (low < 0) ? 0 : low; high = (high > size) ? size : high; for(index = low; index < high ; index++) { void * aid; void * ptr = dynar_getptr(self, index); aid = walker(ptr, extra); if (aid) return aid; } return NULL; } /* Walks over the array using the walker interface, accessing the data as stored structs. */ void * dynar_walkptr(Dynar * self, Walker * walker, void * extra) { return dynar_walkptrbetween(self, 0, dynar_size(self), walker, extra); } /* Walks over the array using the walker interface, accessing the data as stored pointers. */ void * dynar_walkdatabetween(Dynar * self, int low, int high, Walker * walker, void * extra) { int index; int size = dynar_size(self); low = (low < 0) ? 0 : low; high = (high > size) ? size : high; for(index = low; index < high ; index++) { void * aid; void * ptr = dynar_getdata(self, index); aid = walker(ptr, extra); if (aid) return aid; } return NULL; } /* Walks over the array using the walker interface, accessing the data as stored structs. */ void * dynar_walkdata(Dynar * self, Walker * walker, void * extra) { return dynar_walkdatabetween(self, 0, dynar_size(self), walker, extra); } /* Walks over the array updating it, using the walker interface, accessing the data as stored pointers. */ void * dynar_walkmapptrbetween(Dynar * self, int low, int high, Walker * walker, void * extra) { int index; int size = dynar_size(self); low = (low < 0) ? 0 : low; high = (high > size) ? size : high; for(index = low; index < high ; index++) { void * aid; void * ptr = dynar_getptr(self, index); aid = walker(ptr, extra); dynar_putptr(self, index, ptr); } return NULL; } /* Walks over the array updating it using the walker interface, accessing the data as stored structs. */ void * dynar_walkmapptr(Dynar * self, Walker * walker, void * extra) { return dynar_walkmapptrbetween(self, 0, dynar_size(self), walker, extra); } /* Resizes a dynar filled with pointers, and if the dynar shrinks, calls the * given destructor on all elements that are to be removed. Returns self on success * and NULL on failure. Even in this case, the superfluous elements fmay have been removed. */ Dynar * dynar_resize(Dynar * self, int newsize, MemDestructor * destroy) { int index; int last; if (!self) return NULL; last = dynar_size(self); for(index = newsize; index < last; index ++) { void * aid = dynar_getptr(self, index); dynar_putptr(self, index, destroy(aid)); } return dynar_size_(self, newsize); } /** Makes a new empty dynar to contain long integers */ Dynar * dynar_new_long() { return dynar_newempty(sizeof(long)); } /** Stores a long integer into a dynar (preferrably created with dynar_new_long */ Dynar * dynar_put_long(Dynar * self, int index, long value) { return dynar_putdata(self, index, &value); } /** Gets a long integer from a dynar */ Dynar * dynar_get_long(Dynar * self, int index, long * value) { return dynar_getcopy(self, index, value); } /** Grows the size of the array by 1 and appends the long in the last position */ Dynar * dynar_append_long(Dynar * self, long value) { int pos = dynar_size(self); if (!dynar_size_(self, pos+1)) return NULL; return dynar_put_long(self, pos, value); } /** * Lilis is a doubly Linked List that points to it's members via void pointers * but does not own them in the sense that it doesn't clean them up unless requested. */ struct Lilis_ { Lilis * next; Lilis * prev; void * data; }; /** * frees the tail of a llist only. Does nothing to self or other. */ Lilis * lilis_freetail(Lilis * self) { Lilis * next, * now; if(!self) return NULL; now = self->next; // skip current node, that will be deleted in lilis_free while (now) { next = now->next; // already get next one mem_free(now); // now we can safely free the current node now = next; // set current to next one. } return self; } /** Frees the tail of the list pointed to by self. It does not free any of the emlements contained in * data. Does not alter self->prev, and doesn't free that either. */ Lilis * lilis_done(Lilis * self ) { if(!self) return NULL; lilis_freetail(self); self->data = NULL; self->next = NULL; return self; } /** Frees a linked list node and it's tail. Returns NULL. */ Lilis * lilis_free(Lilis * self) { lilis_done(self); return mem_free(self); } /** Allocates a new empty linked list. */ Lilis * lilis_alloc() { return STRUCT_ALLOC(Lilis); } /** Initializes a linked list node */ Lilis * lilis_init(Lilis * self, Lilis * next, Lilis * prev, void * data) { if(!self) return NULL; self->data = data; self->next = next; self->prev = prev; return self; } /** Initializes a new linked list node that points to nothing. */ Lilis * lilis_initempty(Lilis * self) { return lilis_init(self, NULL, NULL, NULL); } /** Returns a newly allocatd linked list node. */ Lilis * lilis_new(Lilis * next, Lilis * prev, void * data) { Lilis * res = lilis_alloc(); if(!lilis_init(res, next, prev, data)) { return lilis_free(res); } return res; } /** Returns a newly allocated linked list node that points to nothing. */ Lilis * lilis_newempty() { return lilis_new(NULL, NULL, NULL); } /** Appends a node to the current one, inserting it if needed. * returns other. */ Lilis * lilis_add(Lilis * self, Lilis * other) { Lilis * oldnext = self->next; if ((!self) || (!other)) return NULL; if (oldnext) { oldnext->prev = other; // next one's previous becomes the inserted link other->next = oldnext; // inserted link's next is the old next } // these two belowear always needed even if there is no oldnext. other->prev = self; // and other's previous one is self. self->next = other; // and curent next is the inserted link return other; } /** Creates a new node and adds it */ Lilis * lilis_addnew(Lilis * self, void * data) { Lilis * other = lilis_new(NULL, NULL, data); return lilis_add(self, other); } /** Removes the node that follows self, but does NOT call lilis_free on it. * returns the removed node */ Lilis * lilis_removenext(Lilis * self) { Lilis * next, * nextnext; if(!self) return NULL; next = self->next; if(!next) return NULL; nextnext = next->next; if(nextnext) { nextnext->prev = self; self->next = nextnext; } else { self->next = NULL; } return next; } /** Removes self, modifies prev if this is needed, but does NOT call * lilis_free on it. * returns the removed node */ Lilis * lilis_remove(Lilis * self) { Lilis * prev, * next; if(!self) return NULL; next = self->next; prev = self->prev; if(next) { // link prev node to next node, so this node is cut out. prev->next = next; next->prev = prev; } else { // no next node, prev also gets no next node. prev->next = NULL; } // finally unlink self. self->next = NULL; self->prev = NULL; return next; } /** Removes the node self, and calls lilis_free on it. * returns NULL */ Lilis * lilis_erase(Lilis * self) { Lilis * eraseme = lilis_remove(self); return lilis_free(eraseme); } /** Removes the node that follows self, and calls lilis_free on it. * returns NULL */ Lilis * lilis_erasenext(Lilis * self) { Lilis * eraseme = lilis_removenext(self); return lilis_free(eraseme); } /** Gets the next node in the list. */ Lilis * lilis_next(Lilis * self) { if(!self) return NULL; return self->next; } /** Gets the previous node in the list. */ Lilis * lilis_previous(Lilis * self) { if(!self) return NULL; return self->prev; } /** Gets the data of the list node */ void * lilis_data(Lilis * self) { if(!self) return NULL; return self->data; }