alist.c revision 1618:8c9a4f31d225
1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22/* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27#pragma ident "%Z%%M% %I% %E% SMI" 28 29#include <sgs.h> 30#include <string.h> 31 32/* 33 * Alist manipulation. An Alist is a list of elements formed into an array. 34 * Traversal of the list is an array scan, which because of the locality of 35 * each reference is probably more efficient than a link-list traversal. 36 * 37 * The elements of an Alist are variable length. They can be pointers to 38 * other data structures, or data structures themselves. Traversal of an Alist 39 * thus returns a pointer to each data element. 40 * 41 * Alist elements can be deleted. This involve sliding any following elements 42 * over the element being deleted. ALIST_TRAVERSE() may be employed to traverse 43 * the list, at the same time elements are being deleted. Therefore, the next 44 * element is always determined as an offset from the beginning of the list. 45 */ 46void * 47alist_append(Alist ** alpp, const void * item, size_t size, int cnt) 48{ 49 Alist * alp = *alpp; 50 void * new; 51 52 if (alp == 0) { 53 Aliste bsize, esize = (Aliste)S_ROUND(size, sizeof (void *)); 54 55 /* 56 * First time here, allocate a new Alist. Note that the Alist 57 * al_desc[] entry accounts for one void * already. 58 */ 59 bsize = (Aliste)(sizeof (Alist) - sizeof (void *) + 60 (size * cnt)); 61 if ((alp = malloc((size_t)bsize)) == 0) 62 return (0); 63 alp->al_next = sizeof (Alist) - sizeof (void *); 64 alp->al_end = bsize; 65 alp->al_size = esize; 66 67 } else if (alp->al_next == alp->al_end) { 68 Aliste bsize; 69 70 /* 71 * The list is full, add another block of elements. Determine 72 * the present number of elements and double them. 73 */ 74 bsize = (Aliste)((alp->al_end * 2) - sizeof (Alist) + 75 sizeof (void *)); 76 if ((alp = realloc((void *)alp, (size_t)bsize)) == 0) 77 return (0); 78 alp->al_end = bsize; 79 } 80 81 new = (void *)((char *)alp + alp->al_next); 82 alp->al_next += alp->al_size; 83 84 /* 85 * If a data item has been provided, initialize the current alist entry 86 * with this item. Otherwise, initialize the entry to zero, presumably 87 * the caller will fill this in. 88 */ 89 if (item) 90 (void) memcpy(new, item, alp->al_size); 91 else 92 (void) memset(new, 0, alp->al_size); 93 94 *alpp = alp; 95 return (new); 96} 97 98/* 99 * Delete an element from an Alist. If a count is provided then the caller 100 * already knows what element to remove. Return a decremented count value so 101 * that the caller can continue an ALIST_TRAVERSE scan. 102 */ 103int 104alist_delete(Alist *alp, const void *item, Aliste *offp) 105{ 106 void *addr; 107 Aliste off; 108 109 if (offp) { 110 off = *offp; 111 addr = (void *)((char *)alp + off); 112 } else { 113 for (ALIST_TRAVERSE(alp, off, addr)) { 114 if (memcmp(addr, item, alp->al_size) == 0) 115 break; 116 } 117 } 118 119 if (off >= alp->al_next) 120 return (0); 121 122 /* 123 * If the element to be removed is not the last entry of the array, 124 * slide the following elements over the present element. 125 */ 126 if (off < (alp->al_next -= alp->al_size)) { 127 (void) memmove((void *)addr, (void *)((char *)addr + 128 alp->al_size), (alp->al_next - off)); 129 } 130 131 /* 132 * Reset the new offset, and decrement the callers count control 133 * variable if necessary. Null out the old tail element. 134 */ 135 addr = (void *)((char *)alp + alp->al_next); 136 (void) memset(addr, 0, alp->al_size); 137 138 if (offp) 139 *offp -= alp->al_size; 140 141 return (1); 142} 143 144/* 145 * Generic alist test and append routine. 146 */ 147int 148alist_test(Alist ** alpp, void * ip, size_t size, int cnt) 149{ 150 Aliste off; 151 void ** ipp; 152 153 for (ALIST_TRAVERSE(*alpp, off, ipp)) { 154 if (size == sizeof (uintptr_t)) { 155 if (ip == *ipp) 156 return (ALE_EXISTS); 157 } else { 158 if (memcmp(ip, *ipp, size) == 0) 159 return (ALE_EXISTS); 160 } 161 } 162 163 if (cnt) { 164 if (alist_append(alpp, &ip, size, cnt) == 0) 165 return (0); 166 } 167 return (ALE_CREATE); 168} 169