1 /*- 2 * Copyright (c) 2009 Internet Initiative Japan Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 /**@file 27 * provide list accesses against any pointer 28 */ 29 /* 30 * void **list; 31 * list_size; // allocated size for the list 32 * last_idx; // The last index 33 * first_idx; // The first index 34 * 35 * - first_idx == last_idx means empty. 36 * - 0 <= (fist_idx and last_idx) <= (list_size - 1) 37 * - Allocated size is (last_idx - first_idx) % list_size. 38 * To make the code for checking empty and full simple, we use only 39 * list_size-1 items instead of using the full size. 40 * - XXX Wnen itr_curr is removed... 41 */ 42 #include <sys/types.h> 43 44 #include <stdint.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #include "slist.h" 49 50 #define GROW_SIZE 256 51 #define PTR_SIZE (sizeof(intptr_t)) 52 53 #ifdef SLIST_DEBUG 54 #include <stdio.h> 55 #define SLIST_ASSERT(cond) \ 56 if (!(cond)) { \ 57 fprintf(stderr, \ 58 "\nAssertion failure("#cond") at (%s):%s:%d\n", \ 59 __func__, __FILE__, __LINE__); \ 60 } 61 #else 62 #define SLIST_ASSERT(cond) 63 #endif 64 65 /** 66 * Returns 1 if a index is in the valid range, otherwise returns 0. 67 */ 68 #define VALID_IDX(_list, _idx) \ 69 (((_list)->first_idx <= (_list)->last_idx) \ 70 ? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\ 71 : (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0)) 72 73 /** Convert an index into the internal index */ 74 #define REAL_IDX(_list, _idx) \ 75 (((_list)->first_idx + (_idx)) % (_list)->list_size) 76 77 /** Convert a virtual index into the index */ 78 #define VIRT_IDX(_list, _idx) (((_list)->first_idx <= (_idx)) \ 79 ? (_idx) - (_list)->first_idx \ 80 : (_list)->list_size - (_list)->first_idx + (_idx)) 81 82 /** Decrement an index */ 83 #define DECR_IDX(_list, _memb) \ 84 (_list)->_memb = ((_list)->list_size + --((_list)->_memb)) \ 85 % (_list)->list_size 86 /** Increment an index */ 87 #define INCR_IDX(_list, _memb) \ 88 (_list)->_memb = (++((_list)->_memb)) % (_list)->list_size 89 90 static int slist_grow (slist *); 91 static int slist_grow0 (slist *, int); 92 static __inline void slist_swap0 (slist *, int, int); 93 94 #define itr_is_valid(list) ((list)->itr_next >= 0) 95 #define itr_invalidate(list) ((list)->itr_next = -1) 96 97 /** Initialize a slist */ 98 void 99 slist_init(slist *list) 100 { 101 memset(list, 0, sizeof(slist)); 102 itr_invalidate(list); 103 } 104 105 /** 106 * Specify the size of a list. The size must be specified with the size you 107 * want to use +1. Extra 1 entry is for internal use. The size doesn't shrink. 108 */ 109 int 110 slist_set_size(slist *list, int size) 111 { 112 if (size > list->list_size) 113 return slist_grow0(list, size - list->list_size); 114 115 return 0; 116 } 117 118 /** Finish using. Free the buffers and reinit. */ 119 void 120 slist_fini(slist *list) 121 { 122 if (list->list != NULL) 123 free(list->list); 124 slist_init(list); 125 } 126 127 /** The length of the list */ 128 int 129 slist_length(slist *list) 130 { 131 return 132 (list->first_idx <= list->last_idx) 133 ? (list->last_idx - list->first_idx) 134 : (list->list_size - list->first_idx + list->last_idx); 135 } 136 137 /** Extend the size. Used if the list is full. */ 138 static int 139 slist_grow0(slist *list, int grow_sz) 140 { 141 int size_new; 142 void **list_new = NULL; 143 144 /* just return if it is possible to add one item */ 145 if (slist_length(list) + 1 < list->list_size) 146 /* "+ 1" to avoid the situation list_size == slist_length() */ 147 return 0; 148 149 size_new = list->list_size + grow_sz; 150 if ((list_new = realloc(list->list, PTR_SIZE * size_new)) 151 == NULL) 152 return -1; 153 154 memset(&list_new[list->list_size], 0, 155 PTR_SIZE * (size_new - list->list_size)); 156 157 list->list = list_new; 158 if (list->last_idx < list->first_idx && list->last_idx >= 0) { 159 160 /* 161 * space is created at the right side when center has space, 162 * so move left side to right side 163 */ 164 if (list->last_idx <= grow_sz) { 165 /* 166 * The right side has enough space, so move the left 167 * side to right side. 168 */ 169 memmove(&list->list[list->list_size], 170 &list->list[0], PTR_SIZE * list->last_idx); 171 list->last_idx = list->list_size + list->last_idx; 172 } else { 173 /* 174 * Copy the left side to right side as long as we 175 * can 176 */ 177 memmove(&list->list[list->list_size], 178 &list->list[0], PTR_SIZE * grow_sz); 179 /* Shift the remain to left */ 180 memmove(&list->list[0], &list->list[grow_sz], 181 PTR_SIZE *(list->last_idx - grow_sz)); 182 183 list->last_idx -= grow_sz; 184 } 185 } 186 list->list_size = size_new; 187 188 return 0; 189 } 190 191 static int 192 slist_grow(slist *list) 193 { 194 return slist_grow0(list, GROW_SIZE); 195 } 196 197 /** Add an item to a list */ 198 void * 199 slist_add(slist *list, void *item) 200 { 201 if (slist_grow(list) != 0) 202 return NULL; 203 204 list->list[list->last_idx] = item; 205 206 if (list->itr_next == -2) { 207 /* the iterator points the last, update it. */ 208 list->itr_next = list->last_idx; 209 } 210 211 INCR_IDX(list, last_idx); 212 213 return item; 214 } 215 216 #define slist_get0(list_, idx) ((list_)->list[REAL_IDX((list_), (idx))]) 217 218 /** Add all items in add_items to a list. */ 219 int 220 slist_add_all(slist *list, slist *add_items) 221 { 222 int i, n; 223 224 n = slist_length(add_items); 225 for (i = 0; i < n; i++) { 226 if (slist_add(list, slist_get0(add_items, i)) == NULL) 227 return 1; 228 } 229 230 return 0; 231 } 232 233 /** Return "idx"th item. */ 234 void * 235 slist_get(slist *list, int idx) 236 { 237 SLIST_ASSERT(idx >= 0); 238 SLIST_ASSERT(slist_length(list) > idx); 239 240 if (idx < 0 || slist_length(list) <= idx) 241 return NULL; 242 243 return slist_get0(list, idx); 244 } 245 246 /** Store a value in "idx"th item */ 247 int 248 slist_set(slist *list, int idx, void *item) 249 { 250 SLIST_ASSERT(idx >= 0); 251 SLIST_ASSERT(slist_length(list) > idx); 252 253 if (idx < 0 || slist_length(list) <= idx) 254 return -1; 255 256 list->list[REAL_IDX(list, idx)] = item; 257 258 return 0; 259 } 260 261 /** Remove the 1st entry and return it. */ 262 void * 263 slist_remove_first(slist *list) 264 { 265 void *oldVal; 266 267 if (slist_length(list) <= 0) 268 return NULL; 269 270 oldVal = list->list[list->first_idx]; 271 272 if (itr_is_valid(list) && list->itr_next == list->first_idx) 273 INCR_IDX(list, itr_next); 274 275 if (!VALID_IDX(list, list->itr_next)) 276 itr_invalidate(list); 277 278 INCR_IDX(list, first_idx); 279 280 return oldVal; 281 } 282 283 /** Remove the last entry and return it */ 284 void * 285 slist_remove_last(slist *list) 286 { 287 if (slist_length(list) <= 0) 288 return NULL; 289 290 DECR_IDX(list, last_idx); 291 if (!VALID_IDX(list, list->itr_next)) 292 itr_invalidate(list); 293 294 return list->list[list->last_idx]; 295 } 296 297 /** Remove all entries */ 298 void 299 slist_remove_all(slist *list) 300 { 301 void **list0 = list->list; 302 303 slist_init(list); 304 305 list->list = list0; 306 } 307 308 /* Swap items. This doesn't check boudary. */ 309 static __inline void 310 slist_swap0(slist *list, int m, int n) 311 { 312 void *m0; 313 314 itr_invalidate(list); /* Invalidate iterator */ 315 316 m0 = list->list[REAL_IDX(list, m)]; 317 list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)]; 318 list->list[REAL_IDX(list, n)] = m0; 319 } 320 321 /** Swap between mth and nth */ 322 void 323 slist_swap(slist *list, int m, int n) 324 { 325 int len; 326 327 len = slist_length(list); 328 SLIST_ASSERT(m >= 0); 329 SLIST_ASSERT(n >= 0); 330 SLIST_ASSERT(len > m); 331 SLIST_ASSERT(len > n); 332 333 if (m < 0 || n < 0) 334 return; 335 if (m >= len || n >= len) 336 return; 337 338 slist_swap0(list, m, n); 339 } 340 341 /** Remove "idx"th item */ 342 void * 343 slist_remove(slist *list, int idx) 344 { 345 int first, last, idx0, reset_itr; 346 void *oldVal; 347 348 SLIST_ASSERT(idx >= 0); 349 SLIST_ASSERT(slist_length(list) > idx); 350 351 if (idx < 0 || slist_length(list) <= idx) 352 return NULL; 353 354 idx0 = REAL_IDX(list, idx); 355 oldVal = list->list[idx0]; 356 reset_itr = 0; 357 358 first = -1; 359 last = -1; 360 361 if (list->itr_next == idx0) { 362 INCR_IDX(list, itr_next); 363 if (!VALID_IDX(list, list->itr_next)) 364 list->itr_next = -2; /* on the last item */ 365 } 366 367 /* should we reduce the last side or the first side? */ 368 if (list->first_idx < list->last_idx) { 369 /* take the smaller side */ 370 if (idx0 - list->first_idx < list->last_idx - idx0) { 371 first = list->first_idx; 372 INCR_IDX(list, first_idx); 373 } else { 374 last = list->last_idx; 375 DECR_IDX(list, last_idx); 376 } 377 } else { 378 /* 379 * 0 < last (unused) first < idx < size, so let's reduce the 380 * first. 381 */ 382 if (list->first_idx <= idx0) { 383 first = list->first_idx; 384 INCR_IDX(list, first_idx); 385 } else { 386 last = list->last_idx; 387 DECR_IDX(list, last_idx); 388 } 389 } 390 391 /* the last side */ 392 if (last != -1 && last != 0 && last != idx0) { 393 394 /* move left the items that is from idx0 to the last */ 395 if (itr_is_valid(list) && 396 idx0 <= list->itr_next && list->itr_next <= last) { 397 DECR_IDX(list, itr_next); 398 if (!VALID_IDX(list, list->itr_next)) 399 itr_invalidate(list); 400 } 401 402 memmove(&list->list[idx0], &list->list[idx0 + 1], 403 (PTR_SIZE) * (last - idx0)); 404 } 405 /* the first side */ 406 if (first != -1 && first != idx0) { 407 408 /* move right the items that is from first to the idx0 */ 409 if (itr_is_valid(list) && 410 first <= list->itr_next && list->itr_next <= idx0) { 411 INCR_IDX(list, itr_next); 412 if (!VALID_IDX(list, list->itr_next)) 413 itr_invalidate(list); 414 } 415 416 memmove(&list->list[first + 1], &list->list[first], 417 (PTR_SIZE) * (idx0 - first)); 418 } 419 if (list->first_idx == list->last_idx) { 420 list->first_idx = 0; 421 list->last_idx = 0; 422 } 423 424 return oldVal; 425 } 426 427 /** 428 * Shuffle items. 429 * slist_shuffle() uses random(3). Call srandom(3) before use it. 430 */ 431 void 432 slist_shuffle(slist *list) 433 { 434 int i, len; 435 436 len = slist_length(list); 437 for (i = len; i > 1; i--) 438 slist_swap0(list, i - 1, (int)(random() % i)); 439 } 440 441 /** Init an iterator. Only one iterator exists. */ 442 void 443 slist_itr_first(slist *list) 444 { 445 list->itr_next = list->first_idx; 446 if (!VALID_IDX(list, list->itr_next)) 447 itr_invalidate(list); 448 } 449 450 /** 451 * Return whether a iterator can go to the next item. 452 * @return Return 1 if the iterator can return the next item. 453 * Return 0 it reaches the end of the list or the list is modified 454 * destructively. 455 */ 456 int 457 slist_itr_has_next(slist *list) 458 { 459 if (list->itr_next < 0) 460 return 0; 461 return VALID_IDX(list, list->itr_next); 462 } 463 464 /** Return the next item and iterate to the next */ 465 void * 466 slist_itr_next(slist *list) 467 { 468 void *rval; 469 470 if (!itr_is_valid(list)) 471 return NULL; 472 SLIST_ASSERT(VALID_IDX(list, list->itr_next)); 473 474 if (list->list == NULL) 475 return NULL; 476 477 rval = list->list[list->itr_next]; 478 list->itr_curr = list->itr_next; 479 INCR_IDX(list, itr_next); 480 481 if (!VALID_IDX(list, list->itr_next)) 482 list->itr_next = -2; /* on the last item */ 483 484 return rval; 485 } 486 487 /** Delete the current iterated item */ 488 void * 489 slist_itr_remove(slist *list) 490 { 491 SLIST_ASSERT(list != NULL); 492 493 return slist_remove(list, VIRT_IDX(list, list->itr_curr)); 494 } 495