/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1996 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "pkglib.h" /* * This is the module responsible for allocating and maintaining lists that * require allocation of memory. For certain lists, large chunks are * allocated once to contain a large number of entries in each chunk (bl_* * for block list). The other approach involves the augmentation of linked * lists, each entry of which is alloc'd individually. */ #define ERR_CS_ALLOC "ERROR: Cannot allocate control structure for %s array." #define ERR_MEM_ALLOC "ERROR: Cannot allocate memory for %s array." #define MAX_ARRAYS 50 #define ARRAY_END(x) (bl_cs_array[x]->cur_segment->avail_ptr) #define REC_SIZE(x) (bl_cs_array[x]->struct_size) #define EOSEG(x) (bl_cs_array[x]->cur_segment->eoseg_ptr) #define GET_AVAIL(x) (ARRAY_END(x) + REC_SIZE(x)) struct alloc_seg { char *seg_ptr; /* ptr to the allocated block */ char *avail_ptr; /* ptr to the next available list element */ char *eoseg_ptr; /* last byte in the segment */ int full; /* segment has no available space */ struct alloc_seg *next; /* next record */ }; struct blk_list_cs { int struct_size; /* size of a single list element */ int count_per_block; /* number of list elements per block */ int block_size; /* just to save time - alloc size */ int data_handle; /* list_handle for pointer array */ struct alloc_seg *alloc_segs; /* memory pool */ struct alloc_seg *cur_segment; /* the current allocated segment */ int total_elem; /* total elements stored */ int contiguous; /* use realloc to grow */ char *desc; /* description of the list */ }; static struct blk_list_cs *bl_cs_array[MAX_ARRAYS]; static int next_array_elem; /* Support functions */ static int invalid_handle(int list_handle) { if (list_handle < 0 || list_handle >= next_array_elem) return (1); return (0); } static int invalid_record(int list_handle, int recno) { if (invalid_handle(list_handle)) return (1); if (recno < 0 || recno > bl_cs_array[list_handle]->total_elem) return (1); return (0); } static void free_list(int list_handle) { struct blk_list_cs *bl_ptr; struct alloc_seg *segstr_ptr, *nextstr_ptr; /* Make sure this wasn't free'd earlier */ if (bl_cs_array[list_handle] == NULL) return; bl_ptr = bl_cs_array[list_handle]; /* First free the alloc_seg list. */ segstr_ptr = bl_ptr->alloc_segs; if (segstr_ptr) { do { nextstr_ptr = segstr_ptr->next; /* Free the memory block. */ free((void *)segstr_ptr->seg_ptr); /* Free the control structure. */ free((void *)segstr_ptr); segstr_ptr = nextstr_ptr; } while (segstr_ptr); } /* Free the block control structure. */ free((void *)bl_ptr->desc); free((void *)bl_ptr); bl_cs_array[list_handle] = NULL; } /* Allocate another alloc_seg structure. */ static int alloc_next_seg(struct blk_list_cs *bl_ptr) { struct alloc_seg *new_alloc_cs; if (bl_ptr->contiguous) { int offset_to_avail, seg_size, new_size; struct alloc_seg *alloc_segment; if (bl_ptr->alloc_segs) { alloc_segment = bl_ptr->alloc_segs; offset_to_avail = (alloc_segment->avail_ptr - alloc_segment->seg_ptr); seg_size = (alloc_segment->eoseg_ptr - alloc_segment->seg_ptr); new_size = (seg_size + bl_ptr->block_size); } else { if ((bl_ptr->alloc_segs = (struct alloc_seg *)calloc(1, sizeof (struct alloc_seg))) == NULL) { logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ? bl_ptr->desc : "an unknown")); return (0); } alloc_segment = bl_ptr->alloc_segs; offset_to_avail = 0; seg_size = 0; new_size = bl_ptr->block_size; } bl_ptr->cur_segment = alloc_segment; if ((alloc_segment->seg_ptr = (char *)realloc((void *)alloc_segment->seg_ptr, (unsigned)new_size)) == NULL) { logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ? bl_ptr->desc : "an unknown")); return (0); } alloc_segment->next = NULL; /* reset the status */ alloc_segment->full = 0; /* readjust the original pointers */ alloc_segment->avail_ptr = alloc_segment->seg_ptr + offset_to_avail; alloc_segment->eoseg_ptr = alloc_segment->seg_ptr + new_size; (void) memset(alloc_segment->avail_ptr, '\000', bl_ptr->block_size); } else { /* Allocate the control structure and link it into the list. */ if ((new_alloc_cs = (struct alloc_seg *)malloc( sizeof (struct alloc_seg))) == NULL) { logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ? bl_ptr->desc : "an unknown")); return (0); } if (bl_ptr->alloc_segs == NULL) { /* * If this is the first allocation, then initialize * the head pointer and set cur_segment to this first * block of memory. */ bl_ptr->alloc_segs = new_alloc_cs; } else { /* * Otherwise, point the current cur_segment to the * next one and then point to the new one. */ bl_ptr->cur_segment->next = new_alloc_cs; } new_alloc_cs->next = NULL; bl_ptr->cur_segment = new_alloc_cs; new_alloc_cs->full = 0; /* Now allocate the block of memory that this controls. */ if ((new_alloc_cs->seg_ptr = calloc(bl_ptr->count_per_block, bl_ptr->struct_size)) == NULL) { logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ? bl_ptr->desc : "an unknown")); return (0); } new_alloc_cs->avail_ptr = new_alloc_cs->seg_ptr; new_alloc_cs->eoseg_ptr = (new_alloc_cs->seg_ptr + bl_ptr->block_size); } return (1); } /* * These first functions (beginning with bl_*) manage simple block lists. The * pointers returned, may get lost if they aren't assigned to an array or * something. While individual records can be obtained by record number, the * process isn't very efficient. Look to the array management section * (ar_*)for an easily administrable list. */ /* * Create a block list. Allocate memory for a block list structure and * initialize that structure. This doesn't actually allocate memory for the * list yet, just the controlling data structure. Returns -1 on failure and a * valid block list handle otherwise. * * NOTE: At the time of writing, it was not seen as important to recover block * pointers made available with a bl_free() (two of these at most in * pkginstall). If this became important later, we could trade efficiency for * speed by ignoring next_array_elem and actually scanning through the array * for a NULL pointer and then return that. */ int bl_create(int count_per_block, int struct_size, char *desc) { struct blk_list_cs *bl_ptr; int retval; if ((bl_cs_array[next_array_elem] = (struct blk_list_cs *)calloc(1, sizeof (struct blk_list_cs))) == NULL) { logerr(gettext(ERR_CS_ALLOC), (desc ? desc : "an unknown")); return (-1); } bl_ptr = bl_cs_array[next_array_elem]; retval = next_array_elem++; bl_ptr->data_handle = -1; bl_ptr->struct_size = struct_size; bl_ptr->count_per_block = count_per_block; bl_ptr->block_size = (count_per_block * struct_size); bl_ptr->desc = strdup((desc ? desc : "unknown")); return (retval); } /* * Get the next available entry in the list. This will allocate memory as * required based on the initialization values in bl_create(). Returns a * pointer to the allocated memory segment or NULL if operation was not * possible. */ char * bl_next_avail(int list_handle) { struct blk_list_cs *bl_ptr; char *retval; if (invalid_handle(list_handle)) return (NULL); bl_ptr = bl_cs_array[list_handle]; /* * Allocate more memory if none is allocated yet or our last access * filled the allotted segment. */ if (bl_ptr->cur_segment == NULL || bl_ptr->cur_segment->full) if (!alloc_next_seg(bl_ptr)) return (NULL); /* Get the correct pointer. */ retval = bl_ptr->cur_segment->avail_ptr; /* Advance it and mark if full. */ bl_ptr->cur_segment->avail_ptr += bl_ptr->struct_size; bl_ptr->total_elem++; if (bl_ptr->cur_segment->avail_ptr >= bl_ptr->cur_segment->eoseg_ptr) bl_ptr->cur_segment->full = 1; return (retval); } char * bl_get_record(int list_handle, int recno) { struct blk_list_cs *bl_ptr; struct alloc_seg *cur_as_ptr; int cur_rec = 0; if (invalid_record(list_handle, recno)) return (NULL); bl_ptr = bl_cs_array[list_handle]; cur_as_ptr = bl_ptr->alloc_segs; while (recno > (cur_rec + bl_ptr->count_per_block)) { cur_as_ptr = cur_as_ptr->next; if (cur_as_ptr == NULL) return (NULL); cur_rec += bl_ptr->count_per_block; } /* * Now cur_as_ptr points to the allocated segment bearing the * intended record and all we do now is move down that by the * remaining record lengths. */ return ((char *)cur_as_ptr + ((recno - cur_rec) * bl_ptr->struct_size)); } void bl_free(int list_handle) { int cur_handle; if (list_handle == -1) { for (cur_handle = 0; cur_handle < next_array_elem; cur_handle++) { free_list(cur_handle); } } else { if (invalid_handle(list_handle)) return; free_list(list_handle); } } /* * These are the array management functions. They insert into (and can return * a pointer to) a contiguous list of pointers to stuff. This keeps * everything together in a very handy package and is very similar in * appearance to the arrays created by the old AT&T code. The method for * presenting the interface is entirely different, however. */ /* * This constructs, maintains and returns pointers into a growable array of * pointers to structures of the form * struct something *array[n] * The last element in the array is always NULL. */ int ar_create(int count_per_block, int struct_size, char *desc) { int data_handle, retval; char ar_desc[60]; struct blk_list_cs *array_ptr; if ((data_handle = bl_create(count_per_block, struct_size, desc)) == -1) return (-1); sprintf(ar_desc, "%s pointer", desc); if ((retval = bl_create(count_per_block, sizeof (char *), ar_desc)) == -1) return (-1); array_ptr = bl_cs_array[retval]; array_ptr->contiguous = 1; array_ptr->data_handle = data_handle; return (retval); } /* Return a pointer to the first element in the array. */ char ** ar_get_head(int list_handle) { if (invalid_handle(list_handle) || bl_cs_array[list_handle]->alloc_segs == NULL) return (NULL); return ((char **)bl_cs_array[list_handle]->alloc_segs->seg_ptr); } /* * Free up the entry in the array indicated by index, but hold onto it for * future use. */ int ar_delete(int list_handle, int index) { char **array; char *deleted_rec; int i; struct blk_list_cs *list_ptr, *data_ptr; if ((array = ar_get_head(list_handle)) == NULL) return (0); if (invalid_record(list_handle, index)) return (0); /* Get the pointer to the array control structure. */ list_ptr = bl_cs_array[list_handle]; if (!(list_ptr->contiguous)) return (0); /* This isn't an array. */ data_ptr = bl_cs_array[list_ptr->data_handle]; /* * Since this looks just like an array. Record the pointer being * deleted for insertion into the avail list at the end and move all * elements below it up one. */ deleted_rec = array[index]; for (i = index; array[i] != NULL; i++) array[i] = array[i+1]; /* * Now insert the deleted entry into the avails list after the NULL * and adjust the avail_ptr to point to the NULL again. */ array[i] = deleted_rec; list_ptr->alloc_segs->avail_ptr -= list_ptr->struct_size; /* Adjust other entries in the control structure. */ list_ptr->alloc_segs->full = 0; list_ptr->total_elem -= 1; /* Clear the deleted data area. */ (void) memset(deleted_rec, '\000', data_ptr->struct_size); return (1); } /* * Return a new pointer to a structure pointer. Find an available element in * the array and point it at an available element in the data pool * constructed of block lists. Allocate new memory as necessary. */ char ** ar_next_avail(int list_handle) { struct blk_list_cs *array_ptr; char *data_area, **pointer_area; if (invalid_handle(list_handle) || !(bl_cs_array[list_handle]->contiguous) || invalid_handle(bl_cs_array[list_handle]->data_handle)) return (NULL); array_ptr = bl_cs_array[list_handle]; /* * First see if an avail has already been allocated (it will be right * after the NULL termination of the array if it exists). Return * that, if found. */ if ((bl_cs_array[list_handle]->cur_segment != NULL) && (ARRAY_END(list_handle) + REC_SIZE(list_handle) < EOSEG(list_handle)) && (*(pointer_area = (char **) GET_AVAIL(list_handle)) != NULL)) { /* We can reclaim a previous deletion. */ data_area = *pointer_area; *(char **)(ARRAY_END(list_handle)) = data_area; /* reactivate */ *pointer_area-- = NULL; /* new end */ array_ptr->cur_segment->avail_ptr += array_ptr->struct_size; array_ptr->total_elem++; } else { /* * Get the data area first. This is the record we're pointing * to from the array. */ data_area = bl_next_avail(array_ptr->data_handle); /* Now get the next pointer from the pointer array. */ pointer_area = (char **) bl_next_avail(list_handle); *pointer_area = data_area; /* * The array must be NULL terminated. So, if the block list * structure is full, we have to grow it without resetting * the avail pointer. NOTE: This will only work for a * contiguous list! */ if (bl_cs_array[list_handle]->alloc_segs->full) { char **old_list_pointer, **new_list_pointer; /* * First grab the old numbers in case realloc() moves * everything. */ old_list_pointer = ar_get_head(list_handle); /* * Now allocate additional contiguous memory, moving * the original block if necessary. */ if (!alloc_next_seg(array_ptr)) return (NULL); /* * Now determine if everything moved and readjust the * pointer_area if required. */ new_list_pointer = ar_get_head(list_handle); if (old_list_pointer != new_list_pointer) { pointer_area += (new_list_pointer - old_list_pointer); } } } return (pointer_area); } /* * Relinquish the array back to the memory pool. Note that there is no method * provided to free *all* arrays. */ void ar_free(int list_handle) { if (invalid_handle(list_handle)) return; bl_free(bl_cs_array[list_handle]->data_handle); bl_free(list_handle); }