source: trunk/lib/range_list.c @ 98

Last change on this file since 98 was 98, checked in by tim, 17 years ago

adding experimental library which will be used to speed up hbin lookups

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