source: trunk/lib/range_list.c @ 157

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

integrated talloc into most of the rest of the regfi library
fixed a length validation issue

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