source: trunk/lib/range_list.c @ 217

Last change on this file since 217 was 169, checked in by tim, 15 years ago

filled in additional, minimal documentation

  • Property svn:keywords set to Id
File size: 7.7 KB
Line 
1/*
2 * Copyright (C) 2008-2010 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: range_list.c 169 2010-03-03 19:24:58Z tim $
18 */
19
20/**
21 * @file
22 */
23
24#include "range_list.h"
25
26
27/*******************/
28/* Private symbols */
29/*******************/
30#define RANGE_LIST_ALLOC_SIZE 256
31
32#if 0 /* For debugging */
33#include <stdio.h>
34static void range_list_print(const range_list* rl)
35{
36  uint32_t i;
37  for(i=0; i<rl->size; i++)
38    fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i],
39            rl->elements[i]->offset, rl->elements[i]->length,
40            rl->elements[i]->data);
41  fprintf(stderr, "\n");
42}
43#endif
44
45/*
46 * Inserts elem into rl at the specified index and updates rl->size.
47 * Memory reallocation of rl->elements is handled when necessary, and
48 * rl->elem_alloced is updated in this case..  Returns false if memory
49 * could not be allocated. 
50 */
51static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index)
52{
53  uint32_t i;
54  range_list_element** tmp;
55
56  if(rl->size == rl->elem_alloced)
57  {
58    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
59                         (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE));
60    if(tmp == NULL)
61      return false;
62    rl->elements = tmp;
63    rl->elem_alloced += RANGE_LIST_ALLOC_SIZE;
64  }
65
66  /* Do the shuffle to the right. */
67  for(i=rl->size; i > index; i--)
68    rl->elements[i] = rl->elements[i-1];
69  rl->elements[index] = elem;
70
71  rl->size++;
72  return true;
73}
74
75/*
76 * Finds the element with the closest offset to that provided, such that
77 * the element's offset <= the provided offset.  If no such element
78 * exists, this returns -1 which indicates that the provided offset
79 * appears before all elements.
80 */
81static int32_t range_list_find_previous(const range_list* rl, uint32_t offset)
82{
83  uint32_t h_idx, l_idx, cur_idx;
84  uint32_t h_val, l_val;
85  range_list_element* cur_elem;
86
87  if((rl->size == 0) || (offset < rl->elements[0]->offset))
88    return -1;
89
90  if(offset >= rl->elements[rl->size-1]->offset)
91    return rl->size-1;
92
93  h_idx = rl->size-1;
94  l_idx = 0;
95  while(h_idx != l_idx)
96  {
97    h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length;
98    l_val = rl->elements[l_idx]->offset;
99    /* Make an educated guess as to the "middle" index based on the
100     * ratios of the offset and high/low values.
101     */
102    cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx));
103    if(cur_idx > h_idx)
104      cur_idx = h_idx;
105    if(cur_idx < l_idx)
106      cur_idx = l_idx;
107    cur_elem = rl->elements[cur_idx];
108
109    if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset))
110      return cur_idx;
111   
112    if(offset < cur_elem->offset)
113      h_idx = cur_idx-1;
114    else
115      l_idx = cur_idx+1;
116  }
117
118  return h_idx;
119}
120
121
122/******************/
123/* Public symbols */
124/******************/
125range_list* range_list_new()
126{
127  range_list* rl;
128
129  rl = talloc(NULL, range_list);
130  if(rl == NULL)
131    return NULL;
132
133  rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE);
134  if(rl->elements == NULL)
135  {
136    talloc_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  if(rl != NULL)
150    talloc_free(rl);
151}
152
153
154uint32_t range_list_size(const range_list* rl)
155{
156  return rl->size;
157}
158
159
160
161bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data)
162{
163  uint32_t insert_index;
164  range_list_element* elem;
165  range_list_element* prev_elem;
166  /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/
167  /* Sorry, limited to 2**31-1 elements. */
168  if(rl->size >= 0x7FFFFFFF)
169    return false;
170
171  /* 0-length ranges aren't allowed. */
172  if(length == 0)
173    return false;
174 
175  /* Check for integer overflows */
176  if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length)
177    return false;
178
179  /* Find insertion point and validate there are no overlaps */
180  insert_index = range_list_find_previous(rl, offset)+1;
181 
182  /* Does the previous element overlap with this one? */
183  if(insert_index > 0)
184  {
185    prev_elem = rl->elements[insert_index-1];
186    if(offset < prev_elem->length + prev_elem->offset)
187      return false;
188  }
189
190  /* Does this new element overlap with the next one? */
191  if((insert_index+1 < rl->size) 
192     && (offset+length > rl->elements[insert_index+1]->offset))
193    return false;
194
195  elem = talloc(rl->elements, range_list_element);
196  if(elem == NULL)
197    return false;
198  elem->offset = offset;
199  elem->length = length;
200  elem->data = data;
201 
202  if(!range_list_insert(rl, elem, insert_index))
203  {
204    talloc_free(elem);
205    return false;
206  }
207
208  return true;
209}
210
211
212bool range_list_remove(range_list* rl, uint32_t index)
213{
214  uint32_t i;
215  range_list_element** tmp;
216
217  if(index >= rl->size)
218    return false;
219
220  talloc_free(rl->elements[index]);
221
222  /* Do the shuffle to the left. */
223  for(i=index; i < (rl->size-1); i++)
224    rl->elements[i] = rl->elements[i+1];
225  rl->elements[rl->size-1] = NULL;
226  rl->size--;
227
228  /* Try to keep memory usage down */
229  if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE  < rl->elem_alloced)
230  {
231    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
232                         (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE));
233    if(tmp != NULL)
234    {
235      rl->elements = tmp;
236      rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE;
237    }
238  }
239
240  return true;
241}
242
243
244const range_list_element* range_list_get(const range_list* rl, uint32_t index)
245{
246  if(index >= rl->size)
247    return NULL;
248
249  return rl->elements[index];
250}
251
252
253int32_t range_list_find(const range_list* rl, uint32_t offset)
254{
255  uint32_t prev_idx;
256  range_list_element* elem;
257
258  if(rl->size == 0)
259    return -1;
260
261  if((offset < rl->elements[0]->offset)
262     || (offset > rl->elements[rl->size-1]->offset
263         + rl->elements[rl->size-1]->length))
264    return -2;
265
266  prev_idx = range_list_find_previous(rl, offset);
267  elem = rl->elements[prev_idx];
268  if(offset < elem->offset+elem->length)
269    return prev_idx;
270
271  return -3;
272}
273
274
275void* range_list_find_data(const range_list* rl, uint32_t offset)
276{
277  int32_t index = range_list_find(rl, offset);
278  if(index < 0)
279    return NULL;
280
281  return rl->elements[index]->data;
282}
283
284
285bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
286{
287  range_list_element* cur_elem;
288  range_list_element* new_elem;
289
290  if(index >= rl->size)
291    return false;
292
293  cur_elem = rl->elements[index];
294  if((offset <= cur_elem->offset) 
295     || (offset >= cur_elem->offset+cur_elem->length))
296    return false;
297
298  new_elem = talloc(rl->elements, range_list_element);
299  if(new_elem == NULL)
300    return false;
301 
302  new_elem->offset = offset;
303  new_elem->length = cur_elem->offset + cur_elem->length - offset;
304  new_elem->data = cur_elem->data;
305 
306  if(!range_list_insert(rl, new_elem, index+1))
307  {
308    talloc_free(new_elem);
309    return false;
310  }
311
312  cur_elem->length = new_elem->offset - cur_elem->offset;
313
314  return true;
315}
316
317
318bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length)
319{
320  int32_t idx1, idx2;
321
322  idx1 = range_list_find(rl, start);
323  if(idx1 < 0)
324    return false;
325
326  idx2 = range_list_find(rl, start+length);
327  if(idx2 < 0)
328    return false;
329
330  if(idx1 == idx2)
331    return true;
332
333  while(idx1 != idx2)
334  {
335    if(rl->elements[idx1]->offset + rl->elements[idx1]->length
336       != rl->elements[idx1+1]->offset)
337      return false;
338    idx1++;
339  }
340
341  return true;
342}
Note: See TracBrowser for help on using the repository browser.