/* * ara.c -- a resizable array * * Copyright 2022 EPIC Software Labs * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. All redistributions, whether of source code or in binary form must * retain the the above copyright notice, the above paragraph (the one * permitting redistribution), this list of conditions, and the following * disclaimer. * 2. The names of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" #include "ara.h" #include "ircaux.h" #include "output.h" static void ara_insert (ara *a, int location); static void ara_delete (ara *a, int location); static void ara_enlarge_list (ara *a, size_t new_size); /* * Make 'location' available, by moving (location, end) to (location+1, end+1) * Does not change (0, location - 1) * Changes (location) to NULL * * If 'a' is "full" it is enlarged by 10 spots first. * If 'location' is invalid */ static void ara_insert (ara *a, int location) { size_t i; if (location < 0 || (size_t)location >= a->size) return; else if (a->size == 0 || a->list == NULL) ara_enlarge_list(a, a->size + 10); else if (a->type == ARA_TYPE_DATA && a->list[a->size - 1].d != NULL) ara_enlarge_list(a, a->size + 10); else if (a->list[a->size - 1].f != NULL) ara_enlarge_list(a, a->size + 10); for (i = a->size; i > (size_t)location; i--) a->list[i] = a->list[i - 1]; i = location; if (a->type == ARA_TYPE_DATA) a->list[i].d = NULL; else a->list[i].f = NULL; } /* * Fill in a gap at 'location' by moving (location + 1, end) to (location, end-1) * Does not change (0, location - 1) * Obviously it overwites (location) */ static void ara_delete (ara *a, int location) { if (location < 0 || (size_t)location >= a->size) return; if (a->size == 0 || a->list == NULL) return; /* XXX TODO XXX */ } static void ara_enlarge_list (ara *a, size_t new_size) { size_t r; AraItem *new_list; AraItem *old_list; new_list = (AraItem *)new_malloc(sizeof(AraItem) * new_size); if (a->type == ARA_TYPE_DATA) { for (r = 0; r < a->size; r++) new_list[r].d = a->list[r].d; for (r = a->size; r < new_size; r++) new_list[r].d = NULL; } else { for (r = 0; r < a->size; r++) new_list[r].f = a->list[r].f; for (r = a->size; r < new_size; r++) new_list[r].f = NULL; } old_list = a->list; a->list = new_list; a->size = new_size; if (old_list) new_free((char **)&old_list); } /* * Copy 'one' to 'two'. */ static void ara_copy (ara *one, ara *two) { size_t i; if (two->size < one->size) ara_enlarge_list(two, one->size + 10); for (i = 0; i < one->size; i++) two[i] = one[i]; } void init_ara (ara *a, int type, size_t maximum_location) { a->list = NULL; a->size = 0; a->type = type; ara_enlarge_list(a, maximum_location + 1); } /* * Set the 'location'th element in 'a' to 'data' * If errcode is not NULL: * 1 - The value was successfully set * 0 - The value was not successfully set because there was already a value there * -1 - 'a' was NULL or the value of 'location' was invalid (not successful) */ void ara_set_item (ara *a, int location, AraItem data, int *errorcode) { size_t idx; if (!a || location < 0) { *errorcode = -1; return; } idx = (size_t)location; if (idx >= a->size) ara_enlarge_list(a, location + 10); if (a->type == ARA_TYPE_DATA) { if (a->list[idx].d != NULL) { *errorcode = 0; } else { a->list[idx].d = data.d; *errorcode = 1; } } else { if (a->list[idx].f != NULL) { *errorcode = 0; } else { a->list[idx].f = data.f; *errorcode = 1; } } } /* * Return the 'location'th element in 'a' * If errcode is not NULL: * 1 - The value 'location' is valid and contains a value (returned) * 0 - The value 'location' is valid but it had no value (returned NULL) * -1 - 'a' was NULL or the value of 'location' was invalid (returned NULL) */ AraItem ara_get_item (ara *a, int location, int *errorcode) { size_t idx; AraItem nothing; if (!a || location < 0 || (size_t)location >= a->size) { *errorcode = -1; if (!a || a->type == ARA_TYPE_DATA) nothing.d = NULL; else nothing.f = NULL; return nothing; } idx = (size_t)location; if (a->type == ARA_TYPE_DATA) { if (a->list[idx].d == NULL) *errorcode = 0; else *errorcode = 1; } else { if (a->list[idx].f == NULL) *errorcode = 0; else *errorcode = 1; } return a->list[idx]; } /* * Set the 'location'th element in 'a' to 'data' and return the previous value * If errcode is not NULL: * 1 - The value was successfully updated (previous value returned) * 0 - * -1 - 'a' was NULL or the value of 'location' was invalid */ AraItem ara_update_item (ara *a, int location, AraItem data, int *errcode) { size_t idx; AraItem retval; if (!a || location < 0 || (size_t)location >= a->size) { *errcode = -1; if (!a || a->type == ARA_TYPE_DATA) retval.d = NULL; else retval.f = NULL; return retval; } idx = (size_t)location; if (a->type == ARA_TYPE_DATA) { retval.d = a->list[idx].d; a->list[idx].d = data.d; } else { retval.f = a->list[idx].f; a->list[idx].f = data.f; } *errcode = 1; return retval; } /* * Clear the 'location'th element in 'a' and return the previous value * If errcode is not NULL: * 1 - The value was successfully removed (previous value returned) * 0 - The value was not cleared because there was no previous value (returned NULL) * -1 - 'a' was NULL or the value of 'location' was invalid */ AraItem ara_remove_item (ara *a, int location, int *errcode) { AraItem retval; retval = ara_get_item(a, location, errcode); if (*errcode <= 0) { if (a->type == ARA_TYPE_DATA) retval.d = NULL; else retval.f = NULL; } else { if (a->type == ARA_TYPE_DATA) a->list[location].d = NULL; else a->list[location].f = NULL; *errcode = 1; } return retval; } /* * Return the next element in 'ara' after 'location' * If *location == -1 -> begin at ara location 0 (to start iterating) * Otherwise -> begin at *location + 1 * Returns: * -1 - 'a' was NULL or 'location' was NULL * 0 - There were no further values (*location is not changed - do not use) * 1 - The value in "*location" was set -- keep calling! */ int traverse_all_ara_items (ara *a, int *location) { if (!a || a->size <= 0 || !a->list || (*location) < -1) return -1; for (;;) { if ((size_t)*location >= a->size - 1) return 0; (*location)++; if (a->type == ARA_TYPE_DATA) { if (a->list[*location].d != NULL) return 1; } else { if (a->list[*location].f != NULL) return 1; } } } /* * Return the maximum valid value for 'location' in 'a' * Returns: * -1 - 'a' is NULL, or there are no valid locations in 'a' * >= 0 - The maximum value you may pass as 'location' * (Note: This is not the number of items in the * ara! If there is only one item, then its * location is '0'!) */ int ara_max (ara *a) { size_t idx; if (!a || a->size == 0 || a->list == NULL) return -1; for (idx = a->size - 1; ; idx--) { if (a->type == ARA_TYPE_DATA && a->list[idx].d != NULL) return idx; else if (a->type == ARA_TYPE_FUNC && a->list[idx].f != NULL) return idx; if (idx == 0) return -1; } return -1; } /* * Return the number of valid values in 'a' * Returns: * -1 - 'a' is NULL * >0 - The number of non-null 'locations' in 'a' that have been set */ int ara_number (ara *a) { size_t idx; size_t count = 0; if (!a) return -1; for (idx = count = 0; idx < a->size; idx++) { if (a->type == ARA_TYPE_DATA && a->list[idx].d != NULL) count++; else if (a->type == ARA_TYPE_FUNC && a->list[idx].f != NULL) count++; } return count; } /* Return the location of 'item' in ara, or -1 if not found */ int ara_find_item (ara *a, AraItem item) { size_t idx; if (!a) return -1; for (idx = 0; idx < a->size; idx++) { if (a->type == ARA_TYPE_DATA && a->list[idx].d == item.d) return (int)idx; else if (a->type == ARA_TYPE_FUNC && a->list[idx].f == item.f) return (int)idx; } return -1; } /* Add 'new_item' to location 0 in ara; push everything else up one */ int ara_unshift (ara *a, AraItem new_item) { int errorcode = 0; ara_insert(a, 0); ara_set_item(a, 0, new_item, &errorcode); return errorcode; } /* Remove+return location 0 and shift everything down one */ AraItem ara_shift (ara *a) { AraItem retval; int errcode = 0; retval = ara_get_item(a, 0, &errcode); ara_delete(a, 0); return retval; } /* Add 'new_item' to the end of ara */ int ara_push (ara *a, AraItem new_item) { int location; int errcode = 0; if ((location = ara_max(a)) < 0) { ara_enlarge_list(a, 10); ara_set_item(a, 0, new_item, &errcode); } else ara_set_item(a, location, new_item, &errcode); return errcode; } /* Remove+return the last item in the ara */ AraItem ara_pop (ara *a) { int location; AraItem retval; if ((location = ara_max(a)) < 0) { if (a->type == ARA_TYPE_DATA) retval.d = NULL; else retval.f = NULL; return retval; } else return ara_pop_item(a, location); } /* Remove+return the 'location'th item in the ara */ AraItem ara_pop_item (ara *a, int location) { AraItem retval; int errcode = 0; retval = ara_get_item(a, location, &errcode); ara_delete(a, location); return retval; } /* Insert 'new_item' to 'location'th spot, move everything after location down one spot */ int ara_insert_item (ara *a, int location, AraItem new_item) { int errcode = 0; ara_insert(a, location); ara_set_item(a, location, new_item, &errcode); return errcode; } /* Insert 'new_item' before 'existing_item', moving everything down one spot */ int ara_insert_before_item (ara *a, AraItem existing_item, AraItem new_item) { int errcode = 0; int location; if ((location = ara_find_item(a, existing_item)) < 0) return -1; ara_insert(a, location); ara_set_item(a, location, new_item, &errcode); return errcode; } /* Insert 'new_item' after 'existing_item', moving everything down one spot */ int ara_insert_after_item (ara *a, AraItem existing_item, AraItem new_item) { int errcode = 0; int location; if ((location = ara_find_item(a, existing_item)) < 0) return -1; ara_insert(a, location + 1); ara_set_item(a, location + 1, new_item, &errcode); return errcode; } /* Find any gaps in the ara and fill them in */ int ara_collapse (ara *a) { size_t idx; if (!a) return -1; for (idx = 0; idx < a->size; idx++) { if (a->type == ARA_TYPE_DATA) { if (a->list[idx].d == NULL) ara_delete(a, idx); } else { if (a->list[idx].f == NULL) ara_delete(a, idx); } } return 1; }