1 /*
2  * Copyright (c) 2015 NLNet Labs. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include "status.h"
33 #include "datastructure.h"
34 
35 struct collection_class_struct {
36     FILE* store;
37     void* cargo;
38     int (*member_destroy)(void* cargo, void* member);
39     int (*member_dispose)(void* cargo, void* member, FILE*);
40     int (*member_restore)(void* cargo, void* member, FILE*);
41 };
42 
43 struct collection_instance_struct {
44     struct collection_class_struct* method;
45     char* array; /** array with members */
46     size_t size; /** member size */
47     int iterator;
48     int count; /** number of members in array */
49     long location;
50 };
51 
52 static int
swapin(collection_t collection)53 swapin(collection_t collection)
54 {
55     int i;
56     if(collection->count > 0) {
57         if(fseek(collection->method->store, collection->location, SEEK_SET))
58             return 1;
59         for(i=0; i<collection->count; i++) {
60             if(collection->method->member_restore(collection->method->cargo,
61                     collection->array + collection->size * i, collection->method->store))
62                 return 1;
63         }
64     }
65     return 0;
66 }
67 
68 static int
swapout(collection_t collection)69 swapout(collection_t collection)
70 {
71     int i;
72     if(collection->count > 0) {
73         if(fseek(collection->method->store, 0, SEEK_END))
74             return 1;
75         collection->location = ftell(collection->method->store);
76         for(i=0; i<collection->count; i++) {
77             if(collection->method->member_dispose(collection->method->cargo,
78                     collection->array + collection->size * i, collection->method->store))
79                 return 1;
80         }
81     }
82     return 0;
83 }
84 
85 void
collection_class_allocated(collection_class * klass,void * cargo,int (* member_destroy)(void * cargo,void * member))86 collection_class_allocated(collection_class* klass, void *cargo,
87         int (*member_destroy)(void* cargo, void* member))
88 {
89     CHECKALLOC(*klass = malloc(sizeof(struct collection_class_struct)));
90     (*klass)->cargo = cargo;
91     (*klass)->member_destroy = member_destroy;
92     (*klass)->member_dispose = NULL;
93     (*klass)->member_restore = NULL;
94     (*klass)->store = NULL;
95 }
96 
97 void
collection_class_backed(collection_class * klass,char * fname,void * cargo,int (* member_destroy)(void * cargo,void * member),int (* member_dispose)(void * cargo,void * member,FILE *),int (* member_restore)(void * cargo,void * member,FILE *))98 collection_class_backed(collection_class* klass, char* fname, void *cargo,
99         int (*member_destroy)(void* cargo, void* member),
100         int (*member_dispose)(void* cargo, void* member, FILE*),
101         int (*member_restore)(void* cargo, void* member, FILE*))
102 {
103     CHECKALLOC(*klass = malloc(sizeof(struct collection_class_struct)));
104     (*klass)->cargo = cargo;
105     (*klass)->member_destroy = member_destroy;
106     (*klass)->member_dispose = member_dispose;
107     (*klass)->member_restore = member_restore;
108     (*klass)->store = fopen(fname, "w+");
109 }
110 
111 void
collection_class_destroy(collection_class * klass)112 collection_class_destroy(collection_class* klass)
113 {
114     if (klass == NULL)
115         return;
116     free(*klass);
117     *klass = NULL;
118 }
119 
120 void
collection_create_array(collection_t * collection,size_t membsize,collection_class klass)121 collection_create_array(collection_t* collection, size_t membsize,
122         collection_class klass)
123 {
124     CHECKALLOC(*collection = malloc(sizeof(struct collection_instance_struct)));
125     (*collection)->size = membsize;
126     (*collection)->count = 0;
127     (*collection)->array = NULL;
128     (*collection)->iterator = -1;
129     (*collection)->method = klass;
130 }
131 
132 void
collection_destroy(collection_t * collection)133 collection_destroy(collection_t* collection)
134 {
135     int i;
136     if(collection == NULL)
137         return;
138     for (i=0; i < (*collection)->count; i++) {
139         (*collection)->method->member_destroy((*collection)->method->cargo,
140                 &(*collection)->array[(*collection)->size * i]);
141     }
142     if((*collection)->array)
143         free((*collection)->array);
144     free(*collection);
145     *collection = NULL;
146 }
147 
148 void
collection_add(collection_t collection,void * data)149 collection_add(collection_t collection, void *data)
150 {
151     void* ptr;
152     if(collection->method->store)
153         swapin(collection);
154     CHECKALLOC(ptr = realloc(collection->array, (collection->count+1)*collection->size));
155     collection->array = ptr;
156     memcpy(&collection->array[collection->size * collection->count], data, collection->size);
157     collection->count += 1;
158     if(collection->method->store)
159         swapout(collection);
160 }
161 
162 void
collection_del_index(collection_t collection,int index)163 collection_del_index(collection_t collection, int index)
164 {
165     void* ptr;
166     if (index<0 || index >= collection->count)
167         return;
168     if(collection->method->store)
169         swapin(collection);
170     collection->method->member_destroy(collection->method->cargo, &collection->array[collection->size * index]);
171     collection->count -= 1;
172     memmove(&collection->array[collection->size * index], &collection->array[collection->size * (index + 1)], (collection->count - index) * collection->size);
173     if (collection->count > 0) {
174         CHECKALLOC(ptr = realloc(collection->array, collection->count * collection->size));
175         collection->array = ptr;
176     } else {
177         free(collection->array);
178         collection->array = NULL;
179     }
180     if(collection->method->store)
181         swapout(collection);
182 }
183 
184 void
collection_del_cursor(collection_t collection)185 collection_del_cursor(collection_t collection)
186 {
187     collection_del_index(collection, collection->iterator);
188 }
189 
190 void*
collection_iterator(collection_t collection)191 collection_iterator(collection_t collection)
192 {
193     if(collection->iterator < 0) {
194         if(collection->method->store)
195             swapin(collection);
196         collection->iterator = collection->count;
197     }
198     collection->iterator -= 1;
199     if(collection->iterator >= 0) {
200         return &collection->array[collection->iterator * collection->size];
201     } else {
202         if(collection->method->store)
203             swapout(collection);
204         return NULL;
205     }
206 }
207