source: trunk/lib/range_list.c @ 100

Last change on this file since 100 was 100, checked in by tim, 16 years ago

fixed integer underflow bug

updated some documentation

File size: 7.4 KB
Line 
1/*
2 * Copyright (C) 2008 Timothy D. Morgan
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
16 *
17 * $Id: $
18 */
19
20#include <stdio.h>
21#include <math.h>
22#include "../include/range_list.h"
23
24
25/*******************/
26/* Private symbols */
27/*******************/
28#define RANGE_LIST_ALLOC_SIZE 256
29
30
31static void range_list_print(const range_list* rl)
32{
33  uint32_t i;
34  for(i=0; i<rl->size; i++)
35    fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i],
36            rl->elements[i]->offset, rl->elements[i]->length, 
37            rl->elements[i]->data);
38  fprintf(stderr, "\n");
39}
40
41
42/*
43 * Inserts elem into rl at the specified index and updates rl->size.
44 * Memory reallocation of rl->elements is handled when necessary, and
45 * rl->elem_alloced is updated in this case..  Returns false if memory
46 * could not be allocated. 
47 */
48static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index)
49{
50  uint32_t i;
51  range_list_element** tmp;
52
53  if(rl->size == rl->elem_alloced)
54  {
55    tmp = (range_list_element**)realloc(rl->elements, 
56                                        (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)
57                                        * sizeof(range_list_element*));
58    if(tmp == NULL)
59      return false;
60    rl->elements = tmp;
61    rl->elem_alloced += RANGE_LIST_ALLOC_SIZE;
62  }
63
64  /* Do the shuffle to the right. */
65  for(i=rl->size; i > index; i--)
66    rl->elements[i] = rl->elements[i-1];
67  rl->elements[index] = elem;
68
69  rl->size++;
70  return true;
71}
72
73/*
74 * Finds the element with the closest offset to that provided, such that
75 * the element's offset <= the provided offset.  If no such element
76 * exists, this returns -1 which indicates that the provided offset
77 * appears before all elements.
78 */
79static int32_t range_list_find_previous(const range_list* rl, uint32_t offset)
80{
81  uint32_t h_idx, l_idx, cur_idx;
82  uint32_t h_val, l_val;
83  range_list_element* cur_elem;
84
85  if((rl->size == 0) || (offset < rl->elements[0]->offset))
86    return -1;
87
88  if(offset >= rl->elements[rl->size-1]->offset)
89    return rl->size-1;
90
91  h_idx = rl->size-1;
92  l_idx = 0;
93  while(h_idx != l_idx)
94  {
95    h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length;
96    l_val = rl->elements[l_idx]->offset;
97    /* Make an educated guess as to the "middle" index based on the
98     * ratios of the offset and high/low values.
99     */
100    cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx));
101    if(cur_idx > h_idx)
102      cur_idx = h_idx;
103    if(cur_idx < l_idx)
104      cur_idx = l_idx;
105    cur_elem = rl->elements[cur_idx];
106
107    if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset))
108      return cur_idx;
109   
110    if(offset < cur_elem->offset)
111      h_idx = cur_idx-1;
112    else
113      l_idx = cur_idx+1;
114  }
115
116  return h_idx;
117}
118
119
120/******************/
121/* Public symbols */
122/******************/
123range_list* range_list_new()
124{
125  range_list* rl;
126
127  rl = (range_list*)malloc(sizeof(range_list*));
128  if(rl == NULL)
129    return NULL;
130
131  rl->elements = (range_list_element**)malloc(sizeof(range_list_element*)
132                                              * RANGE_LIST_ALLOC_SIZE);
133
134  if(rl->elements == NULL)
135  {
136    free(rl);
137    return NULL;
138  }
139
140  rl->elem_alloced = RANGE_LIST_ALLOC_SIZE;
141  rl->size = 0;
142
143  return rl;
144}
145
146
147void range_list_free(range_list* rl)
148{
149  uint32_t i;
150  for(i=0; i < rl->size; i++)
151    free(rl->elements[i]);
152
153  free(rl->elements);
154  free(rl);
155}
156
157
158uint32_t range_list_size(const range_list* rl)
159{
160  return rl->size;
161}
162
163
164
165bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data)
166{
167  uint32_t insert_index;
168  range_list_element* elem;
169  range_list_element* prev_elem;
170  /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/
171  /* Sorry, limited to 2**31-1 elements. */
172  if(rl->size >= 0x7FFFFFFF)
173    return false;
174
175  /* 0-length ranges aren't allowed. */
176  if(length == 0)
177    return false;
178 
179  /* Check for integer overflows */
180  if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length)
181    return false;
182
183  /* Find insertion point and validate there are no overlaps */
184  insert_index = range_list_find_previous(rl, offset)+1;
185 
186  /* Does the previous element overlap with this one? */
187  if(insert_index > 0)
188  {
189    prev_elem = rl->elements[insert_index-1];
190    if(offset < prev_elem->length + prev_elem->offset)
191      return false;
192  }
193
194  /* Does this new element overlap with the next one? */
195  if((insert_index+1 < rl->size) 
196     && (offset+length > rl->elements[insert_index+1]->offset))
197    return false;
198
199  elem = (range_list_element*)malloc(sizeof(range_list_element));
200  if(elem == NULL)
201    return false;
202  elem->offset = offset;
203  elem->length = length;
204  elem->data = data;
205 
206  if(!range_list_insert(rl, elem, insert_index))
207  {
208    free(elem);
209    return false;
210  }
211
212  return true;
213}
214
215
216bool range_list_remove(range_list* rl, uint32_t index)
217{
218  uint32_t i;
219  range_list_element** tmp;
220
221  if(index >= rl->size)
222    return false;
223
224  free(rl->elements[index]);
225
226  /* Do the shuffle to the left. */
227  for(i=index; i < (rl->size-1); i++)
228    rl->elements[i] = rl->elements[i+1];
229  rl->elements[rl->size-1] = NULL;
230  rl->size--;
231
232  /* Try to keep memory usage down */
233  if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE  < rl->elem_alloced)
234  {
235    tmp = (range_list_element**)realloc(rl->elements, 
236                                        (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)
237                                        * sizeof(range_list_element*));
238    if(tmp != NULL)
239    {
240      rl->elements = tmp;
241      rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE;
242    }
243  }
244
245  return true;
246}
247
248
249const range_list_element* range_list_get(const range_list* rl, uint32_t index)
250{
251  if(index >= rl->size)
252    return NULL;
253
254  return rl->elements[index];
255}
256
257
258int32_t range_list_find(const range_list* rl, uint32_t offset)
259{
260  uint32_t prev_idx;
261  range_list_element* elem;
262
263  if((offset < rl->elements[0]->offset)
264     || (offset > rl->elements[rl->size-1]->offset
265         + rl->elements[rl->size-1]->length))
266    return -1;
267
268  prev_idx = range_list_find_previous(rl, offset);
269  elem = rl->elements[prev_idx];
270  if(offset < elem->offset+elem->length)
271    return prev_idx;
272
273  return -2;
274}
275
276
277void* range_list_find_data(const range_list* rl, uint32_t offset)
278{
279  int32_t index = range_list_find(rl, offset);
280  if(index < 0)
281    return NULL;
282
283  return rl->elements[index]->data;
284}
285
286
287bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
288{
289  range_list_element* cur_elem;
290  range_list_element* new_elem;
291
292  if(index >= rl->size)
293    return false;
294
295  cur_elem = rl->elements[index];
296  if((offset <= cur_elem->offset) 
297     || (offset >= cur_elem->offset+cur_elem->length))
298    return false;
299
300  new_elem = (range_list_element*)malloc(sizeof(range_list_element));
301  if(new_elem == NULL)
302    return false;
303 
304  new_elem->offset = offset;
305  new_elem->length = cur_elem->offset + cur_elem->length - offset;
306  new_elem->data = cur_elem->data;
307 
308  if(!range_list_insert(rl, new_elem, index+1))
309  {
310    free(new_elem);
311    return false;
312  }
313
314  cur_elem->length = new_elem->offset - cur_elem->offset;
315
316  return true;
317}
Note: See TracBrowser for help on using the repository browser.