Hands-off readering code built into tracklength plugin.
#include <string.h>
#include <fcntl.h>
+static int hreader_fill(struct hreader *h, off_t offset);
+
void hreader_init(const char *path, struct hreader *h) {
memset(h, 0, sizeof *h);
h->path = xstrdup(path);
}
int hreader_read(struct hreader *h, void *buffer, size_t n) {
- if(h->consumed == h->bytes) {
- int r;
+ int r = hreader_pread(h, buffer, n, h->read_offset);
+ if(r > 0)
+ h->read_offset += r;
+ return r;
+}
- if((r = hreader_fill(h)) <= 0)
- return r;
+int hreader_pread(struct hreader *h, void *buffer, size_t n, off_t offset) {
+ size_t bytes_read = 0;
+
+ while(bytes_read < n) {
+ // If the desired byte range is outside the file, fetch new contents
+ if(offset < h->buf_offset || offset >= h->buf_offset + (off_t)h->bytes) {
+ int r = hreader_fill(h, offset);
+ if(r < 0)
+ return -1; /* disaster! */
+ else if(r == 0)
+ break; /* end of file */
+ }
+ // Figure out how much we can read this time round
+ size_t left = h->bytes - (offset - h->buf_offset);
+ // Truncate the read if we don't want that much
+ if(left > (n - bytes_read))
+ left = n - bytes_read;
+ memcpy((char *)buffer + bytes_read,
+ h->buffer + (offset - h->buf_offset),
+ left);
+ offset += left;
+ bytes_read += left;
}
- if(n > h->bytes - h->consumed)
- n = h->bytes - h->consumed;
- memcpy(buffer, h->buffer + h->consumed, n);
- h->consumed += n;
- return n;
+ return bytes_read;
}
-int hreader_fill(struct hreader *h) {
- if(h->consumed < h->bytes)
- return h->bytes - h->consumed;
- h->bytes = h->consumed = 0;
+static int hreader_fill(struct hreader *h, off_t offset) {
int fd = open(h->path, O_RDONLY);
if(fd < 0)
return -1;
- if(lseek(fd, h->offset, SEEK_SET) < 0) {
- close(fd);
- return -1;
- }
- int n = read(fd, h->buffer, h->bufsize);
+ int n = pread(fd, h->buffer, h->bufsize, offset);
close(fd);
if(n < 0)
return -1;
+ h->buf_offset = offset;
h->bytes = n;
- h->offset += n;
return n;
}
-void hreader_consume(struct hreader *h, size_t n) {
- h->consumed += n;
-}
-
/*
Local Variables:
c-basic-offset:2
*/
struct hreader {
const char *path; /* file to read */
- off_t offset; /* how far we've read so far */
+ off_t read_offset; /* for next hreader_read() */
+ off_t buf_offset; /* offset of start of buffer */
char *buffer; /* input buffer */
size_t bufsize; /* buffer size */
size_t bytes; /* size of last read */
- size_t consumed; /* bytes consumed by caller from last read */
};
/** @brief Initialize a hands-off reader
*/
int hreader_read(struct hreader *h, void *buffer, size_t n);
-/** @brief Read more bytes
- * @param h Reader to update
- * @return Bytes available to read
- *
- * If not all bytes were consumed so far then just returns the number
- * of bytes left to consume.
- */
-int hreader_fill(struct hreader *h);
-
-/** @brief Consume some bytes
- * @param h Reader to update
- * @param n Bytes to consume
+/** @brief Read some bytes at a given offset
+ * @param h Reader to read from
+ * @param offset Offset to read at
+ * @param buffer Where to store bytes
+ * @param n Maximum bytes to read
+ * @return Bytes read, or 0 at EOF, or -1 on error
*/
-void hreader_consume(struct hreader *h, size_t n);
+int hreader_pread(struct hreader *h, void *buffer, size_t n, off_t offset);
#endif /* HREADER_H */
off_t where;
memset(f, 0, sizeof *f);
- f->fd = -1;
f->data = -1;
- if((f->fd = open(path, O_RDONLY)) < 0) goto error_errno;
+ hreader_init(path, f->input);
/* Read the file header
*
* offset size meaning
* 04 4 length of rest of file
* 08 4 'WAVE'
* */
- if((n = pread(f->fd, header, 12, 0)) < 0) goto error_errno;
+ if((n = hreader_pread(f->input, header, 12, 0)) < 0) goto error_errno;
else if(n < 12) goto einval;
if(strncmp(header, "RIFF", 4) || strncmp(header + 8, "WAVE", 4))
goto einval;
* 00 4 chunk ID
* 04 4 length of rest of chunk
*/
- if((n = pread(f->fd, header, 8, where)) < 0) goto error_errno;
+ if((n = hreader_pread(f->input, header, 8, where)) < 0) goto error_errno;
else if(n < 8) goto einval;
if(!strncmp(header,"fmt ", 4)) {
/* This is the format chunk
* 18 ? extra undocumented rubbish
*/
if(get32(header + 4) < 16) goto einval;
- if((n = pread(f->fd, header + 8, 16, where + 8)) < 0) goto error_errno;
+ if((n = hreader_pread(f->input, header + 8, 16, where + 8)) < 0)
+ goto error_errno;
else if(n < 16) goto einval;
f->channels = get16(header + 0x0A);
f->rate = get32(header + 0x0C);
}
/** @brief Close a WAV file */
-void wav_destroy(struct wavfile *f) {
- if(f) {
- const int save_errno = errno;
-
- if(f->fd >= 0)
- close(f->fd);
- errno = save_errno;
- }
+void wav_destroy(struct wavfile attribute((unused)) *f) {
}
/** @brief Visit all the data in a WAV file
size_t want = (off_t)sizeof buffer > left ? (size_t)left : sizeof buffer;
want -= want % bytes_per_frame;
- if((n = pread(f->fd, buffer, want, where)) < 0) return errno;
+ if((n = hreader_pread(f->input, buffer, want, where)) < 0) return errno;
if((size_t)n < want) return EINVAL;
if((err = callback(f, buffer, n, u))) return err;
where += n;
#ifndef WAV_H
#define WAV_H
+#include "hreader.h"
+
/** @brief WAV file access structure */
struct wavfile {
- /** @brief File descriptor onto file */
- int fd;
+ /** @brief File read handle */
+ struct hreader input[1];
/** @brief File length */
off_t length;
notify_la_SOURCES=notify.c
notify_la_LDFLAGS=-module
-disorder_tracklength_la_SOURCES=tracklength.c mad.c madshim.h ../lib/wav.h ../lib/wav.c
+disorder_tracklength_la_SOURCES=tracklength.c mad.c madshim.h ../lib/wav.h ../lib/wav.c ../lib/hreader.h ../lib/hreader.c
disorder_tracklength_la_LDFLAGS=-module
disorder_tracklength_la_LIBADD=$(LIBVORBISFILE) $(LIBMAD) $(LIBFLAC) -lm