diff options
-rw-r--r-- | apps/buffering.c | 930 | ||||
-rw-r--r-- | apps/buffering.h | 7 |
2 files changed, 457 insertions, 480 deletions
diff --git a/apps/buffering.c b/apps/buffering.c index 326228cbfa..abf1e4b821 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -18,44 +18,23 @@ * KIND, either express or implied. * ****************************************************************************/ - #include "config.h" -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <inttypes.h> -#include "buffering.h" - -#include "storage.h" #include "system.h" +#include "storage.h" #include "thread.h" -#include "file.h" -#include "panic.h" -#include "lcd.h" -#include "font.h" -#include "button.h" #include "kernel.h" -#include "tree.h" +#include "panic.h" #include "debug.h" -#include "settings.h" -#include "codecs.h" -#include "audio.h" -#include "mp3_playback.h" -#include "usb.h" -#include "screens.h" -#include "playlist.h" -#include "pcmbuf.h" +#include "file.h" #include "appevents.h" #include "metadata.h" +#include "bmp.h" #ifdef HAVE_ALBUMART #include "albumart.h" #include "jpeg_load.h" -#include "bmp.h" #include "playback.h" #endif - -#define GUARD_BUFSIZE (32*1024) +#include "buffering.h" /* Define LOGF_ENABLE to enable logf output in this file */ /* #define LOGF_ENABLE */ @@ -82,31 +61,37 @@ #define LOGFQUEUE_SYS_TIMEOUT(...) #endif +#define GUARD_BUFSIZE (32*1024) + /* amount of data to read in one read() call */ #define BUFFERING_DEFAULT_FILECHUNK (1024*32) #define BUF_HANDLE_MASK 0x7FFFFFFF +enum handle_flags +{ + H_CANWRAP = 0x1, /* Handle data may wrap in buffer */ + H_ALLOCALL = 0x2, /* All data must be allocated up front */ + H_FIXEDDATA = 0x4, /* Data is fixed in position */ +}; -/* assert(sizeof(struct memory_handle)%4==0) */ struct memory_handle { - int id; /* A unique ID for the handle */ - enum data_type type; /* Type of data buffered with this handle */ - int8_t pinned; /* Count of references */ - int8_t signaled; /* Stop any attempt at waiting to get the data */ - char path[MAX_PATH]; /* Path if data originated in a file */ - int fd; /* File descriptor to path (-1 if closed) */ - size_t data; /* Start index of the handle's data buffer */ - volatile size_t ridx; /* Read pointer, relative to the main buffer */ - size_t widx; /* Write pointer, relative to the main buffer */ - size_t filesize; /* File total length */ - size_t filerem; /* Remaining bytes of file NOT in buffer */ - volatile size_t available; /* Available bytes to read from buffer */ - size_t offset; /* Offset at which we started reading the file */ + int id; /* A unique ID for the handle */ + enum data_type type; /* Type of data buffered with this handle */ + uint8_t flags; /* Handle property flags */ + int8_t pinned; /* Count of pinnings */ + int8_t signaled; /* Stop any attempt at waiting to get the data */ + char path[MAX_PATH]; /* Path if data originated in a file */ + int fd; /* File descriptor to path (-1 if closed) */ + size_t data; /* Start index of the handle's data buffer */ + size_t ridx; /* Read pointer, relative to the main buffer */ + size_t widx; /* Write pointer, relative to the main buffer */ + ssize_t filesize; /* File total length */ + off_t start; /* Offset at which we started reading the file */ + off_t pos; /* Read position in file */ + off_t volatile end; /* Offset at which we stopped reading the file */ struct memory_handle *next; }; -/* invariant: filesize == offset + available + filerem */ - struct buf_message_data { @@ -119,10 +104,6 @@ static char *guard_buffer; static size_t buffer_len; -static volatile size_t buf_widx; /* current writing position */ -static volatile size_t buf_ridx; /* current reading position */ -/* buf_*idx are values relative to the buffer, not real pointers. */ - /* Configuration */ static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ static size_t high_watermark = 0; /* High watermark for rebuffer */ @@ -146,7 +127,6 @@ static struct memory_handle *cached_handle = NULL; static struct data_counters { size_t remaining; /* Amount of data needing to be buffered */ - size_t wasted; /* Amount of space available for freeing */ size_t buffered; /* Amount of data currently in the buffer */ size_t useful; /* Amount of data still useful to the user */ } data_counters; @@ -176,13 +156,24 @@ static unsigned int buffering_thread_id = 0; static struct event_queue buffering_queue SHAREDBSS_ATTR; static struct queue_sender_list buffering_queue_sender_list SHAREDBSS_ATTR; - +static void close_fd(int *fd_p) +{ + int fd = *fd_p; + if (fd >= 0) { + close(fd); + *fd_p = -1; + } +} /* Ring buffer helper functions */ +static inline void * ringbuf_ptr(uintptr_t p) +{ + return buffer + p; +} static inline uintptr_t ringbuf_offset(const void *ptr) { - return (uintptr_t)(ptr - (void*)buffer); + return (uintptr_t)(ptr - (void *)buffer); } /* Buffer pointer (p) plus value (v), wrapped if necessary */ @@ -194,7 +185,6 @@ static inline uintptr_t ringbuf_add(uintptr_t p, size_t v) return res; } - /* Buffer pointer (p) minus value (v), wrapped if necessary */ static inline uintptr_t ringbuf_sub(uintptr_t p, size_t v) { @@ -205,7 +195,6 @@ static inline uintptr_t ringbuf_sub(uintptr_t p, size_t v) return res - v; } - /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ static inline ssize_t ringbuf_add_cross(uintptr_t p1, size_t v, uintptr_t p2) { @@ -216,9 +205,6 @@ static inline ssize_t ringbuf_add_cross(uintptr_t p1, size_t v, uintptr_t p2) return res; } -/* Bytes available in the buffer */ -#define BUF_USED ringbuf_sub(buf_widx, buf_ridx) - /* Real buffer watermark */ #define BUF_WATERMARK MIN(conf_watermark, high_watermark) @@ -232,112 +218,119 @@ find_handle : Get a handle pointer from an ID move_handle : Move a handle in the buffer (with or without its data) These functions only handle the linked list structure. They don't touch the -contents of the struct memory_handle headers. They also change the buf_*idx -pointers when necessary and manage the handle IDs. +contents of the struct memory_handle headers. The first and current (== last) handle are kept track of. -A new handle is added at buf_widx and becomes the current one. -buf_widx always points to the current writing position for the current handle -buf_ridx always points to the location of the first handle. -buf_ridx == buf_widx means the buffer is empty. +A new handle is added at to the end and becomes the current one. + +num_handles = N +first_handle -> h0 -> h1 -> h2 -> ... hN-1 -> NULL + ^ +cur_handle -------------------------+ */ +static int next_handle_id(void) +{ + static int cur_handle_id = 0; + + /* Wrap signed int is safe and 0 doesn't happen */ + int next_hid = (cur_handle_id + 1) & BUF_HANDLE_MASK; + if (next_hid == 0) + next_hid = 1; + + cur_handle_id = next_hid; + + return next_hid; +} + +/* adds the handle to the linked list */ +static void link_cur_handle(struct memory_handle *h) +{ + h->next = NULL; + + if (first_handle) + cur_handle->next = h; + else + first_handle = h; /* the first one */ + + cur_handle = h; + num_handles++; +} /* Add a new handle to the linked list and return it. It will have become the new current handle. + flags contains information on how this may be allocated data_size must contain the size of what will be in the handle. - can_wrap tells us whether this type of data may wrap on buffer - alloc_all tells us if we must immediately be able to allocate data_size + widx_out points to variable to receive first available byte of data area returns a valid memory handle if all conditions for allocation are met. NULL if there memory_handle itself cannot be allocated or if the data_size cannot be allocated and alloc_all is set. */ -static struct memory_handle *add_handle(size_t data_size, bool can_wrap, - bool alloc_all) +static struct memory_handle * +add_handle(unsigned int flags, size_t data_size, size_t *data_out) { - /* gives each handle a unique id */ - static int cur_handle_id = 0; - size_t shift; - size_t widx, new_widx; - size_t len; - ssize_t overlap; - + /* Gives each handle a unique id */ if (num_handles >= BUF_MAX_HANDLES) return NULL; - widx = buf_widx; + size_t ridx = 0, widx = 0; + off_t cur_total = 0; + + if (first_handle) { + /* Buffer is not empty */ + ridx = ringbuf_offset(first_handle); + widx = cur_handle->data; + cur_total = cur_handle->filesize - cur_handle->start; + } - if (cur_handle && cur_handle->filerem > 0) { + if (cur_total > 0) { /* the current handle hasn't finished buffering. We can only add a new one if there is already enough free space to finish the buffering. */ - size_t req = cur_handle->filerem; - if (ringbuf_add_cross(cur_handle->widx, req, buf_ridx) >= 0) { + if (ringbuf_add_cross(widx, cur_total, ridx) >= 0) { /* Not enough space to finish allocation */ return NULL; } else { - /* Allocate the remainder of the space for the current handle */ - widx = ringbuf_add(cur_handle->widx, cur_handle->filerem); + /* Apply all the needed reserve */ + widx = ringbuf_add(widx, cur_total); } } - /* align to 4 bytes up always leaving a gap */ - new_widx = ringbuf_add(widx, 4) & ~3; - - len = data_size + sizeof(struct memory_handle); + /* Align to pointer size up */ + size_t adjust = ALIGN_UP(widx, sizeof(intptr_t)) - widx; + size_t index = ringbuf_add(widx, adjust); + size_t len = data_size + sizeof(struct memory_handle); /* First, will the handle wrap? */ /* If the handle would wrap, move to the beginning of the buffer, * or if the data must not but would wrap, move it to the beginning */ - if (new_widx + sizeof(struct memory_handle) > buffer_len || - (!can_wrap && new_widx + len > buffer_len)) { - new_widx = 0; + if (index + sizeof(struct memory_handle) > buffer_len || + (!(flags & H_CANWRAP) && index + len > buffer_len)) { + index = 0; } - /* How far we shifted the new_widx to align things, must be < buffer_len */ - shift = ringbuf_sub(new_widx, widx); + /* How far we shifted index to align things, must be < buffer_len */ + size_t shift = ringbuf_sub(index, widx); /* How much space are we short in the actual ring buffer? */ - overlap = ringbuf_add_cross(widx, shift + len, buf_ridx); - if (overlap >= 0 && (alloc_all || (size_t)overlap >= data_size)) { + ssize_t overlap = ringbuf_add_cross(widx, shift + len, ridx); + if (overlap >= 0 && + ((flags & H_ALLOCALL) || (size_t)overlap >= data_size)) { /* Not enough space for required allocations */ return NULL; } - /* There is enough space for the required data, advance the buf_widx and - * initialize the struct */ - buf_widx = new_widx; - - struct memory_handle *new_handle = - (struct memory_handle *)(&buffer[buf_widx]); - - /* Prevent buffering thread from looking at it */ - new_handle->filerem = 0; - - /* Handle can be moved by default */ - new_handle->pinned = 0; - - /* Handle data can be waited for by default */ - new_handle->signaled = 0; + /* There is enough space for the required data, initialize the struct */ + struct memory_handle *h = ringbuf_ptr(index); - /* only advance the buffer write index of the size of the struct */ - buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); + h->id = next_handle_id(); + h->flags = flags; + h->pinned = 0; /* Can be moved */ + h->signaled = 0; /* Data can be waited for */ - new_handle->id = cur_handle_id; - /* Wrap signed int is safe and 0 doesn't happen */ - cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; - new_handle->next = NULL; - num_handles++; - - if (!first_handle) - /* the new handle is the first one */ - first_handle = new_handle; + /* Return the start of the data area */ + *data_out = ringbuf_add(index, sizeof (struct memory_handle)); - if (cur_handle) - cur_handle->next = new_handle; - - cur_handle = new_handle; - - return new_handle; + return h; } /* Delete a given memory handle from the linked list @@ -347,28 +340,25 @@ static bool rm_handle(const struct memory_handle *h) if (h == NULL) return true; - if (h == first_handle) { - first_handle = h->next; - if (h == cur_handle) { + struct memory_handle *m = first_handle; + struct memory_handle *c = cur_handle; + + if (h == m) { + m = m->next; + first_handle = m; + if (!m) { /* h was the first and last handle: the buffer is now empty */ cur_handle = NULL; - buf_ridx = buf_widx = 0; - } else { - /* update buf_ridx to point to the new first handle */ - buf_ridx = (size_t)ringbuf_offset(first_handle); } } else { - struct memory_handle *m = first_handle; /* Find the previous handle */ while (m && m->next != h) { m = m->next; } if (m && m->next == h) { m->next = h->next; - if (h == cur_handle) { + if (h == c) cur_handle = m; - buf_widx = cur_handle->widx; - } } else { /* If we don't find ourselves, this is a seriously incoherent state with a corrupted list and severe action is needed! */ @@ -392,15 +382,18 @@ static struct memory_handle *find_handle(int handle_id) if (handle_id < 0 || !first_handle) return NULL; - /* simple caching because most of the time the requested handle - will either be the same as the last, or the one after the last */ - if (cached_handle) { - if (cached_handle->id == handle_id) { - return cached_handle; - } else if (cached_handle->next && - (cached_handle->next->id == handle_id)) { - cached_handle = cached_handle->next; - return cached_handle; + /* Simple caching because most of the time the requested handle + will either be the same as the last, or the one after the last */ + struct memory_handle *cached = cached_handle; + if (cached) { + if (cached->id == handle_id) { + return cached; + } else { + cached = cached->next; + if (cached && cached->id == handle_id) { + cached_handle = cached; + return cached; + } } } @@ -408,6 +401,7 @@ static struct memory_handle *find_handle(int handle_id) while (m && m->id != handle_id) { m = m->next; } + /* This condition can only be reached with !m or m->id == handle_id */ if (m) cached_handle = m; @@ -425,36 +419,33 @@ static struct memory_handle *find_handle(int handle_id) list for adjustment. This function has no side effects if false is returned. */ static bool move_handle(struct memory_handle **h, size_t *delta, - size_t data_size, bool can_wrap) + size_t data_size) { - struct memory_handle *dest; const struct memory_handle *src; - size_t final_delta = *delta, size_to_move; - uintptr_t oldpos, newpos; - intptr_t overlap, overlap_old; if (h == NULL || (src = *h) == NULL) return false; - size_to_move = sizeof(struct memory_handle) + data_size; + size_t size_to_move = sizeof(struct memory_handle) + data_size; - /* Align to four bytes, down */ - final_delta &= ~3; + /* Align to pointer size down */ + size_t final_delta = *delta; + final_delta = ALIGN_DOWN(final_delta, sizeof(intptr_t)); if (final_delta < sizeof(struct memory_handle)) { /* It's not legal to move less than the size of the struct */ return false; } - oldpos = ringbuf_offset(src); - newpos = ringbuf_add(oldpos, final_delta); - overlap = ringbuf_add_cross(newpos, size_to_move, buffer_len); - overlap_old = ringbuf_add_cross(oldpos, size_to_move, buffer_len); + uintptr_t oldpos = ringbuf_offset(src); + uintptr_t newpos = ringbuf_add(oldpos, final_delta); + intptr_t overlap = ringbuf_add_cross(newpos, size_to_move, buffer_len); + intptr_t overlap_old = ringbuf_add_cross(oldpos, size_to_move, buffer_len); if (overlap > 0) { /* Some part of the struct + data would wrap, maybe ok */ ssize_t correction = 0; /* If the overlap lands inside the memory_handle */ - if (!can_wrap) { + if (!(src->flags & H_CANWRAP)) { /* Otherwise the overlap falls in the data area and must all be * backed out. This may become conditional if ever we move * data that is allowed to wrap (ie audio) */ @@ -466,8 +457,8 @@ static bool move_handle(struct memory_handle **h, size_t *delta, correction = overlap - data_size; } if (correction) { - /* Align correction to four bytes up */ - correction = (correction + 3) & ~3; + /* Align correction to pointer size up */ + correction = ALIGN_UP(correction, sizeof(intptr_t)); if (final_delta < correction + sizeof(struct memory_handle)) { /* Delta cannot end up less than the size of the struct */ return false; @@ -478,11 +469,10 @@ static bool move_handle(struct memory_handle **h, size_t *delta, } } - dest = (struct memory_handle *)(&buffer[newpos]); + struct memory_handle *dest = ringbuf_ptr(newpos); if (src == first_handle) { first_handle = dest; - buf_ridx = newpos; } else { struct memory_handle *m = first_handle; while (m && m->next != src) { @@ -533,7 +523,7 @@ static bool move_handle(struct memory_handle **h, size_t *delta, */ if (overlap_old > 0) { /* Move over already wrapped data by the final delta */ - memmove(&buffer[final_delta], buffer, overlap_old); + memmove(ringbuf_ptr(final_delta), ringbuf_ptr(0), overlap_old); if (overlap <= 0) size_to_move -= overlap_old; } @@ -541,7 +531,7 @@ static bool move_handle(struct memory_handle **h, size_t *delta, if (overlap > 0) { /* Move data that now wraps to the beginning */ size_to_move -= overlap; - memmove(buffer, SKIPBYTES(src, size_to_move), + memmove(ringbuf_ptr(0), SKIPBYTES(src, size_to_move), overlap_old > 0 ? final_delta : (size_t)overlap); } @@ -568,59 +558,44 @@ fill_buffer : Call buffer_handle for all handles that have data to buffer These functions are used by the buffering thread to manage buffer space. */ -static size_t handle_size_available(const struct memory_handle *h) -{ - /* Obtain proper distances from data start */ - size_t rd = ringbuf_sub(h->ridx, h->data); - size_t wr = ringbuf_sub(h->widx, h->data); - - if (LIKELY(wr > rd)) - return wr - rd; - - return 0; /* ridx is ahead of or equal to widx at this time */ -} -static void update_data_counters(struct data_counters *dc) +static int update_data_counters(struct data_counters *dc) { - size_t buffered = 0; - size_t wasted = 0; + size_t buffered = 0; size_t remaining = 0; - size_t useful = 0; - - struct memory_handle *m; - bool is_useful; + size_t useful = 0; if (dc == NULL) dc = &data_counters; mutex_lock(&llist_mutex); - m = find_handle(base_handle_id); - is_useful = m == NULL; + int num = num_handles; + struct memory_handle *m = find_handle(base_handle_id); + bool is_useful = m == NULL; + + for (m = first_handle; m; m = m->next) + { + off_t pos = m->pos; + off_t end = m->end; - m = first_handle; - while (m) { - buffered += m->available; - /* wasted could come out larger than the buffer size if ridx's are - overlapping data ahead of their handles' buffered data */ - wasted += ringbuf_sub(m->ridx, m->data); - remaining += m->filerem; + buffered += end - m->start; + remaining += m->filesize - end; if (m->id == base_handle_id) is_useful = true; if (is_useful) - useful += handle_size_available(m); - - m = m->next; + useful += end - pos; } mutex_unlock(&llist_mutex); - dc->buffered = buffered; - dc->wasted = wasted; + dc->buffered = buffered; dc->remaining = remaining; - dc->useful = useful; + dc->useful = useful; + + return num; } static inline bool buffer_is_low(void) @@ -635,62 +610,64 @@ static bool buffer_handle(int handle_id, size_t to_buffer) { logf("buffer_handle(%d, %lu)", handle_id, (unsigned long)to_buffer); struct memory_handle *h = find_handle(handle_id); - bool stop = false; - if (!h) return true; logf(" type: %d", (int)h->type); - if (h->filerem == 0) { + if (h->end >= h->filesize) { /* nothing left to buffer */ return true; } if (h->fd < 0) { /* file closed, reopen */ - if (*h->path) + if (h->path[0] != '\0') h->fd = open(h->path, O_RDONLY); - if (h->fd < 0) - { + if (h->fd < 0) { /* could not open the file, truncate it where it is */ - h->filesize -= h->filerem; - h->filerem = 0; + h->filesize = h->end; return true; } - if (h->offset) - lseek(h->fd, h->offset, SEEK_SET); + if (h->start) + lseek(h->fd, h->start, SEEK_SET); } trigger_cpu_boost(); if (h->type == TYPE_ID3) { - if (!get_metadata((struct mp3entry *)(buffer + h->data), - h->fd, h->path)) { + if (!get_metadata(ringbuf_ptr(h->data), h->fd, h->path)) { /* metadata parsing failed: clear the buffer. */ - wipe_mp3entry((struct mp3entry *)(buffer + h->data)); + wipe_mp3entry(ringbuf_ptr(h->data)); } - close(h->fd); - h->fd = -1; - h->filerem = 0; - h->available = sizeof(struct mp3entry); - h->widx = ringbuf_add(h->widx, sizeof(struct mp3entry)); + close_fd(&h->fd); + h->widx = ringbuf_add(h->data, h->filesize); + h->end = h->filesize; send_event(BUFFER_EVENT_FINISHED, &handle_id); return true; } - while (h->filerem > 0 && !stop) + bool stop = false; + while (h->end < h->filesize && !stop) { /* max amount to copy */ - ssize_t copy_n = MIN( MIN(h->filerem, BUFFERING_DEFAULT_FILECHUNK), - buffer_len - h->widx); - uintptr_t offset = h->next ? ringbuf_offset(h->next) : buf_ridx; - ssize_t overlap = ringbuf_add_cross(h->widx, copy_n, offset) + 1; + size_t widx = h->widx; + + ssize_t copy_n = h->filesize - h->end; + copy_n = MIN(copy_n, BUFFERING_DEFAULT_FILECHUNK); + copy_n = MIN(copy_n, (off_t)(buffer_len - widx)); + + uintptr_t offset = ringbuf_offset(h->next ?: first_handle); + ssize_t overlap = ringbuf_add_cross(widx, copy_n, offset); + + /* read only up to available space and stop if it would overwrite + the next handle; stop one byte early for last handle to avoid + empty/full alias */ + if (!h->next) + overlap++; if (overlap > 0) { - /* read only up to available space and stop if it would overwrite - or be on top of the reading position or the next handle */ stop = true; copy_n -= overlap; } @@ -699,7 +676,7 @@ static bool buffer_handle(int handle_id, size_t to_buffer) return false; /* no space for read */ /* rc is the actual amount read */ - int rc = read(h->fd, &buffer[h->widx], copy_n); + ssize_t rc = read(h->fd, ringbuf_ptr(widx), copy_n); if (rc <= 0) { /* Some kind of filesystem error, maybe recoverable if not codec */ @@ -708,31 +685,21 @@ static bool buffer_handle(int handle_id, size_t to_buffer) break; } - logf("File ended %ld bytes early\n", (long)h->filerem); - h->filesize -= h->filerem; - h->filerem = 0; + logf("File ended %lu bytes early\n", + (unsigned long)(h->filesize - h->end)); + h->filesize = h->end; break; } - /* Advance buffer */ - h->widx = ringbuf_add(h->widx, rc); - if (h == cur_handle) - buf_widx = h->widx; - h->available += rc; - h->filerem -= rc; - - /* If this is a large file, see if we need to break or give the codec - * more time */ - if (h->type == TYPE_PACKET_AUDIO && - pcmbuf_is_lowdata() && !buffer_is_low()) { - sleep(1); - } else { - yield(); - } + /* Advance buffer and make data available to users */ + h->widx = ringbuf_add(widx, rc); + h->end += rc; + + yield(); if (to_buffer == 0) { /* Normal buffering - check queue */ - if(!queue_empty(&buffering_queue)) + if (!queue_empty(&buffering_queue)) break; } else { if (to_buffer <= (size_t)rc) @@ -741,10 +708,9 @@ static bool buffer_handle(int handle_id, size_t to_buffer) } } - if (h->filerem == 0) { + if (h->end >= h->filesize) { /* finished buffering the file */ - close(h->fd); - h->fd = -1; + close_fd(&h->fd); send_event(BUFFER_EVENT_FINISHED, &handle_id); } @@ -752,21 +718,17 @@ static bool buffer_handle(int handle_id, size_t to_buffer) } /* Close the specified handle id and free its allocation. */ +/* Q_CLOSE_HANDLE */ static bool close_handle(int handle_id) { bool retval = true; - struct memory_handle *h; mutex_lock(&llist_mutex); - h = find_handle(handle_id); + struct memory_handle *h = find_handle(handle_id); /* If the handle is not found, it is closed */ if (h) { - if (h->fd >= 0) { - close(h->fd); - h->fd = -1; - } - + close_fd(&h->fd); /* rm_handle returns true unless the handle somehow persists after exit */ retval = rm_handle(h); @@ -791,23 +753,23 @@ static void shrink_handle(struct memory_handle *h) size_t delta = ringbuf_sub(h->ridx, h->data); /* The value of delta might change for alignment reasons */ - if (!move_handle(&h, &delta, 0, true)) + if (!move_handle(&h, &delta, 0)) return; h->data = ringbuf_add(h->data, delta); - h->available -= delta; - h->offset += delta; + h->start += delta; } else { /* metadata handle: we can move all of it */ - if (h->pinned || !h->next || h->filerem != 0) - return; /* Pinned, last handle or not finished loading */ + if (h->pinned || !h->next) + return; /* Pinned, last handle */ + size_t data_size = h->filesize - h->start; uintptr_t handle_distance = ringbuf_sub(ringbuf_offset(h->next), h->data); - size_t delta = handle_distance - h->available; + size_t delta = handle_distance - data_size; /* The value of delta might change for alignment reasons */ - if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC)) + if (!move_handle(&h, &delta, data_size)) return; size_t olddata = h->data; @@ -815,15 +777,24 @@ static void shrink_handle(struct memory_handle *h) h->ridx = ringbuf_add(h->ridx, delta); h->widx = ringbuf_add(h->widx, delta); - if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) { - /* when moving an mp3entry we need to readjust its pointers. */ - adjust_mp3entry((struct mp3entry *)&buffer[h->data], - (void *)&buffer[h->data], - (const void *)&buffer[olddata]); - } else if (h->type == TYPE_BITMAP) { - /* adjust the bitmap's pointer */ - struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; - bmp->data = &buffer[h->data + sizeof(struct bitmap)]; + switch (h->type) + { + case TYPE_ID3: + if (h->filesize != sizeof(struct mp3entry)) + break; + /* when moving an mp3entry we need to readjust its pointers */ + adjust_mp3entry(ringbuf_ptr(h->data), ringbuf_ptr(h->data), + ringbuf_ptr(olddata)); + break; + + case TYPE_BITMAP: + /* adjust the bitmap's pointer */ + ((struct bitmap *)ringbuf_ptr(h->data))->data = + ringbuf_ptr(h->data + sizeof(struct bitmap)); + break; + + default: + break; } } } @@ -839,11 +810,9 @@ static bool fill_buffer(void) shrink_handle(m); while (queue_empty(&buffering_queue) && m) { - if (m->filerem > 0) { - if (!buffer_handle(m->id, 0)) { - m = NULL; - break; - } + if (m->end < m->filesize && !buffer_handle(m->id, 0)) { + m = NULL; + break; } m = m->next; } @@ -863,26 +832,24 @@ static bool fill_buffer(void) buffer, with a struct bitmap and the actual data immediately following. Return value is the total size (struct + data). */ static int load_image(int fd, const char *path, - struct bufopen_bitmap_data *data) + struct bufopen_bitmap_data *data, + size_t bufidx) { int rc; - struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + struct bitmap *bmp = ringbuf_ptr(bufidx); struct dim *dim = data->dim; struct mp3_albumart *aa = data->embedded_albumart; /* get the desired image size */ bmp->width = dim->width, bmp->height = dim->height; /* FIXME: alignment may be needed for the data buffer. */ - bmp->data = &buffer[buf_widx + sizeof(struct bitmap)]; -#ifndef HAVE_JPEG - (void) path; -#endif + bmp->data = ringbuf_ptr(bufidx + sizeof(struct bitmap)); + #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) bmp->maskdata = NULL; #endif - - int free = (int)MIN(buffer_len - BUF_USED, buffer_len - buf_widx) - - sizeof(struct bitmap); + int free = (int)MIN(buffer_len - buf_used(), buffer_len - bufidx) + - sizeof(struct bitmap); #ifdef HAVE_JPEG if (aa != NULL) { @@ -892,14 +859,16 @@ static int load_image(int fd, const char *path, } else if (strcmp(path + strlen(path) - 4, ".bmp")) rc = read_jpeg_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| - FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); else #endif rc = read_bmp_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + return rc + (rc > 0 ? sizeof(struct bitmap) : 0); + (void)path; } -#endif +#endif /* HAVE_ALBUMART */ /* @@ -933,11 +902,9 @@ management functions for all the actual handle management work. int bufopen(const char *file, size_t offset, enum data_type type, void *user_data) { -#ifndef HAVE_ALBUMART - /* currently only used for aa loading */ - (void)user_data; -#endif int handle_id = ERR_BUFFER_FULL; + size_t data; + struct memory_handle *h; /* No buffer refs until after the mutex_lock call! */ @@ -945,24 +912,23 @@ int bufopen(const char *file, size_t offset, enum data_type type, /* ID3 case: allocate space, init the handle and return. */ mutex_lock(&llist_mutex); - struct memory_handle *h = - add_handle(sizeof(struct mp3entry), false, true); + h = add_handle(H_ALLOCALL, sizeof(struct mp3entry), &data); if (h) { handle_id = h->id; - h->fd = -1; - h->filesize = sizeof(struct mp3entry); - h->offset = 0; - h->data = buf_widx; - h->ridx = buf_widx; - h->widx = buf_widx; - h->available = 0; - h->type = type; - strlcpy(h->path, file, MAX_PATH); - buf_widx = ringbuf_add(buf_widx, sizeof(struct mp3entry)); + h->type = type; + strlcpy(h->path, file, MAX_PATH); + h->fd = -1; + h->data = data; + h->ridx = data; + h->widx = data; + h->filesize = sizeof(struct mp3entry); + h->start = 0; + h->pos = 0; + h->end = 0; - h->filerem = sizeof(struct mp3entry); + link_cur_handle(h); /* Inform the buffering thread that we added a handle */ LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", handle_id); @@ -975,7 +941,7 @@ int bufopen(const char *file, size_t offset, enum data_type type, else if (type == TYPE_UNKNOWN) return ERR_UNSUPPORTED_TYPE; #ifdef APPLICATION - /* loading code from memory is not supported in application builds */ + /* Loading code from memory is not supported in application builds */ else if (type == TYPE_CODEC) return ERR_UNSUPPORTED_TYPE; #endif @@ -987,27 +953,31 @@ int bufopen(const char *file, size_t offset, enum data_type type, size_t size = 0; #ifdef HAVE_ALBUMART if (type == TYPE_BITMAP) { - /* if albumart is embedded, the complete file is not buffered, + /* If albumart is embedded, the complete file is not buffered, * but only the jpeg part; filesize() would be wrong */ - struct bufopen_bitmap_data *aa = (struct bufopen_bitmap_data*)user_data; + struct bufopen_bitmap_data *aa = user_data; if (aa->embedded_albumart) size = aa->embedded_albumart->size; } #endif + if (size == 0) size = filesize(fd); - bool can_wrap = type==TYPE_PACKET_AUDIO || type==TYPE_CODEC; + + unsigned int hflags = 0; + if (type == TYPE_PACKET_AUDIO || type == TYPE_CODEC) + hflags = H_CANWRAP; size_t adjusted_offset = offset; if (adjusted_offset > size) adjusted_offset = 0; /* Reserve extra space because alignment can move data forward */ - size_t padded_size = STORAGE_PAD(size-adjusted_offset); + size_t padded_size = STORAGE_PAD(size - adjusted_offset); mutex_lock(&llist_mutex); - struct memory_handle *h = add_handle(padded_size, can_wrap, false); + h = add_handle(hflags, padded_size, &data); if (!h) { DEBUGF("%s(): failed to add handle\n", __func__); mutex_unlock(&llist_mutex); @@ -1016,8 +986,10 @@ int bufopen(const char *file, size_t offset, enum data_type type, } handle_id = h->id; + + h->type = type; strlcpy(h->path, file, MAX_PATH); - h->offset = adjusted_offset; + h->fd = -1; #ifdef STORAGE_WANTS_ALIGN /* Don't bother to storage align bitmaps because they are not @@ -1025,44 +997,40 @@ int bufopen(const char *file, size_t offset, enum data_type type, */ if (type != TYPE_BITMAP) { /* Align to desired storage alignment */ - size_t alignment_pad = STORAGE_OVERLAP(adjusted_offset - - (size_t)(&buffer[buf_widx])); - buf_widx = ringbuf_add(buf_widx, alignment_pad); + size_t alignment_pad = STORAGE_OVERLAP((uintptr_t)adjusted_offset - + (uintptr_t)ringbuf_ptr(data)); + data = ringbuf_add(data, alignment_pad); } #endif /* STORAGE_WANTS_ALIGN */ - h->fd = -1; - h->data = buf_widx; - h->ridx = buf_widx; - h->widx = buf_widx; - h->available = 0; - h->type = type; + h->data = data; + h->ridx = data; + h->start = adjusted_offset; + h->pos = adjusted_offset; #ifdef HAVE_ALBUMART if (type == TYPE_BITMAP) { /* Bitmap file: we load the data instead of the file */ - int rc; - rc = load_image(fd, file, (struct bufopen_bitmap_data*)user_data); + int rc = load_image(fd, file, user_data, data); if (rc <= 0) { - rm_handle(h); handle_id = ERR_FILE_ERROR; } else { - h->filesize = rc; - h->available = rc; - buf_widx = ringbuf_add(buf_widx, rc); - h->widx = buf_widx; + data = ringbuf_add(data, rc); + size = rc; + adjusted_offset = rc; } } else #endif - { - if (type == TYPE_CUESHEET) - h->fd = fd; + if (type == TYPE_CUESHEET) { + h->fd = fd; + } + if (handle_id >= 0) { + h->widx = data; h->filesize = size; - h->available = 0; - h->widx = buf_widx; - h->filerem = size - adjusted_offset; + h->end = adjusted_offset; + link_cur_handle(h); } mutex_unlock(&llist_mutex); @@ -1084,6 +1052,9 @@ int bufopen(const char *file, size_t offset, enum data_type type, logf("bufopen: new hdl %d", handle_id); return handle_id; + + /* Currently only used for aa loading */ + (void)user_data; } /* Open a new handle from data that needs to be copied from memory. @@ -1095,16 +1066,15 @@ int bufopen(const char *file, size_t offset, enum data_type type, */ int bufalloc(const void *src, size_t size, enum data_type type) { - int handle_id; - if (type == TYPE_UNKNOWN) return ERR_UNSUPPORTED_TYPE; - handle_id = ERR_BUFFER_FULL; + int handle_id = ERR_BUFFER_FULL; mutex_lock(&llist_mutex); - struct memory_handle *h = add_handle(size, false, true); + size_t data; + struct memory_handle *h = add_handle(H_ALLOCALL, size, &data); if (h) { handle_id = h->id; @@ -1112,23 +1082,24 @@ int bufalloc(const void *src, size_t size, enum data_type type) if (src) { if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { /* specially take care of struct mp3entry */ - copy_mp3entry((struct mp3entry *)&buffer[buf_widx], - (const struct mp3entry *)src); + copy_mp3entry(ringbuf_ptr(data), src); } else { - memcpy(&buffer[buf_widx], src, size); + memcpy(ringbuf_ptr(data), src, size); } } - h->fd = -1; - *h->path = 0; - h->filesize = size; - h->offset = 0; - h->ridx = buf_widx; - h->data = buf_widx; - buf_widx = ringbuf_add(buf_widx, size); - h->widx = buf_widx; - h->available = size; - h->type = type; + h->type = type; + h->path[0] = '\0'; + h->fd = -1; + h->data = data; + h->ridx = data; + h->widx = ringbuf_add(data, size); + h->filesize = size; + h->start = 0; + h->pos = 0; + h->end = size; + + link_cur_handle(h); } mutex_unlock(&llist_mutex); @@ -1155,32 +1126,36 @@ bool bufclose(int handle_id) /* Backend to bufseek and bufadvance. Call only in response to Q_REBUFFER_HANDLE! */ -static void rebuffer_handle(int handle_id, size_t newpos) +static void rebuffer_handle(int handle_id, off_t newpos) { struct memory_handle *h = find_handle(handle_id); - if (!h) { queue_reply(&buffering_queue, ERR_HANDLE_NOT_FOUND); return; } + /* Check that we still need to do this since the request could have + possibly been met by this time */ + if (newpos >= h->start && newpos <= h->end) { + h->ridx = ringbuf_add(h->data, newpos - h->start); + h->pos = newpos; + queue_reply(&buffering_queue, 0); + return; + } + /* When seeking foward off of the buffer, if it is a short seek attempt to avoid rebuffering the whole track, just read enough to satisfy */ - if (newpos > h->offset && - newpos - h->offset < BUFFERING_DEFAULT_FILECHUNK) { - - size_t amount = newpos - h->offset; - h->ridx = ringbuf_add(h->data, amount); - - if (buffer_handle(handle_id, amount + 1)) { - size_t rd = ringbuf_sub(h->ridx, h->data); - size_t wr = ringbuf_sub(h->widx, h->data); - if (wr >= rd) { - /* It really did succeed */ - queue_reply(&buffering_queue, 0); - buffer_handle(handle_id, 0); /* Ok, try the rest */ - return; - } + off_t amount = newpos - h->pos; + + if (amount > 0 && amount <= BUFFERING_DEFAULT_FILECHUNK) { + h->ridx = ringbuf_add(h->data, newpos - h->start); + h->pos = newpos; + + if (buffer_handle(handle_id, amount + 1) && h->end >= h->pos) { + /* It really did succeed */ + queue_reply(&buffering_queue, 0); + buffer_handle(handle_id, 0); /* Ok, try the rest */ + return; } /* Data collision or other file error - must reset */ @@ -1188,10 +1163,7 @@ static void rebuffer_handle(int handle_id, size_t newpos) newpos = h->filesize; /* file truncation happened above */ } - /* Reset the handle to its new position */ - h->offset = newpos; - - size_t next = h->next ? ringbuf_offset(h->next) : buf_ridx; + size_t next = ringbuf_offset(h->next ?: first_handle); #ifdef STORAGE_WANTS_ALIGN /* Strip alignment padding then redo */ @@ -1200,8 +1172,8 @@ static void rebuffer_handle(int handle_id, size_t newpos) /* Align to desired storage alignment if space permits - handle could have been shrunken too close to the following one after a previous rebuffer. */ - size_t alignment_pad = - STORAGE_OVERLAP(h->offset - (size_t)(&buffer[new_index])); + size_t alignment_pad = STORAGE_OVERLAP((uintptr_t)newpos - + (uintptr_t)ringbuf_ptr(new_index)); if (ringbuf_add_cross(new_index, alignment_pad, next) >= 0) alignment_pad = 0; /* Forego storage alignment this time */ @@ -1212,23 +1184,19 @@ static void rebuffer_handle(int handle_id, size_t newpos) size_t new_index = h->data; #endif /* STORAGE_WANTS_ALIGN */ + /* Reset the handle to its new position */ h->ridx = h->widx = h->data = new_index; - - if (h == cur_handle) - buf_widx = new_index; - - h->available = 0; - h->filerem = h->filesize - h->offset; + h->start = h->pos = h->end = newpos; if (h->fd >= 0) - lseek(h->fd, h->offset, SEEK_SET); + lseek(h->fd, newpos, SEEK_SET); - if (h->next && ringbuf_sub(next, h->data) <= h->filesize - newpos) { + off_t filerem = h->filesize - newpos; + if (h->next && ringbuf_add_cross(new_index, filerem, next) > 0) { /* There isn't enough space to rebuffer all of the track from its new offset, so we ask the user to free some */ DEBUGF("%s(): space is needed\n", __func__); - int hid = handle_id; - send_event(BUFFER_EVENT_REBUFFER, &hid); + send_event(BUFFER_EVENT_REBUFFER, &(int){ handle_id }); } /* Now we do the rebuffer */ @@ -1237,25 +1205,20 @@ static void rebuffer_handle(int handle_id, size_t newpos) } /* Backend to bufseek and bufadvance */ -static int seek_handle(struct memory_handle *h, size_t newpos) +static int seek_handle(struct memory_handle *h, off_t newpos) { - if (newpos > h->filesize) { - /* access beyond the end of the file */ - return ERR_INVALID_VALUE; - } - else if ((newpos < h->offset || h->offset + h->available <= newpos) && - (newpos < h->filesize || h->filerem > 0)) { + if ((newpos < h->start || newpos >= h->end) && + (newpos < h->filesize || h->end < h->filesize)) { /* access before or after buffered data and not to end of file or file is not buffered to the end-- a rebuffer is needed. */ - struct buf_message_data parm = { h->id, newpos }; return queue_send(&buffering_queue, Q_REBUFFER_HANDLE, - (intptr_t)&parm); + (intptr_t)&(struct buf_message_data){ h->id, newpos }); } else { - h->ridx = ringbuf_add(h->data, newpos - h->offset); + h->ridx = ringbuf_add(h->data, newpos - h->start); + h->pos = newpos; + return 0; } - - return 0; } /* Set reading index in handle (relatively to the start of the file). @@ -1271,14 +1234,17 @@ int bufseek(int handle_id, size_t newpos) if (!h) return ERR_HANDLE_NOT_FOUND; + if (newpos > (size_t)h->filesize) + return ERR_INVALID_VALUE; + return seek_handle(h, newpos); } /* Advance the reading index in a handle (relatively to its current position). Return 0 for success and for failure: ERR_HANDLE_NOT_FOUND if the handle wasn't found - ERR_INVALID_VALUE if the new requested position was beyond the end of - the file + ERR_INVALID_VALUE if the new requested position was before the beginning + or beyond the end of the file */ int bufadvance(int handle_id, off_t offset) { @@ -1286,8 +1252,13 @@ int bufadvance(int handle_id, off_t offset) if (!h) return ERR_HANDLE_NOT_FOUND; - size_t newpos = h->offset + ringbuf_sub(h->ridx, h->data) + offset; - return seek_handle(h, newpos); + off_t pos = h->pos; + + if ((offset < 0 && offset < -pos) || + (offset >= 0 && offset > h->filesize - pos)) + return ERR_INVALID_VALUE; + + return seek_handle(h, pos + offset); } /* Get the read position from the start of the file @@ -1299,63 +1270,76 @@ off_t bufftell(int handle_id) const struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - return h->offset + ringbuf_sub(h->ridx, h->data); + + return h->pos; } /* Used by bufread and bufgetdata to prepare the buffer and retrieve the - * actual amount of data available for reading. This function explicitly - * does not check the validity of the input handle. It does do range checks - * on size and returns a valid (and explicit) amount of data for reading */ + * actual amount of data available for reading. It does range checks on + * size and returns a valid (and explicit) amount of data for reading */ static struct memory_handle *prep_bufdata(int handle_id, size_t *size, bool guardbuf_limit) { struct memory_handle *h = find_handle(handle_id); - size_t realsize; - if (!h) return NULL; - size_t avail = handle_size_available(h); - - if (avail == 0 && h->filerem == 0) { + if (h->pos >= h->filesize) { /* File is finished reading */ *size = 0; return h; } - realsize = *size; + off_t realsize = *size; + off_t filerem = h->filesize - h->pos; - if (realsize == 0 || realsize > avail + h->filerem) - realsize = avail + h->filerem; + if (realsize <= 0 || realsize > filerem) + realsize = filerem; /* clip to eof */ - if (guardbuf_limit && h->type == TYPE_PACKET_AUDIO - && realsize > GUARD_BUFSIZE) { + if (guardbuf_limit && realsize > GUARD_BUFSIZE) { logf("data request > guardbuf"); /* If more than the size of the guardbuf is requested and this is a * bufgetdata, limit to guard_bufsize over the end of the buffer */ - realsize = MIN(realsize, buffer_len - h->ridx + GUARD_BUFSIZE); + realsize = MIN((size_t)realsize, buffer_len - h->ridx + GUARD_BUFSIZE); /* this ensures *size <= buffer_len - h->ridx + GUARD_BUFSIZE */ } - if (h->filerem > 0 && avail < realsize) { - /* Data isn't ready. Request buffering */ - LOGFQUEUE("buffering >| Q_START_FILL %d",handle_id); - queue_send(&buffering_queue, Q_START_FILL, handle_id); + off_t end = h->end; + off_t wait_end = h->pos + realsize; + + if (end < wait_end && end < h->filesize) { /* Wait for the data to be ready */ + unsigned int request = 1; + do { + if (--request == 0) { + request = 100; + /* Data (still) isn't ready; ping buffering thread */ + LOGFQUEUE("buffering >| Q_START_FILL %d",handle_id); + queue_send(&buffering_queue, Q_START_FILL, handle_id); + } + sleep(0); /* it is not safe for a non-buffering thread to sleep while * holding a handle */ h = find_handle(handle_id); - if (!h || h->signaled != 0) + if (!h) return NULL; - avail = handle_size_available(h); + + if (h->signaled != 0) + return NULL; /* Wait must be abandoned */ + + end = h->end; } - while (h->filerem > 0 && avail < realsize); + while (end < wait_end && end < h->filesize); + + filerem = h->filesize - h->pos; + if (realsize > filerem) + realsize = filerem; } - *size = MIN(realsize, avail); + *size = realsize; return h; } @@ -1374,23 +1358,21 @@ static struct memory_handle *prep_bufdata(int handle_id, size_t *size, */ ssize_t bufread(int handle_id, size_t size, void *dest) { - const struct memory_handle *h; - size_t adjusted_size = size; - - h = prep_bufdata(handle_id, &adjusted_size, false); + const struct memory_handle *h = + prep_bufdata(handle_id, &size, false); if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->ridx + adjusted_size > buffer_len) { + if (h->ridx + size > buffer_len) { /* the data wraps around the end of the buffer */ size_t read = buffer_len - h->ridx; - memcpy(dest, &buffer[h->ridx], read); - memcpy(dest+read, buffer, adjusted_size - read); + memcpy(dest, ringbuf_ptr(h->ridx), read); + memcpy(dest + read, ringbuf_ptr(0), size - read); } else { - memcpy(dest, &buffer[h->ridx], adjusted_size); + memcpy(dest, ringbuf_ptr(h->ridx), size); } - return adjusted_size; + return size; } /* Update the "data" pointer to make the handle's data available to the caller. @@ -1404,81 +1386,80 @@ ssize_t bufread(int handle_id, size_t size, void *dest) */ ssize_t bufgetdata(int handle_id, size_t size, void **data) { - const struct memory_handle *h; - size_t adjusted_size = size; - - h = prep_bufdata(handle_id, &adjusted_size, true); + struct memory_handle *h = + prep_bufdata(handle_id, &size, true); if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->ridx + adjusted_size > buffer_len) { + if (h->ridx + size > buffer_len) { /* the data wraps around the end of the buffer : use the guard buffer to provide the requested amount of data. */ - size_t copy_n = h->ridx + adjusted_size - buffer_len; + size_t copy_n = h->ridx + size - buffer_len; /* prep_bufdata ensures adjusted_size <= buffer_len - h->ridx + GUARD_BUFSIZE, so copy_n <= GUARD_BUFSIZE */ - memcpy(guard_buffer, (const unsigned char *)buffer, copy_n); + memcpy(guard_buffer, ringbuf_ptr(0), copy_n); } if (data) - *data = &buffer[h->ridx]; + *data = ringbuf_ptr(h->ridx); - return adjusted_size; + return size; } ssize_t bufgettail(int handle_id, size_t size, void **data) { - size_t tidx; - - const struct memory_handle *h; + if (thread_self() != buffering_thread_id) + return ERR_WRONG_THREAD; /* only from buffering thread */ - h = find_handle(handle_id); + /* We don't support tail requests of > guardbuf_size, for simplicity */ + if (size > GUARD_BUFSIZE) + return ERR_INVALID_VALUE; + const struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->filerem) - return ERR_HANDLE_NOT_DONE; + if (h->end >= h->filesize) { + size_t tidx = ringbuf_sub(h->widx, size); - /* We don't support tail requests of > guardbuf_size, for simplicity */ - if (size > GUARD_BUFSIZE) - return ERR_INVALID_VALUE; - - tidx = ringbuf_sub(h->widx, size); + if (tidx + size > buffer_len) { + size_t copy_n = tidx + size - buffer_len; + memcpy(guard_buffer, ringbuf_ptr(0), copy_n); + } - if (tidx + size > buffer_len) { - size_t copy_n = tidx + size - buffer_len; - memcpy(guard_buffer, (const unsigned char *)buffer, copy_n); + *data = ringbuf_ptr(tidx); + } + else { + size = ERR_HANDLE_NOT_DONE; } - *data = &buffer[tidx]; return size; } ssize_t bufcuttail(int handle_id, size_t size) { - struct memory_handle *h; - size_t adjusted_size = size; - - h = find_handle(handle_id); + if (thread_self() != buffering_thread_id) + return ERR_WRONG_THREAD; /* only from buffering thread */ + struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->filerem) - return ERR_HANDLE_NOT_DONE; - - if (h->available < adjusted_size) - adjusted_size = h->available; + if (h->end >= h->filesize) { + /* Cannot trim to before read position */ + size_t available = h->end - MAX(h->start, h->pos); + if (available < size) + size = available; - h->available -= adjusted_size; - h->filesize -= adjusted_size; - h->widx = ringbuf_sub(h->widx, adjusted_size); - if (h == cur_handle) - buf_widx = h->widx; + h->widx = ringbuf_sub(h->widx, size); + h->filesize -= size; + h->end -= size; + } else { + size = ERR_HANDLE_NOT_DONE; + } - return adjusted_size; + return size; } @@ -1507,7 +1488,7 @@ ssize_t buf_handle_offset(int handle_id) const struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - return h->offset; + return h->start; } void buf_set_base_handle(int handle_id) @@ -1530,7 +1511,7 @@ ssize_t buf_handle_remaining(int handle_id) const struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - return h->filerem; + return h->filesize - h->end; } bool buf_is_handle(int handle_id) @@ -1572,7 +1553,11 @@ size_t buf_length(void) /* Return the amount of buffer space used */ size_t buf_used(void) { - return BUF_USED; + struct memory_handle *first = first_handle; + if (!first) + return 0; + + return ringbuf_sub(cur_handle->widx, ringbuf_offset(first)); } void buf_set_watermark(size_t bytes) @@ -1615,7 +1600,6 @@ static void NORETURN_ATTR buffering_thread(void) { bool filling = false; struct queue_event ev; - struct buf_message_data *parm; while (true) { @@ -1654,11 +1638,14 @@ static void NORETURN_ATTR buffering_thread(void) break; case Q_REBUFFER_HANDLE: - parm = (struct buf_message_data *)ev.data; + { + struct buf_message_data *parm = + (struct buf_message_data *)ev.data; LOGFQUEUE("buffering < Q_REBUFFER_HANDLE %d %ld", parm->handle_id, parm->data); rebuffer_handle(parm->handle_id, parm->data); break; + } case Q_CLOSE_HANDLE: LOGFQUEUE("buffering < Q_CLOSE_HANDLE %d", (int)ev.data); @@ -1689,7 +1676,7 @@ static void NORETURN_ATTR buffering_thread(void) if (num_handles > 0 && data_counters.useful <= high_watermark) send_event(BUFFER_EVENT_BUFFER_LOW, 0); - if (data_counters.remaining > 0 && BUF_USED <= high_watermark) { + if (data_counters.remaining > 0 && buf_used() <= high_watermark) { /* This is a new fill, shrink the buffer up first */ if (!filling) shrink_buffer(); @@ -1769,9 +1756,6 @@ bool buffering_reset(char *buf, size_t buflen) buffer_len = buflen; guard_buffer = buf + buflen; - buf_widx = 0; - buf_ridx = 0; - first_handle = NULL; cur_handle = NULL; cached_handle = NULL; @@ -1794,10 +1778,8 @@ bool buffering_reset(char *buf, size_t buflen) void buffering_get_debugdata(struct buffering_debug *dbgdata) { struct data_counters dc; - update_data_counters(&dc); - dbgdata->num_handles = num_handles; + dbgdata->num_handles = update_data_counters(&dc); dbgdata->data_rem = dc.remaining; - dbgdata->wasted_space = dc.wasted; dbgdata->buffered_data = dc.buffered; dbgdata->useful_data = dc.useful; dbgdata->watermark = BUF_WATERMARK; diff --git a/apps/buffering.h b/apps/buffering.h index 6d52794233..218f77ed85 100644 --- a/apps/buffering.h +++ b/apps/buffering.h @@ -45,7 +45,7 @@ enum data_type { #define ERR_FILE_ERROR -4 #define ERR_HANDLE_NOT_DONE -5 #define ERR_UNSUPPORTED_TYPE -6 - +#define ERR_WRONG_THREAD -7 /* Initialise the buffering subsystem */ void buffering_init(void) INIT_ATTR; @@ -116,10 +116,6 @@ void buf_back_off_storage(bool back_off); #endif /* Settings */ -enum { - BUFFERING_SET_WATERMARK = 1, - BUFFERING_SET_CHUNKSIZE, -}; void buf_set_watermark(size_t bytes); size_t buf_get_watermark(void); @@ -127,7 +123,6 @@ size_t buf_get_watermark(void); struct buffering_debug { int num_handles; size_t buffered_data; - size_t wasted_space; size_t data_rem; size_t useful_data; size_t watermark; |