diff options
Diffstat (limited to 'apps/tagdb/artist.c')
-rw-r--r-- | apps/tagdb/artist.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/apps/tagdb/artist.c b/apps/tagdb/artist.c new file mode 100644 index 0000000000..82db81df2f --- /dev/null +++ b/apps/tagdb/artist.c | |||
@@ -0,0 +1,370 @@ | |||
1 | #include "malloc.h" // realloc() and free() | ||
2 | #include <string.h> // strncasecmp() | ||
3 | |||
4 | #include "artist.h" | ||
5 | |||
6 | // how is our flag organized? | ||
7 | #define FLAG(deleted, spare) ( 0xC0 | (deleted?0x10:0x00) | (spare & 0x0F) ) | ||
8 | #define FLAG_VALID(flag) ((flag & 0xE0) == 0xC0) | ||
9 | #define FLAG_DELETED(flag) (flag & 0x10) | ||
10 | #define FLAG_SPARE(flag) (flag & 0x0F) | ||
11 | |||
12 | static int do_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count, const int zero_fill); | ||
13 | |||
14 | struct artist_entry* new_artist_entry(const uint32_t name_len, const uint32_t album_count) { | ||
15 | // start by allocating memory | ||
16 | struct artist_entry *e = (struct artist_entry*)malloc(sizeof(struct artist_entry)); | ||
17 | if( e == NULL ) { | ||
18 | DEBUGF("new_artist_entry: could not allocate memory\n"); | ||
19 | return NULL; | ||
20 | } | ||
21 | |||
22 | // We begin empty | ||
23 | e->name = NULL; | ||
24 | e->size.name_len = 0; | ||
25 | e->album = NULL; | ||
26 | e->size.album_count = 0; | ||
27 | e->flag = FLAG(0, 0); | ||
28 | |||
29 | // and resize to the requested size | ||
30 | if( do_resize(e, name_len, album_count, 1) ) { | ||
31 | free(e); | ||
32 | return NULL; | ||
33 | } | ||
34 | return e; | ||
35 | } | ||
36 | |||
37 | int artist_entry_destruct(struct artist_entry *e) { | ||
38 | assert(e != NULL); | ||
39 | assert(FLAG_VALID(e->flag)); | ||
40 | |||
41 | free(e->name); | ||
42 | free(e->album); | ||
43 | |||
44 | free(e); | ||
45 | |||
46 | return ERR_NONE; | ||
47 | } | ||
48 | |||
49 | static int do_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count, const int zero_fill) { | ||
50 | void* temp; | ||
51 | |||
52 | assert(e != NULL); | ||
53 | assert(FLAG_VALID(e->flag)); | ||
54 | |||
55 | // begin with name | ||
56 | if( name_len != e->size.name_len ) { | ||
57 | temp = realloc(e->name, name_len); | ||
58 | if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
59 | DEBUGF("artist_entry_resize: out of memory to resize name\n"); | ||
60 | return ERR_MALLOC; | ||
61 | } | ||
62 | e->name = (char*)temp; | ||
63 | |||
64 | // if asked, fill it with zero's | ||
65 | if( zero_fill ) { | ||
66 | uint32_t i; | ||
67 | for(i=e->size.name_len; i<name_len; i++) | ||
68 | e->name[i] = (char)0x00; | ||
69 | } | ||
70 | |||
71 | e->size.name_len = name_len; | ||
72 | } | ||
73 | |||
74 | // now the album | ||
75 | if( album_count != e->size.album_count ) { | ||
76 | temp = realloc(e->album, album_count * sizeof(*e->album)); | ||
77 | if(temp == NULL && album_count > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
78 | DEBUGF("artist_entry_resize: out of memory to resize album[]\n"); | ||
79 | return ERR_MALLOC; | ||
80 | } | ||
81 | e->album = (uint32_t*)temp; | ||
82 | |||
83 | // if asked, fill it with zero's | ||
84 | if( zero_fill ) { | ||
85 | uint32_t i; | ||
86 | for(i=e->size.album_count; i<album_count; i++) | ||
87 | e->album[i] = (uint32_t)0x00000000; | ||
88 | } | ||
89 | |||
90 | e->size.album_count = album_count; | ||
91 | } | ||
92 | |||
93 | return ERR_NONE; | ||
94 | } | ||
95 | |||
96 | int artist_entry_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count) { | ||
97 | return do_resize(e, name_len, album_count, 1); | ||
98 | } | ||
99 | |||
100 | int artist_entry_serialize(FILE *fd, const struct artist_entry *e) { | ||
101 | assert(fd != NULL); | ||
102 | assert(e != NULL); | ||
103 | assert(FLAG_VALID(e->flag)); | ||
104 | |||
105 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
106 | return ERR_NONE; | ||
107 | } | ||
108 | |||
109 | // First byte we write is a flag-byte to indicate this is a valid record | ||
110 | if( fwrite(&e->flag, 1, 1, fd) != 1 ) { | ||
111 | DEBUGF("artist_entry_serialize: failed to write flag-byte\n"); | ||
112 | return ERR_FILE; | ||
113 | } | ||
114 | |||
115 | // First we write the length of the name field | ||
116 | if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) { | ||
117 | DEBUGF("artist_entry_serialize: failed to write name_len\n"); | ||
118 | return ERR_FILE; | ||
119 | } | ||
120 | |||
121 | // now the name field itself | ||
122 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
123 | DEBUGF("artist_entry_serialize: failed to write name\n"); | ||
124 | return ERR_FILE; | ||
125 | } | ||
126 | |||
127 | // count of albums | ||
128 | if( fwrite(&e->size.album_count, sizeof(e->size.album_count), 1, fd) != 1 ) { | ||
129 | DEBUGF("artist_entry_serialize: failed to write album_count\n"); | ||
130 | return ERR_FILE; | ||
131 | } | ||
132 | |||
133 | // album[] itself | ||
134 | if( fwrite(e->album, sizeof(*e->album), e->size.album_count, fd) != e->size.album_count ) { | ||
135 | DEBUGF("artist_entry_serialize: failed to write albums\n"); | ||
136 | return ERR_FILE; | ||
137 | } | ||
138 | |||
139 | return ERR_NONE; | ||
140 | } | ||
141 | |||
142 | int artist_entry_unserialize(struct artist_entry **e, FILE *fd) { | ||
143 | uint32_t length; | ||
144 | unsigned char flag; | ||
145 | |||
146 | assert(e != NULL); | ||
147 | assert(fd != NULL); | ||
148 | |||
149 | // First byte we read is flag-byte | ||
150 | if( fread(&flag, 1, 1, fd) != 1 ) { | ||
151 | DEBUGF("artist_entry_unserialize: failed to read flag-byte\n"); | ||
152 | return ERR_FILE; | ||
153 | } | ||
154 | |||
155 | // See what we have: | ||
156 | if( ! FLAG_VALID(flag) ) { | ||
157 | DEBUGF("artist_entry_unserialize: flag-byte not found\n"); | ||
158 | return ERR_INVALID; | ||
159 | } | ||
160 | |||
161 | // Allocate memory | ||
162 | *e = new_artist_entry(0, 0); | ||
163 | if( *e == NULL ) { | ||
164 | DEBUGF("artist_entry_unserialize: could not create new artist_entry\n"); | ||
165 | return ERR_MALLOC; | ||
166 | } | ||
167 | |||
168 | (*e)->flag = flag; | ||
169 | |||
170 | // First we read the length of the name field | ||
171 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
172 | DEBUGF("artist_entry_unserialize: failed to read name_len\n"); | ||
173 | artist_entry_destruct(*e); | ||
174 | return ERR_FILE; | ||
175 | } | ||
176 | |||
177 | // allocate memory for the upcomming name-field | ||
178 | if( do_resize((*e), length, 0, 0) ) { | ||
179 | DEBUGF("artist_entry_unserialize: failed to allocate memory for name\n"); | ||
180 | artist_entry_destruct(*e); | ||
181 | return ERR_MALLOC; | ||
182 | } | ||
183 | |||
184 | // read it in | ||
185 | if( fread((*e)->name, 1, (*e)->size.name_len, fd) != (*e)->size.name_len ) { | ||
186 | DEBUGF("artist_entry_unserialize: failed to read name\n"); | ||
187 | artist_entry_destruct(*e); | ||
188 | return ERR_FILE; | ||
189 | } | ||
190 | |||
191 | if( FLAG_DELETED(flag) ) { | ||
192 | // all there is... free some memory | ||
193 | if( do_resize(*e, 0, 0, 0) ) { | ||
194 | DEBUGF("artist_entry_unserialize: couldn't free() name\n"); | ||
195 | return ERR_MALLOC; | ||
196 | } | ||
197 | return ERR_NONE; | ||
198 | } | ||
199 | |||
200 | // Next the count of albums | ||
201 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
202 | DEBUGF("artist_entry_unserialize: failed to read album_count\n"); | ||
203 | artist_entry_destruct(*e); | ||
204 | return ERR_FILE; | ||
205 | } | ||
206 | |||
207 | // allocate memory for the upcomming name-field | ||
208 | if( do_resize(*e, (*e)->size.name_len, length, 0) ) { | ||
209 | DEBUGF("artist_entry_unserialize: failed to allocate memory for album[]\n"); | ||
210 | artist_entry_destruct(*e); | ||
211 | return ERR_MALLOC; | ||
212 | } | ||
213 | |||
214 | // read it in | ||
215 | if( fread((*e)->album, sizeof(*(*e)->album), (*e)->size.album_count, fd) != (*e)->size.album_count ) { | ||
216 | DEBUGF("artist_entry_unserialize: failed to read albums\n"); | ||
217 | artist_entry_destruct(*e); | ||
218 | return ERR_FILE; | ||
219 | } | ||
220 | |||
221 | return ERR_NONE; | ||
222 | } | ||
223 | |||
224 | int artist_entry_write(FILE *fd, const struct artist_entry *e, const struct artist_size *s) { | ||
225 | uint32_t i, be; | ||
226 | char pad = 0x00; | ||
227 | |||
228 | assert(fd != NULL); | ||
229 | assert(e != NULL); | ||
230 | assert(FLAG_VALID(e->flag)); | ||
231 | |||
232 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
233 | return ERR_NONE; | ||
234 | } | ||
235 | |||
236 | // artist name | ||
237 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
238 | DEBUGF("artist_entry_write: failed to write name\n"); | ||
239 | return ERR_FILE; | ||
240 | } | ||
241 | // padd the rest | ||
242 | i = e->size.name_len; | ||
243 | while( s != NULL && s->name_len > i) { | ||
244 | if( fwrite(&pad, 1, 1, fd) == 1 ) { | ||
245 | i++; | ||
246 | continue; | ||
247 | } else { | ||
248 | DEBUGF("artist_entry_write: failed to padd name\n"); | ||
249 | return ERR_FILE; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | // album offsets, but in BIG ENDIAN! | ||
254 | // so we need to iterate over each item to convert it | ||
255 | for(i=0; i<e->size.album_count; i++) { | ||
256 | be = BE32(e->album[i]); | ||
257 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
258 | DEBUGF("artist_entry_write: failed to write album[%d]\n", i); | ||
259 | return ERR_FILE; | ||
260 | } | ||
261 | } | ||
262 | // padd the rest | ||
263 | be = BE32(0x00000000); | ||
264 | for(; s != NULL && i<s->album_count; i++) { | ||
265 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
266 | DEBUGF("artist_entry_write: failed to padd album[]\n"); | ||
267 | return ERR_FILE; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | return ERR_NONE; | ||
272 | } | ||
273 | |||
274 | inline int artist_entry_compare(const struct artist_entry *a, const struct artist_entry *b) { | ||
275 | assert(a != NULL); | ||
276 | assert(b != NULL); | ||
277 | if( a->name == NULL || b->name == NULL ) | ||
278 | return 1; // never match on no-names | ||
279 | return strcasecmp(a->name, b->name); | ||
280 | } | ||
281 | |||
282 | struct artist_size* new_artist_size() { | ||
283 | struct artist_size *s; | ||
284 | s = (struct artist_size*)malloc(sizeof(struct artist_size)); | ||
285 | if( s == NULL ) { | ||
286 | DEBUGF("new_artist_size: failed to allocate memory\n"); | ||
287 | return NULL; | ||
288 | } | ||
289 | s->name_len = 0; | ||
290 | s->album_count = 0; | ||
291 | |||
292 | return s; | ||
293 | } | ||
294 | |||
295 | inline uint32_t artist_size_get_length(const struct artist_size *size) { | ||
296 | assert(size != NULL); | ||
297 | return size->name_len + 4*size->album_count; | ||
298 | } | ||
299 | |||
300 | inline int artist_size_max(struct artist_size *s, const struct artist_entry *e) { | ||
301 | s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len ); | ||
302 | s->album_count = ( s->album_count >= e->size.album_count ? s->album_count : e->size.album_count ); | ||
303 | return ERR_NONE; | ||
304 | } | ||
305 | |||
306 | int artist_size_destruct(struct artist_size *s) { | ||
307 | assert(s != NULL); | ||
308 | // nothing to do... | ||
309 | free(s); | ||
310 | return ERR_NONE; | ||
311 | } | ||
312 | |||
313 | int artist_entry_add_album_mem(struct artist_entry *e, struct artist_size *s, const uint32_t album) { | ||
314 | assert(e != NULL); | ||
315 | assert(FLAG_VALID(e->flag)); | ||
316 | |||
317 | if( do_resize(e, e->size.name_len, e->size.album_count+1, 0) ) { | ||
318 | DEBUGF("artist_entry_add_song_mem: failed to resize album[]\n"); | ||
319 | return ERR_MALLOC; | ||
320 | } | ||
321 | |||
322 | e->album[e->size.album_count-1] = album; | ||
323 | |||
324 | if(s != NULL) artist_size_max(s, e); // can't fail | ||
325 | |||
326 | return ERR_NONE; | ||
327 | } | ||
328 | |||
329 | static int delete_serialized(FILE *fd, struct artist_entry *e) { | ||
330 | // the entry should be both, in memory and in file at the current location | ||
331 | // this function will mark the file-entry as deleted | ||
332 | uint32_t size; | ||
333 | unsigned char flag; | ||
334 | // overwrite the beginning of the serialized data: | ||
335 | assert(fd != NULL); | ||
336 | assert(e != NULL); | ||
337 | assert(FLAG_VALID(e->flag)); | ||
338 | |||
339 | flag = FLAG(1, 0); // mark as deleted | ||
340 | |||
341 | // First byte we write is the flag-byte to indicate this is a deleted | ||
342 | if( fwrite(&flag, 1, 1, fd) != 1 ) { | ||
343 | DEBUGF("artist_entry_delete_serialized: failed to write flag-byte\n"); | ||
344 | return ERR_FILE; | ||
345 | } | ||
346 | |||
347 | // Then we write the length of the COMPLETE entry | ||
348 | size = artist_size_get_length(&e->size) + 4; // 4 = overhead for the album[] | ||
349 | if( fwrite(&size, sizeof(size), 1, fd) != 1 ) { | ||
350 | DEBUGF("artist_entry_delete_serialized: failed to write len\n"); | ||
351 | return ERR_FILE; | ||
352 | } | ||
353 | |||
354 | return ERR_NONE; | ||
355 | } | ||
356 | |||
357 | int artist_entry_add_album_file(FILE *fd, struct artist_entry *e, struct artist_size *s, const uint32_t album) { | ||
358 | assert(fd != NULL); | ||
359 | assert(e != NULL); | ||
360 | assert(FLAG_VALID(e->flag)); | ||
361 | |||
362 | DEBUGF("artist_entry_add_song_file() called\n"); | ||
363 | |||
364 | if( delete_serialized(fd, e) ) { | ||
365 | DEBUGF("artist_entry_add_album_file: could not mark as deleted\n"); | ||
366 | return ERR_FILE; | ||
367 | } | ||
368 | |||
369 | return ERR_NO_INPLACE_UPDATE; | ||
370 | } | ||