add some note-to-self comments
[Rockbox-MoB.git] / buffering.c
blobf21a0e1d59e818a7292f8b62f8f6f1caf93f62e7
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 <unistd.h>
26 #include "buffering.h"
27 #include "helpers.h"
29 /* Rockbox constants */
30 #define HZ 1
31 /* amount of data to read in one read() call */
32 #define AUDIO_DEFAULT_FILECHUNK (1024*32)
35 #define BUFFER_SIZE (32*1024*1024)
36 #define GUARD_SIZE (32*1024)
39 /* Ring buffer helper macros */
40 /* Buffer pointer (p) plus value (v), wrapped if necessary */
41 #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len)
42 /* Buffer pointer (p) minus value (v), wrapped if necessary */
43 #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v)
44 /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */
45 #define RINGBUF_ADD_CROSS(p1,v,p2) \
46 ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len)
47 /* Bytes available in the buffer */
48 #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx)
50 #ifndef MIN
51 #define MIN(a, b) (((a)<(b))?(a):(b))
52 #endif
55 static size_t buffer_len;
56 static char *buffer;
57 static char *buffer_end;
59 static size_t conf_filechunk;
61 static size_t buf_widx;
62 static size_t buf_ridx;
64 /* current memory handle in the linked list. NULL when the list is empty. */
65 static struct memory_handle *cur_handle;
66 /* first memory handle in the linked list. NULL when the list is empty. */
67 static struct memory_handle *first_handle;
68 static int num_handles;
71 /* add a new handle to the linked list and return it. It will have become the
72 new current handle */
73 static struct memory_handle *add_handle(void)
75 /* this will give each handle a unique id */
76 static int cur_handle_id = 0;
78 int data_size = sizeof(struct memory_handle) /*
79 + 1024*1024 - sizeof(struct memory_handle) */;
81 /* check that we actually can add the handle and its data */
82 if (RINGBUF_ADD_CROSS(buf_widx, data_size, buf_ridx) >= 0) {
83 return NULL;
86 struct memory_handle *new_handle = (struct memory_handle *)(buffer + buf_widx);
87 buf_widx = RINGBUF_ADD(buf_widx, data_size);
89 if (!first_handle) {
90 /* the new handle is the first one */
91 first_handle = new_handle;
94 if (cur_handle) {
95 cur_handle->next = new_handle;
98 cur_handle = new_handle;
99 cur_handle->id = cur_handle_id++;
100 cur_handle->next = NULL;
101 num_handles++;
102 return cur_handle;
105 /* delete a given memory handle from the linked list
106 and return true for success. Nothing is actually erased from memory. */
107 static bool rm_handle(struct memory_handle *h)
109 if (h == first_handle) {
110 first_handle = h->next;
111 if (h == cur_handle) {
112 cur_handle = NULL;
114 } else {
115 struct memory_handle *m = first_handle;
116 while (m && m->next != h) {
117 m = m->next;
119 if (h && m && m->next == h) {
120 m->next = h->next;
121 if (h == cur_handle) {
122 cur_handle = m;
124 } else {
125 return false;
128 num_handles--;
129 return true;
132 /* Move a memory handle to newpos */
133 static struct memory_handle *move_handle(size_t newpos, struct memory_handle *h)
135 struct memory_handle *dest = (struct memory_handle *)(buffer + newpos);
137 if (h == cur_handle) {
138 cur_handle = dest;
141 if (h == first_handle) {
142 first_handle = dest;
143 } else {
144 struct memory_handle *m = first_handle;
145 while (m && m->next != h) {
146 m = m->next;
148 if (h && m && m->next == h) {
149 m->next = dest;
150 } else {
151 return NULL;
154 memmove(dest, h, sizeof(struct memory_handle));
155 return dest;
158 /* Return a pointer to the memory handle of given ID.
159 NULL if the handle wasn't found */
160 static struct memory_handle *find_handle(int handle_id)
162 static int last_handle_id = -1;
163 static struct memory_handle *last_m = NULL;
164 struct memory_handle *m = first_handle;
165 /* simple caching because most of the time the requested handle
166 will either be the same as the last, or the one after the last */
167 if (last_m)
169 if (last_handle_id == handle_id)
170 return last_m;
171 else if (last_m->next && (last_m->next->id == handle_id))
173 /* JD's quick testing showd this block was only entered
174 2/1971 calls to find_handle.
175 8/1971 calls to find_handle resulted in a cache miss */
176 last_m = last_m->next;
177 last_handle_id = handle_id;
178 return last_m;
181 while (m && m->id != handle_id) {
182 m = m->next;
184 last_handle_id = handle_id;
185 last_m = m;
186 return (m && m->id == handle_id) ? m : NULL;
189 /* Buffer data for the given handle. Return the amount of data buffered
190 or -1 if the handle wasn't found */
191 static int buffer_handle(int handle_id)
193 struct memory_handle *h = find_handle(handle_id);
194 //printf("find_handle %d:\nid: %d\npath: %s\n\n", handle_id, h->id, h->path);
195 if (!h)
196 return -1;
198 if (h->filerem == 0) {
199 /* nothing left to buffer */
200 return 0;
203 if (h->fd < 0) /* file closed, reopen */
205 if (*h->path)
206 h->fd = open(h->path, O_RDONLY);
207 else
208 return -1;
210 if (h->fd < 0)
211 return -1;
213 if (h->offset)
214 lseek(h->fd, h->offset, SEEK_SET);
217 int ret = 0;
218 while (h->filerem > 0)
220 /* max amount to copy */
221 size_t copy_n = MIN(conf_filechunk, buffer_len - buf_widx);
223 if (RINGBUF_ADD_CROSS(buf_widx, copy_n, buf_ridx) >= 0)
224 break;
226 /* rc is the actual amount read */
227 int rc = read(h->fd, &buffer[buf_widx], copy_n);
229 if (rc < 0)
231 printf("File ended %ldB early", (long)h->filerem);
232 h->filesize -= h->filerem;
233 h->filerem = 0;
234 break;
237 /* Advance buffer */
238 buf_widx = RINGBUF_ADD(buf_widx, rc);
239 h->available += rc;
240 ret += rc;
241 h->filerem -= rc;
244 if (h->filerem == 0) {
245 /* finished buffering the file */
246 close(h->fd);
249 printf("buffered %d bytes (%d of %d available, remaining: %d)\n",
250 ret, h->available, h->filesize, h->filerem);
251 return ret;
254 /* Request a file be buffered
255 filename: name of the file t open
256 offset: starting offset to buffer from the file
257 RETURNS: <0 if the file cannot be opened, or one file already
258 queued to be opened, otherwise the handle for the file in the buffer
260 int bufopen(char *file, size_t offset)
262 /* add the file to the buffering queue. */
263 /* for now, we'll assume the queue is always empty, so the handle
264 gets added immediately */
266 printf("bufopen: %s (offset: %d)\n", file, offset);
268 int fd = open(file, O_RDONLY);
269 if (fd < 0)
270 return -1;
272 if (offset)
273 lseek(fd, offset, SEEK_SET);
275 struct memory_handle *h = add_handle();
276 if (!h)
277 return -1;
278 strncpy(h->path, file, MAX_PATH);
279 h->fd = fd;
280 h->filesize = filesize(fd);
281 h->filerem = h->filesize - offset;
282 h->offset = offset;
283 h->buf_idx = buf_widx;
284 h->data = buf_widx;
285 h->available = 0;
287 printf("added handle : %d\n", h->id);
288 return h->id;
291 /* Close the handle. Return 0 for success and < 0 for failure */
292 int bufclose(int handle_id)
294 printf("bufclose: %d\n", handle_id);
295 struct memory_handle *h = find_handle(handle_id);
296 if (!h)
297 return -1;
299 /* when we close the first handle, we can reclaim the space from its buffer */
300 if (h == first_handle) {
301 buf_ridx = RINGBUF_ADD(h->buf_idx, h->available);
304 return rm_handle(h) ? 0 : -1;
307 /* Seek in a handle. Return 0 for success and < 0 for failure */
308 int bufseek(int handle_id, size_t offset)
310 struct memory_handle *h = find_handle(handle_id);
311 if (!h)
312 return -1;
314 h->buf_idx = RINGBUF_ADD(h->data, offset);
315 return 0;
318 /* Copy data from the given handle to the dest buffer.
319 Return the number of bytes copied or -1 for failure. */
320 int bufread(int handle_id, size_t size, char *dest)
322 struct memory_handle *h = find_handle(handle_id);
323 size_t buffered_data;
324 if (!h)
325 return -1;
326 if (h->available == 0)
327 return 0;
328 buffered_data = MIN(size,h->available);
330 if (h->buf_idx + buffered_data > buffer_len)
332 size_t read = buffer_len - h->buf_idx;
333 memcpy(dest, &buffer[h->buf_idx], read);
334 memcpy(dest+read, buffer, buffered_data - read);
336 else memcpy(dest, &buffer[h->buf_idx], buffered_data);
337 h->buf_idx = RINGBUF_ADD(h->buf_idx, buffered_data);
338 h->available -= buffered_data;
339 return buffered_data;
342 bool test_ll(void)
344 struct memory_handle *m1, *m2, *m3, *m4;
346 if (cur_handle != NULL || first_handle != NULL)
347 return false;
349 m1 = add_handle();
351 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
352 return false;
354 m2 = add_handle();
356 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
357 return false;
359 m3 = add_handle();
361 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
362 return false;
364 rm_handle(m2);
366 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
367 return false;
369 rm_handle(m3);
371 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
372 return false;
374 m4 = add_handle();
376 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
377 return false;
379 rm_handle(m1);
381 if (cur_handle != m4 || first_handle != m4)
382 return false;
384 rm_handle(m4);
386 if (cur_handle != NULL || first_handle != NULL)
387 return false;
389 m1 = add_handle();
390 m2 = add_handle();
391 m3 = add_handle();
392 m4 = add_handle();
394 if (cur_handle != m4 || first_handle != m1)
395 return false;
397 m2 = move_handle(m2->data + 1024*1024, m2);
399 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3)
400 return false;
402 m1 = move_handle(m1->data + 1024*1024*3, m1);
404 if (cur_handle != m4 || first_handle != m1 || m1->next != m2)
405 return false;
407 rm_handle(m1);
408 rm_handle(m2);
409 rm_handle(m3);
410 rm_handle(m4);
412 if (cur_handle != NULL || first_handle != NULL)
413 return false;
415 return true;
418 static void list_handles(void)
420 struct memory_handle *m = first_handle;
421 while (m) {
422 printf("%02d - %d\n", m->id, (void *)m-(void *)buffer);
423 m = m->next;
427 void buffer_init(void)
429 buffer = (char *)malloc(sizeof(char) * (BUFFER_SIZE + GUARD_SIZE));
430 if (!buffer)
432 printf("couldn't allocate buffer\n");
433 exit(1);
435 buffer_len = BUFFER_SIZE;
436 buffer_end = buffer + BUFFER_SIZE;
438 buf_widx = 0;
439 buf_ridx = 0;
441 first_handle = NULL;
442 num_handles = 0;
444 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
447 /* returns true if the file still has some on disk unread */
448 bool handle_has_data(int handle)
450 struct memory_handle *m = find_handle(handle);
451 if (m)
453 return m->filerem != 0;
455 return false;
458 bool need_rebuffer(void)
460 size_t free;
461 free = BUFFER_SIZE - BUF_USED;
462 return ((free >= BUFFER_SIZE/4));
465 #define MAX_HANDLES 64
466 int main(int argc, char *argv[])
468 int next_file = 1;
469 int last_handle = -1;
470 int handle_order[MAX_HANDLES];
471 int reading_handle = 0;
472 bool done = false;
473 char read_buffer[GUARD_SIZE];
474 int fd = -1; /* used to write the files out as they are read */
475 struct memory_handle *m = NULL;
476 buffer_init();
478 if (!test_ll())
480 printf("linked list test failed\n");
481 exit(1);
484 buffer_init();
486 while (done == false)
488 /* "Buffering thread" section */
489 if ((next_file <= argc) && need_rebuffer())
491 printf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
492 if ( !m || ((m->filerem == 0) && (m->next == NULL)))
494 int h = bufopen(argv[next_file++], 0);
495 m = find_handle(h);
496 if (h >= 0)
498 printf("new handle %d\n",h);
499 last_handle++;
500 handle_order[last_handle] = h;
501 buffer_handle(m->id);
504 else
506 if (m->filerem == 0)
507 m = m->next;
508 printf("buffering handle %d\n",m->id);
509 buffer_handle(m->id);
512 /* "Playback thread" section */
513 else
515 printf("reading handle: %d\n", handle_order[reading_handle]);
516 long read;
517 char file[MAX_PATH];
518 if (reading_handle >= last_handle)
519 done = true;
521 if (fd < 0)
523 snprintf(file, MAX_PATH, "./file%d.mp3", reading_handle);
524 fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0770);
525 if (fd < 0)
527 printf("ERROROROROR\n");
528 exit(1);
533 read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer);
534 write(fd, read_buffer, read);
536 while (read > 0);
537 /* close the fd/handle if there is no more data or an error */
538 if (read < 0 || handle_has_data(handle_order[reading_handle]) == false)
540 printf("finished reading %d\n",handle_order[reading_handle]);
541 bufclose(handle_order[reading_handle]);
542 close(fd);
543 reading_handle++;
544 fd = -1;
549 free(buffer);
550 return 0;