implement bufread() and better testing. checking md5sum of the outputted
[Rockbox-MoB.git] / buffering.c
blob350d18d907ef547abca75b3f436993c2e44500d2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include "buffering.h"
26 #include "aux.h"
28 /* Rockbox constants */
29 #define HZ 1
30 /* amount of data to read in one read() call */
31 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
34 #define BUFFER_SIZE (32*1024*1024)
35 #define GUARD_SIZE (32*1024)
38 /* Ring buffer helper macros */
39 /* Buffer pointer (p) plus value (v), wrapped if necessary */
40 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
41 /* Buffer pointer (p) minus value (v), wrapped if necessary */
42 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
43 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
44 #define RINGBUF_ADD_CROSS(p1,v,p2) \
45 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
46 /* Bytes available in the buffer */
47 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
49 #ifndef MIN
50 #define MIN(a, b) (((a)<(b))?(a):(b))
51 #endif
54 static size_t buffer_len;
55 static char *buffer;
56 static char *buffer_end;
58 static size_t conf_filechunk;
60 static size_t buf_widx;
61 static size_t buf_ridx;
63 /* current memory handle in the linked list. NULL when the list is empty. */
64 static struct memory_handle *cur_handle;
65 /* first memory handle in the linked list. NULL when the list is empty. */
66 static struct memory_handle *first_handle;
67 static int num_handles;
70 /* add a new handle to the linked list and return it. It will have become the
71 new current handle */
72 static struct memory_handle *add_handle(void)
74 /* this will give each handle a unique id */
75 static int cur_handle_id = 0;
77 int data_size = sizeof(struct memory_handle) /*
78 + 1024*1024 - sizeof(struct memory_handle) */;
80 /* check that we actually can add the handle and its data */
81 if (RINGBUF_ADD_CROSS(buf_widx, data_size, buf_ridx) >= 0) {
82 return NULL;
85 struct memory_handle *new_handle = (struct memory_handle *)(buffer + buf_widx);
86 buf_widx = RINGBUF_ADD(buf_widx, data_size);
88 if (!first_handle) {
89 /* the new handle is the first one */
90 first_handle = new_handle;
93 if (cur_handle) {
94 cur_handle->next = new_handle;
97 cur_handle = new_handle;
98 cur_handle->id = cur_handle_id++;
99 num_handles++;
100 return cur_handle;
103 /* delete a given memory handle from the linked list
104 and return true for success. Nothing is actually erased from memory. */
105 static bool rm_handle(struct memory_handle *h)
107 if (h == first_handle) {
108 first_handle = h->next;
109 } else {
110 struct memory_handle *m = first_handle;
111 while (m && m->next != h) {
112 m = m->next;
114 if (h && m && m->next == h) {
115 m->next = h->next;
116 } else {
117 return false;
120 num_handles--;
121 return true;
124 /* Return a pointer to the memory handle of given ID.
125 NULL if the handle wasn't found */
126 static struct memory_handle *find_handle(int handle_id)
128 static int last_handle_id = -1;
129 static struct memory_handle *last_m = NULL;
130 struct memory_handle *m = first_handle;
131 /* simple cacheing because most of the time the requested handle
132 will either be the same as the last, or the one after the last */
133 if (last_m)
135 if (last_handle_id == handle_id)
136 return last_m;
137 else if (last_m->next && (last_m->next->id == handle_id))
139 last_m = last_m->next;
140 last_handle_id = handle_id;
141 return last_m;
144 while (m && m->id != handle_id) {
145 m = m->next;
147 last_handle_id = handle_id;
148 last_m = m;
149 return (m && m->id == handle_id) ? m : NULL;
152 /* Buffer data for the given handle. Return the amount of data buffered
153 or -1 if the handle wasn't found */
154 static int buffer_handle(int handle_id)
156 struct memory_handle *h = find_handle(handle_id);
157 //printf("find_handle %d:\nid: %d\npath: %s\n\n", handle_id, h->id, h->path);
158 if (!h)
159 return -1;
161 if (h->filerem == 0) {
162 /* nothing left to buffer */
163 return 0;
166 if (h->fd < 0) /* file closed, reopen */
168 if (*h->path)
169 h->fd = open(h->path, O_RDONLY);
170 else
171 return -1;
173 if (h->fd < 0)
174 return -1;
176 if (h->offset)
177 lseek(h->fd, h->offset, SEEK_SET);
180 int ret = 0;
181 while (h->filerem > 0)
183 /* max amount to copy */
184 size_t copy_n = MIN(conf_filechunk, buffer_len - buf_widx);
186 if (RINGBUF_ADD_CROSS(buf_widx, copy_n, buf_ridx) >= 0)
187 break;
189 /* rc is the actual amount read */
190 int rc = read(h->fd, &buffer[buf_widx], copy_n);
192 if (rc < 0)
194 printf("File ended %ldB early", h->filerem);
195 h->filesize -= h->filerem;
196 h->filerem = 0;
197 break;
200 /* Advance buffer */
201 buf_widx = RINGBUF_ADD(buf_widx, rc);
202 h->available += rc;
203 ret += rc;
204 h->filerem -= rc;
207 if (h->filerem == 0) {
208 /* finished buffering the file */
209 close(h->fd);
212 printf("buffered %d bytes (%d of %d available, remaining: %d)\n",
213 ret, h->available, h->filesize, h->filerem);
214 return ret;
217 /* Request a file be buffered
218 filename: name of the file t open
219 offset: starting offset to buffer from the file
220 RETURNS: <0 if the file cannot be opened, or one file already
221 queued to be opened, otherwise the handle for the file in the buffer
223 int bufopen(char *file, size_t offset)
225 /* add the file to the buffering queue. */
226 /* for now, we'll assume the queue is always empty, so the handle
227 gets added immediately */
229 printf("bufopen: %s (offset: %d)\n", file, offset);
231 int fd = open(file, O_RDONLY);
232 if (fd < 0)
233 return -1;
235 if (offset)
236 lseek(fd, offset, SEEK_SET);
238 struct memory_handle *h = add_handle();
239 if (!h)
240 return -1;
241 strncpy(h->path, file, MAX_PATH);
242 h->fd = fd;
243 h->filesize = filesize(fd);
244 h->filerem = h->filesize - offset;
245 h->offset = offset;
246 h->buf_idx = buf_widx;
247 h->data = buf_widx;
248 h->available = 0;
249 h->next = NULL;
251 printf("added handle : %d\n", h->id);
252 return h->id;
255 /* Close the handle. Return 0 for success and < 0 for failure */
256 int bufclose(int handle_id)
258 printf("bufclose: %d\n", handle_id);
259 struct memory_handle *h = find_handle(handle_id);
260 if (!h)
261 return -1;
263 /* when we close the first handle, we can reclaim the space from its buffer */
264 if (h == first_handle) {
265 buf_ridx = h->buf_idx + h->available;
266 /* is this right in all cases ? */
269 return rm_handle(h) ? 0 : -1;
272 /* Seek in a handle. Return 0 for success and < 0 for failure */
273 int bufseek(int handle_id, size_t offset)
275 struct memory_handle *h = find_handle(handle_id);
276 if (!h)
277 return -1;
279 h->buf_idx = RINGBUF_ADD(h->data, offset);
280 return 0;
283 int bufread(int handle_id, size_t size, char *dest)
285 struct memory_handle *h = find_handle(handle_id);
286 size_t buffered_data;
287 if (!h)
288 return -1;
289 if (h->available == 0)
290 return 0;
291 buffered_data = MIN(size,h->available);
293 if (h->buf_idx + buffered_data > buffer_len)
295 size_t read = (h->buf_idx + buffered_data) -buffer_len;
296 memcpy(dest, &buffer[h->buf_idx], read);
297 memcpy(dest+read, buffer, buffered_data - read);
299 else memcpy(dest, &buffer[h->buf_idx], buffered_data);
300 h->buf_idx = RINGBUF_ADD(h->buf_idx, buffered_data);
301 h->available -= buffered_data;
302 return buffered_data;
305 static void list_handles(void)
307 struct memory_handle *m = first_handle;
308 while (m) {
309 printf("%02d - %d\n", m->id, (void *)m-(void *)buffer);
310 m = m->next;
314 void buffer_init(void)
316 buffer = (char *)malloc(sizeof(char) * (BUFFER_SIZE + GUARD_SIZE));
317 if (!buffer)
319 printf("couldn't allocate buffer\n");
320 exit(1);
322 buffer_len = BUFFER_SIZE;
323 buffer_end = buffer + BUFFER_SIZE;
325 buf_widx = 0;
326 buf_ridx = 0;
328 first_handle = NULL;
329 first_handle = NULL;
330 num_handles = 0;
332 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
334 #if 0
335 int main(int argc, char **argv)
337 buffer_init();
339 printf("sizeof memory_handle : %d\n", sizeof(struct memory_handle));
340 printf("\n");
342 int i, hdl;
343 for (i = 1; i < argc; i++) {
344 hdl = bufopen(argv[i], 0);
345 buffer_handle(hdl);
346 printf("used: %d, free: %d\n", BUF_USED, BUFFER_SIZE - BUF_USED);
347 printf("buf_widx: %d\n", buf_widx);
348 printf("----\n");
351 printf("\n");
352 bufclose(0);
353 printf("used: %d, free: %d\n", BUF_USED, BUFFER_SIZE - BUF_USED);
354 printf("buf_widx: %d\n", buf_widx);
355 printf("\n");
356 bufclose(1);
357 printf("used: %d, free: %d\n", BUF_USED, BUFFER_SIZE - BUF_USED);
358 printf("buf_widx: %d\n", buf_widx);
359 printf("\n");
361 buffer_handle(2);
362 printf("used: %d, free: %d\n", BUF_USED, BUFFER_SIZE - BUF_USED);
363 printf("buf_widx: %d\n", buf_widx);
365 return 0;
367 #endif
368 /* returns true if the file still has some on disk unread */
369 bool handle_has_data(int handle)
371 struct memory_handle *m = find_handle(handle);
372 if (m)
374 return m->filerem != 0;
376 return false;
378 bool need_rebuffer(void)
380 size_t free;
381 free = BUFFER_SIZE - BUF_USED;
382 return ((free >= BUFFER_SIZE/4));
384 #define MAX_HANDLES 64
385 int main(int argc, char *argv[])
387 int next_file = 1;
388 int last_handle = -1;
389 int handle_order[MAX_HANDLES];
390 int reading_handle = 0;
391 bool done = false;
392 char read_buffer[GUARD_SIZE];
393 buffer_init();
394 while (done == false)
396 if (next_file <= argc && need_rebuffer())
398 printf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
399 if ( (!num_handles ||
400 handle_has_data(handle_order[last_handle]) == false))
402 int h = bufopen(argv[next_file++], 0);
403 if (h >= 0)
405 printf("new handle %d\n",h);
406 last_handle++;
407 handle_order[last_handle] = h;
408 buffer_handle(handle_order[last_handle]);
411 else
413 if (handle_has_data(handle_order[last_handle]) == true)
415 printf("buffering handle %d\n",handle_order[last_handle]);
416 buffer_handle(handle_order[last_handle]);
420 else
422 printf("reading handle: %d\n", handle_order[reading_handle]);
423 long total = 0, read;
424 char file[MAX_PATH];
425 int fd;
426 if (next_file > argc && reading_handle >= last_handle)
427 done = true;
429 snprintf(file, MAX_PATH, "./file%d.mp3", reading_handle);
430 fd = open(file, O_CREAT|O_TRUNC|O_RDWR, 770);
431 if (fd < 0)
433 printf("ERROROROROR\n");
434 exit(1);
436 while (1)
438 // printf("reading %d: %d left\n", handle_order[reading_handle],
439 // find_handle(handle_order[reading_handle])->available);
440 read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer);
441 total += read;
442 write(fd, read_buffer, read);
443 if (read <= 0)
445 printf("finished reading %d, %d\n",handle_order[reading_handle], total);
446 bufclose(handle_order[reading_handle]);
447 close(fd);
448 reading_handle++;
449 break;
455 free(buffer);
456 return 0;