Attempt to implement buffer freeing. Something is wrong somewhere with the moving...
[Rockbox-MoB.git] / buffering.c
blob97808bbe045664471403af79615b21c5cdbd448f
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 0
168 if (last_m)
170 if (last_handle_id == handle_id && last_handle_id == last_m->id)
171 return last_m;
172 else if (last_m->next && (last_m->next->id == handle_id))
174 /* JD's quick testing showd this block was only entered
175 2/1971 calls to find_handle.
176 8/1971 calls to find_handle resulted in a cache miss */
177 last_m = last_m->next;
178 last_handle_id = handle_id;
179 return last_m;
182 #endif
183 while (m && m->id != handle_id) {
184 m = m->next;
186 last_handle_id = handle_id;
187 last_m = m;
188 return (m && m->id == handle_id) ? m : NULL;
191 /* Buffer data for the given handle. Return the amount of data buffered
192 or -1 if the handle wasn't found */
193 static int buffer_handle(int handle_id)
195 struct memory_handle *h = find_handle(handle_id);
196 //printf("find_handle %d:\nid: %d\npath: %s\n\n", handle_id, h->id, h->path);
197 if (!h)
198 return -1;
200 if (h->filerem == 0) {
201 /* nothing left to buffer */
202 return 0;
205 if (h->fd < 0) /* file closed, reopen */
207 if (*h->path)
208 h->fd = open(h->path, O_RDONLY);
209 else
210 return -1;
212 if (h->fd < 0)
213 return -1;
215 if (h->offset)
216 lseek(h->fd, h->offset, SEEK_SET);
219 int ret = 0;
220 while (h->filerem > 0)
222 //printf("h: %d\n", (void *)h - (void *)buffer);
223 //printf("buf_widx: %ld\n", (long)buf_widx);
224 /* max amount to copy */
225 size_t copy_n = MIN(conf_filechunk, buffer_len - buf_widx);
227 if (RINGBUF_ADD_CROSS(buf_widx, copy_n, buf_ridx) >= 0)
228 break;
230 /* rc is the actual amount read */
231 int rc = read(h->fd, &buffer[buf_widx], copy_n);
233 if (rc < 0)
235 printf("failed on fd %d at buf_widx = %ld for handle %d\n", h->fd, (long)buf_widx, h->id);
236 printf("File ended %ld bytes early\n", (long)h->filerem);
237 h->filesize -= h->filerem;
238 h->filerem = 0;
239 break;
242 /* Advance buffer */
243 buf_widx = RINGBUF_ADD(buf_widx, rc);
244 h->available += rc;
245 ret += rc;
246 h->filerem -= rc;
249 if (h->filerem == 0) {
250 /* finished buffering the file */
251 close(h->fd);
254 printf("buffered %d bytes (%d of %d available, remaining: %d)\n",
255 ret, h->available, h->filesize, h->filerem);
256 return ret;
259 /* this seems wrong */
260 static void free_buffer(void)
262 size_t delta = RINGBUF_SUB(first_handle->buf_idx, first_handle->data);
263 size_t dest = RINGBUF_ADD((void *)first_handle - (void *)buffer, delta);
264 printf("buf_ridx: %ld, buf_widx: %ld\n", (long)buf_ridx, (long)buf_widx);
265 printf("dest: %ld, delta: %ld\n", (long)dest, (long)delta);
266 first_handle = move_handle(dest, first_handle);
267 first_handle->data = RINGBUF_ADD(first_handle->data, delta);
268 first_handle->buf_idx = first_handle->data;
269 buf_ridx = dest;
272 /* Request a file be buffered
273 filename: name of the file t open
274 offset: starting offset to buffer from the file
275 RETURNS: <0 if the file cannot be opened, or one file already
276 queued to be opened, otherwise the handle for the file in the buffer
278 int bufopen(char *file, size_t offset)
280 /* add the file to the buffering queue. */
281 /* for now, we'll assume the queue is always empty, so the handle
282 gets added immediately */
284 printf("bufopen: %s (offset: %d)\n", file, offset);
286 int fd = open(file, O_RDONLY);
287 if (fd < 0)
288 return -1;
290 if (offset)
291 lseek(fd, offset, SEEK_SET);
293 struct memory_handle *h = add_handle();
294 if (!h)
295 return -1;
296 strncpy(h->path, file, MAX_PATH);
297 h->fd = fd;
298 h->filesize = filesize(fd);
299 h->filerem = h->filesize - offset;
300 h->offset = offset;
301 h->buf_idx = buf_widx;
302 h->data = buf_widx;
303 h->available = 0;
305 printf("added handle : %d\n", h->id);
306 return h->id;
309 /* Close the handle. Return 0 for success and < 0 for failure */
310 int bufclose(int handle_id)
312 printf("bufclose: %d\n", handle_id);
313 struct memory_handle *h = find_handle(handle_id);
314 if (!h)
315 return -1;
317 if (num_handles > 1) {
318 rm_handle(h);
319 buf_ridx = (size_t)first_handle - (size_t)buffer;
320 } else {
321 printf("closing the first and last handle\n");
322 /* num_handles == 1, therefore h == first_handle */
323 buf_ridx = buf_widx;
324 rm_handle(h);
327 return 0;
330 /* Seek in a handle. Return 0 for success and < 0 for failure */
331 int bufseek(int handle_id, size_t offset)
333 struct memory_handle *h = find_handle(handle_id);
334 if (!h)
335 return -1;
337 h->buf_idx = RINGBUF_ADD(h->data, offset);
338 return 0;
341 /* Copy data from the given handle to the dest buffer.
342 Return the number of bytes copied or -1 for failure. */
343 int bufread(int handle_id, size_t size, char *dest)
345 struct memory_handle *h = find_handle(handle_id);
346 size_t buffered_data;
347 if (!h)
348 return -1;
349 if (h->available == 0)
350 return 0;
351 buffered_data = MIN(size,h->available);
353 if (h->buf_idx + buffered_data > buffer_len)
355 size_t read = buffer_len - h->buf_idx;
356 memcpy(dest, &buffer[h->buf_idx], read);
357 memcpy(dest+read, buffer, buffered_data - read);
359 else memcpy(dest, &buffer[h->buf_idx], buffered_data);
361 h->buf_idx = RINGBUF_ADD(h->buf_idx, buffered_data);
362 h->available -= buffered_data;
363 return buffered_data;
366 bool test_ll(void)
368 struct memory_handle *m1, *m2, *m3, *m4;
370 if (cur_handle != NULL || first_handle != NULL)
371 return false;
373 m1 = add_handle();
375 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
376 return false;
378 m2 = add_handle();
380 if (cur_handle != m2 || first_handle != m1 || m1->next != m2 || m2->next != NULL)
381 return false;
383 m3 = add_handle();
385 if (cur_handle != m3 || first_handle != m1 || m2->next != m3 || m3->next != NULL)
386 return false;
388 rm_handle(m2);
390 if (cur_handle != m3 || first_handle != m1 || m1->next != m3)
391 return false;
393 rm_handle(m3);
395 if (cur_handle != m1 || first_handle != m1 || m1->next != NULL)
396 return false;
398 m4 = add_handle();
400 if (cur_handle != m4 || first_handle != m1 || m1->next != m4 || m4->next != NULL)
401 return false;
403 rm_handle(m1);
405 if (cur_handle != m4 || first_handle != m4)
406 return false;
408 rm_handle(m4);
410 if (cur_handle != NULL || first_handle != NULL)
411 return false;
413 m1 = add_handle();
414 m2 = add_handle();
415 m3 = add_handle();
416 m4 = add_handle();
418 if (cur_handle != m4 || first_handle != m1)
419 return false;
421 int m2id = m2->id;
422 m2 = move_handle(m2->data + 1024*1024, m2);
424 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 || m2->next != m3 ||
425 find_handle(m2id) != m2)
426 return false;
428 int m1id = m1->id;
429 m1 = move_handle(m1->data + 1024*1024*3, m1);
431 if (cur_handle != m4 || first_handle != m1 || m1->next != m2 ||
432 find_handle(m1id) != m1)
433 return false;
435 rm_handle(m1);
436 rm_handle(m2);
437 rm_handle(m3);
438 rm_handle(m4);
440 if (cur_handle != NULL || first_handle != NULL)
441 return false;
443 return true;
446 static void list_handles(void)
448 struct memory_handle *m = first_handle;
449 while (m) {
450 printf("%02d - %d\n", m->id, (void *)m-(void *)buffer);
451 m = m->next;
455 /* display a nice graphical view of the ringbuffer. */
456 static void graph_view(int width)
458 int i, r_pos, w_pos;
459 r_pos = buf_ridx * width / buffer_len;
460 w_pos = buf_widx * width / buffer_len;
462 printf("|");
463 for (i=0; i <= width; i++)
465 if (i != r_pos && i != w_pos)
467 if (buf_ridx <= buf_widx)
469 if (i > r_pos && i < w_pos)
470 printf(">");
471 else
472 printf("-");
474 else
476 if (i > r_pos || i < w_pos)
477 printf(">");
478 else
479 printf("-");
482 else
484 if (i == r_pos && i == w_pos)
486 if (buf_ridx <= buf_widx)
487 printf("RW");
488 else
489 printf("WR");
491 else if (i == r_pos)
492 printf("R");
493 else if (i == w_pos)
494 printf("W");
497 printf("|");
498 printf("\n");
501 void buffer_init(void)
503 buffer = (char *)malloc(sizeof(char) * (BUFFER_SIZE + GUARD_SIZE));
504 if (!buffer)
506 printf("couldn't allocate buffer\n");
507 exit(1);
509 buffer_len = BUFFER_SIZE;
510 buffer_end = buffer + BUFFER_SIZE;
512 buf_widx = 0;
513 buf_ridx = 0;
515 first_handle = NULL;
516 num_handles = 0;
518 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
521 /* returns true if the file still has some on disk unread */
522 bool handle_has_data(int handle)
524 struct memory_handle *m = find_handle(handle);
525 if (m)
527 return m->filerem != 0;
529 return false;
532 bool need_rebuffer(void)
534 size_t free;
535 free = BUFFER_SIZE - BUF_USED;
536 return ((free >= BUFFER_SIZE/4));
539 #define MAX_HANDLES 64
540 int main(int argc, char *argv[])
542 int next_file = 1;
543 int last_handle = -1;
544 int handle_order[MAX_HANDLES];
545 int reading_handle = 0;
546 bool done = false;
547 char read_buffer[GUARD_SIZE];
548 int fd = -1; /* used to write the files out as they are read */
549 struct memory_handle *m = NULL;
550 buffer_init();
552 if (!test_ll())
554 printf("linked list test failed\n");
555 exit(1);
558 buffer_init();
560 while (done == false)
562 printf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
563 graph_view(100);
565 /* "Buffering thread" section */
566 if ((next_file <= argc) && need_rebuffer())
568 if ( !m || ((m->filerem == 0) && (m->next == NULL)))
570 int h = bufopen(argv[next_file++], 0);
571 m = find_handle(h);
572 if (h >= 0)
574 printf("new handle %d\n",h);
575 last_handle++;
576 handle_order[last_handle] = h;
577 buffer_handle(m->id);
580 else
582 if (m->filerem == 0)
583 m = m->next;
584 printf("buffering handle %d\n",m->id);
585 buffer_handle(m->id);
588 /* "Playback thread" section */
589 else
591 printf("reading handle: %d\n", handle_order[reading_handle]);
592 long read;
593 long total = 0;
594 char file[MAX_PATH];
595 if (reading_handle >= last_handle
596 && !handle_has_data(handle_order[reading_handle]))
597 done = true;
599 if (fd < 0)
601 snprintf(file, MAX_PATH, "./file%d.mp3", reading_handle);
602 fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0770);
603 if (fd < 0)
605 printf("ERROROROROR\n");
606 exit(1);
611 read = bufread(handle_order[reading_handle], GUARD_SIZE,read_buffer);
612 write(fd, read_buffer, read);
613 total += read;
615 while (read > 0);
617 printf("read %ld bytes from handle %d\n", total, handle_order[reading_handle]);
619 /* close the fd/handle if there is no more data or an error */
620 if (read < 0 || handle_has_data(handle_order[reading_handle]) == false)
622 printf("finished reading %d\n",handle_order[reading_handle]);
623 bufclose(handle_order[reading_handle]);
624 close(fd);
625 reading_handle++;
626 fd = -1;
628 else
630 printf("there is data left to buffer for %d\n", handle_order[reading_handle]);
631 free_buffer();
636 printf("buffer usage: %d handles_used: %d\n", BUF_USED,num_handles);
637 graph_view(100);
639 free(buffer);
640 return 0;