1 #include "malloc.h" // malloc() and free()
3 #include "array_buffer.h"
6 static int add_mem(struct array_buffer
*b
, void *e
);
7 static int add_file(struct array_buffer
*b
, void *e
);
9 static int update_entry_mem(struct array_buffer
*b
, const uint32_t index
, uint32_t item
);
10 static int update_entry_file(struct array_buffer
*b
, const uint32_t index
, uint32_t item
);
12 static int find_entry_mem(struct array_buffer
*b
, const void *needle
, uint32_t *index
);
13 static int find_entry_file(struct array_buffer
*b
, const void *needle
, uint32_t *index
);
15 static int sort_mem(struct array_buffer
*b
);
16 static int sort_mem_merge_blocks(uint32_t *dest
, uint32_t *s1
, uint32_t s1_l
, uint32_t *s2
, uint32_t s2_l
, struct array_buffer
*b
);
17 static int sort_mem_merge(uint32_t *dest
, uint32_t *src
, struct array_buffer
*b
, uint32_t blocksize
);
18 static int sort_file(struct array_buffer
*b
);
20 struct array_buffer
* new_array_buffer( int (*cmp
)(const void *a
, const void *b
),
21 int (*serialize
)(FILE *fd
, const void *e
),
22 int (*unserialize
)(void **e
, FILE *fd
),
23 uint32_t (*get_length
)(const void *size
),
24 int (*write
)(FILE *fd
, void *e
, const void *size
),
25 int (*destruct
)(void *e
),
28 int (*max_size_update
)(void *max_size
, const void *e
),
29 int (*max_size_destruct
)(void *max_size
),
30 int (*add_item_mem
)(void *e
, void *s
, uint32_t item
),
31 int (*add_item_file
)(FILE *fd
, void *e
, void *s
, uint32_t item
),
32 int (*pre_write
)(void *e
, void *s
)
34 struct array_buffer
*b
;
35 b
= (struct array_buffer
*)malloc(sizeof(struct array_buffer
));
37 DEBUGF("new_array_buffer: failed to allocate memory\n");
45 b
->file_name
= file_name
;
50 b
->serialize
= serialize
;
51 b
->unserialize
= unserialize
;
52 b
->get_length
= get_length
;
54 b
->destruct
= destruct
;
56 b
->max_size
= max_size
;
57 b
->max_size_update
= max_size_update
;
58 b
->max_size_destruct
= max_size_destruct
;
60 b
->add_item_mem
= add_item_mem
;
61 b
->add_item_file
= add_item_file
;
63 b
->pre_write
= pre_write
;
68 int array_buffer_destruct(struct array_buffer
*b
, const int free_file_name
) {
72 if( b
->destruct
== NULL
) {
73 DEBUGF("array_buffer_destruct: no destruct() function registered\n");
76 //we have memory to clean up
77 // iterate over all stored objects:
78 for(; b
->count
> 0; b
->count
--) {
79 if( b
->destruct(b
->array
[b
->count
-1].mem
) ) {
80 DEBUGF("array_buffer_destruct: failed to destruct item[%u]\n", b
->count
-1);
88 // we have a file to clean up
89 if( fclose(b
->fd
) != 0 ) {
90 DEBUGF("array_buffer_destruct: fclose() failed\n");
96 if( remove(b
->file_name
) != 0 ) {
97 DEBUGF("array_buffer_destruct: remove() failed\n");
101 if( free_file_name
) {
110 if( b
->max_size
!= NULL
) {
111 if( b
->max_size_destruct
== NULL
) {
112 DEBUGF("array_buffer_destruct: no max_size_destruct() function registered\n");
116 if( b
->max_size_destruct(b
->max_size
) ) {
117 DEBUGF("array_buffer_destruct: failed to destruct max_size\n");
128 int array_buffer_switch_to_file(struct array_buffer
*b
) {
134 if(b
->file_name
== NULL
) {
135 DEBUGF("array_buffer_switch_to_file: no file_name, failing...\n");
139 if( b
->fd
!= NULL
) {
140 DEBUGF("array_buffer_switch_to_file: already in file, failing...\n");
144 // function calls exist?
145 if( b
->serialize
== NULL
|| b
->unserialize
== NULL
) {
146 DEBUGF("array_buffer_switch_to_file: serialize() and/or unserialize() function(s) not registered\n");
150 // since we got here, we are VERY short on memory
151 // We cannot do any memory allocation before free()ing some
152 // The filename is already allocated in the constructor
155 b
->fd
= fopen(b
->file_name
, "w+");
156 if( b
->fd
== NULL
) {
157 DEBUGF("array_buffer_switch_to_file: failed to fopen() file\n");
161 for(i
=0; i
<b
->count
; i
++) {
162 offset
= ftell(b
->fd
);
164 DEBUGF("array_buffer_switch_to_file: ftell() failed\n");
168 if( b
->serialize(b
->fd
, b
->array
[i
].mem
) ) {
169 DEBUGF("array_buffer_switch_to_file: serialize() failed on item[%u], ignoring...\n", i
);
171 b
->destruct(b
->array
[i
].mem
);
173 b
->array
[i
].file_offset
= offset
;
179 static int add_mem(struct array_buffer
*b
, void *e
) {
183 // just copy over the pointer
184 b
->array
[b
->count
].mem
= e
;
189 static int add_file(struct array_buffer
*b
, void *e
) {
195 if( fseek(b
->fd
, 0, SEEK_END
) != 0 ) {
196 DEBUGF("add_file: could not seek to end of file\n");
199 if(( b
->array
[b
->count
].file_offset
= ftell(b
->fd
) ) == -1) {
200 DEBUGF("add_file: ftell() failed to get file_offset\n");
204 if(( rc
= b
->serialize(b
->fd
, e
) )) {
205 DEBUGF("add_file: could not serialize entry\n");
208 if( b
->destruct(e
) ) {
209 DEBUGF("add_file: could not destruct entry, ignoring... (memory leak)\n");
214 int array_buffer_add(struct array_buffer
*b
, void *e
, uint32_t *index
) {
221 // allow the object to update the max_size
222 // Do this first, so if it fails we can just return without cleanup to do
223 if( b
->max_size_update
!= NULL
) {
224 if(( rc
= b
->max_size_update(b
->max_size
, e
) )) {
225 DEBUGF("array_buffer_add: could not update max_size, failing...\n");
230 // we need to enlarge the array[]
231 temp
= (void*)realloc(b
->array
, sizeof(*b
->array
)*(b
->count
+1));
232 while( temp
== NULL
) {
233 DEBUGF("array_buffer_add: failed to enlarge index_map[]. Switching to file\n");
234 if(( rc
= array_buffer_switch_to_file(b
) )) {
235 DEBUGF("array_buffer_add: failed to switch to file, failing...\n");
239 temp
= (void*)realloc(b
->array
, sizeof(*b
->array
)*(b
->count
+1));
241 b
->array
= (union entry
*)temp
;
243 if( b
->fd
== NULL
) { // we are in memory
245 if( rc
== ERR_MALLOC
) {
246 DEBUGF("array_buffer_add: failed to add in memory due to malloc() trouble, switching to file\n");
247 if(( rc
= array_buffer_switch_to_file(b
) )) {
248 DEBUGF("array_buffer_add: failed to switch to file, failing...\n");
251 // fall out and catch next if
253 } // NOT else, so we can catch the fall-through
255 if(( rc
= add_file(b
, e
) )) {
256 DEBUGF("array_buffer_add: failed to add in file, failing...\n");
261 // count and index-stuff
262 if(index
!= NULL
) *index
= b
->count
;
268 inline uint32_t array_buffer_get_next_index(struct array_buffer
*b
) {
273 static int update_entry_mem(struct array_buffer
*b
, const uint32_t index
, const uint32_t item
) {
277 assert(index
< b
->count
);
279 if( (rc
= b
->add_item_mem(b
->array
[index
].mem
, b
->max_size
, item
)) ) {
280 DEBUGF("update_entry_mem: failed to update entry\n");
287 static int update_entry_file(struct array_buffer
*b
, const uint32_t index
, uint32_t item
) {
288 /* uint32_t i, index;
291 long prev_file_offset;*/
294 assert(index
< b
->count
);
296 printf("TODO: update entry in file\n");
303 for(i=0; i<b->count; i++) {
304 prev_file_offset = ftell(b->fd); // keep this file-position
305 if( prev_file_offset == -1 ) {
306 DEBUGF("file_entry_add_file: ftell() failed\n");
310 if( (rc = b->unserialize(&e, b->fd)) ) {
311 DEBUGF("find_entry_add_file: unserialize failed\n");
315 if( b->cmp(e, needle) == 0 ) { // found
316 if( fseek(b->fd, prev_file_offset, SEEK_SET) ) {
317 DEBUGF("file_entry_add_file: fseek() to entry[%u] failed\n", i);
321 rc = b->add_item_file(b->fd, e, b->max_size, item);
322 if( !( rc == ERR_NONE || rc == ERR_NO_INPLACE_UPDATE )) {
323 DEBUGF("find_entry_add_mem: failed to add item\n");
327 break; // stop looping
334 if( fseek(b->fd, 0, SEEK_END) != 0) {
335 DEBUGF("find_entry_add_file: fseek(SEEK_END) failed\n");
339 // We either succeded, deleted the entry or didn't find it:
340 if( rc == ERR_NOTFOUND ) {
342 } else if( rc == ERR_NONE ) {
343 b->destruct(e); // delete the entry and quit
347 // we could not update inplace
348 // the entry is deleted, update it and add it again
349 if( (rc = b->add_item_mem(e, b->max_size, item)) ) {
350 DEBUGF("find_entry_add_file: failed to add item in mem\n");
354 if( (rc = array_buffer_add(b, e, &index) ) ) {
355 DEBUGF("find_entry_add_file: failed to re-add item to array");
359 // the entry is now re-added, but with another index number...
360 // change the index_map to reflect this:
361 b->index_map[i] = index;
366 int array_buffer_entry_update(struct array_buffer
*b
, const uint32_t index
, uint32_t item
) {
369 if(index
>= b
->count
) {
370 DEBUGF("array_buffer_entry_update: index out of bounds\n");
374 if( b
->fd
== NULL
) {
375 return update_entry_mem(b
, index
, item
);
377 return update_entry_file(b
, index
, item
);
381 static int find_entry_mem(struct array_buffer
*b
, const void *needle
, uint32_t *index
) {
385 assert(needle
!= NULL
);
386 assert(index
!= NULL
);
388 for(i
=0; i
<b
->count
; i
++) {
389 if( b
->cmp(b
->array
[i
].mem
, needle
) == 0 ) { // found
397 static int find_entry_file(struct array_buffer
*b
, const void *needle
, uint32_t *index
) {
401 long prev_file_offset
;
404 assert(needle
!= NULL
);
405 assert(index
!= NULL
);
407 // We do this search in the order of the entries in file.
408 // After we found one, we look for the index of that offset
410 // This will (PROBABELY: TODO) be faster than random-access the file
413 for(i
=0; i
<b
->count
; i
++) {
414 prev_file_offset
= ftell(b
->fd
); // keep this file-position
415 if( prev_file_offset
== -1 ) {
416 DEBUGF("file_entry_add_file: ftell() failed\n");
420 if( (rc
= b
->unserialize(&e
, b
->fd
)) ) {
421 DEBUGF("find_entry_add_file: unserialize failed\n");
425 if( b
->cmp(e
, needle
) == 0 ) { // found
426 if( fseek(b
->fd
, prev_file_offset
, SEEK_SET
) ) {
427 DEBUGF("file_entry_add_file: fseek() to entry[%u] failed\n", i
);
432 break; // out of the for() loop
438 if( i
== b
->count
) {
439 // we didn't find anything
443 // we found an entry, look for the index number of that offset:
444 for(i
=0; i
<b
->count
; i
++) {
445 if(prev_file_offset
== b
->array
[i
].file_offset
) {
452 // we should never get here
453 DEBUGF("find_entry_file: found entry in file, but doens't match an index\n");
457 int array_buffer_find_entry(struct array_buffer
*b
, const void *needle
, uint32_t *index
) {
459 assert(needle
!= NULL
);
460 assert(index
!= NULL
); // TODO: if it is null, do the search but trash the index
462 if( b
->fd
== NULL
) {
463 return find_entry_mem(b
, needle
, index
);
465 return find_entry_file(b
, needle
, index
);
470 static int sort_mem_merge_blocks(uint32_t *dest, const uint32_t *s1, const uint32_t s1_l, const uint32_t *s2, const uint32_t s2_l, struct array_buffer *b) {
471 // merges the 2 blocks at s1 (with s1_l items) and s2 (with s2_l items)
473 uint32_t *s1_max, s2_max;
475 #define CMP(a, b) b->cmp( b->entry[a].mem, b->entry[b].mem )
479 while( s1 < s1_max || s2 < s2_max ) {
480 while( s1 < s1_max && ( s2 == s2_max || CMP(s1, s2) <= 0 ) ) // s1 is smaller than s2 (or s2 is used up)
481 *(dest++) = s1++; // copy and move to next
482 while( s2 < s2_max && ( s1 == s1_max || CMP(s1, s2) > 0 ) ) // s2 smaller
489 #define MIN(a, b) ( (a) <= (b) ? (a) : (b) )
490 static int sort_mem_merge(uint32_t *dest, uint32_t *src, struct array_buffer *b, uint32_t blocksize) {
491 // does 1 merge from src[] into dest[]
492 // asumes there are sorted blocks in src[] of size blocksize
493 assert( dest != NULL);
494 assert( src != NULL );
496 assert( b->count > blocksize );
502 static int sort_mem(struct array_buffer
*b
) {
503 uint32_t *tmp
, blocksize
;
507 tmp
= (uint32_t*)malloc(sizeof(uint32_t)*b
->count
);
509 DEBUGF("sort_mem: could not malloc() for second sort[] array\n");
513 for( blocksize
= 1; blocksize
< b
->count
; blocksize
++) {
514 b
->sort
[blocksize
] = blocksize
; // 1-1 map TODO
522 static int sort_file(struct array_buffer
*b
) {
523 printf("TODO: file-sorting\n"); // TODO
527 int array_buffer_sort(struct array_buffer
*b
) {
532 b
->sort
= (uint32_t*)malloc(sizeof(uint32_t)*b
->count
);
533 if( b
->sort
== NULL
) {
534 DEBUGF("array_buffer_sort: could not malloc() sort[] array\n");
538 if( b
->fd
== NULL
) { // in memory
540 if( rc
== ERR_MALLOC
) {
541 if(( rc
= array_buffer_switch_to_file(b
) )) {
542 DEBUGF("array_buffer_sort: could not switch to file mode\n");
547 DEBUGF("array_buffer_sort: could not sort array\n");
556 uint32_t array_buffer_get_offset(struct array_buffer
*b
, const uint32_t index
) {
561 if( index
>= b
->count
) {
562 DEBUGF("array_buffer_get_offset: index out of bounds\n");
563 return (uint32_t)0xffffffff;
566 // what is the (max) length of 1 item
567 if( b
->get_length
== NULL
) {
568 DEBUGF("array_buffer_get_offset: get_length() function not registered\n");
569 return (uint32_t)0xffffffff;
571 offset
= b
->get_length(b
->max_size
);
573 // multiply that by the number of items before me
574 if( b
->sort
== NULL
) { // easy, we are unsorted
578 for(i
=0; i
<b
->count
; i
++) {
579 if( b
->sort
[i
] == index
)
582 if( i
== b
->count
) {
583 DEBUGF("array_buffer_get_offset: index does not appeat in sorted list\n");
586 offset
*= i
; // that many items are before me
591 uint32_t array_buffer_get_length(struct array_buffer
*b
) {
596 // what is the (max) length of 1 item
597 if( b
->get_length
== NULL
) {
598 DEBUGF("array_buffer_get_offset: get_length() function not registered\n");
599 return (uint32_t)0xffffffff;
601 length
= b
->get_length(b
->max_size
);
603 // multiply that by the number of items
608 int array_buffer_write(FILE *fd
, struct array_buffer
*b
) {
615 // check if the functions exist
616 if( b
->write
== NULL
) {
617 DEBUGF("array_buffer_write: write() function not registered\n");
620 // if the array is in file
621 // serialize and unserialize will exist, since they're checked
622 // in the array_buffer_switch_to_file()
624 if( b
->fd
!= NULL
) {
625 rewind(b
->fd
); // seek to the beginning
628 for(i
=0; i
<b
->count
; i
++) { // for each element
632 // go through the sort-array and see which item should go next
633 if(b
->sort
!= NULL
) {
637 // get the item in memory
638 if( b
->fd
== NULL
) { // it already is im memory, fetch the pointer
639 item
= b
->array
[j
].mem
;
641 // since it's sorted, we shouldn't have to seek
642 if( (rc
= b
->unserialize(&item
, b
->fd
)) ) {
643 DEBUGF("array_buffer_write: could not unserialize item[%u], failing...\n", i
);
648 if(b
->pre_write
!= NULL
&& ( rc
= b
->pre_write(item
, b
->max_size
) )) {
649 DEBUGF("array_buffer_write: pre_write function failed, failing...\n");
653 // write item to file
654 if(( rc
= b
->write(fd
, item
, b
->max_size
) )) {
655 DEBUGF("array_buffer_write: could not write item[%u], failing...\n", i
);
659 // put it back where it came from
660 if( b
->fd
!= NULL
) {