summaryrefslogtreecommitdiff
path: root/source/fitz/stream-prog.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-07-17 18:42:33 +0100
committerRobin Watts <robin.watts@artifex.com>2013-07-19 19:54:27 +0100
commitf5f7c0e4dd83257f526b158e3998970717852a0e (patch)
treec34ee93ab7773e4fbe48506c97fb515c03707e57 /source/fitz/stream-prog.c
parent3c559928d88fccfe17da4953ea1c93ceb42a90cb (diff)
downloadmupdf-f5f7c0e4dd83257f526b158e3998970717852a0e.tar.xz
Initial work on progressive loading
We are testing this using a new -p flag to mupdf that sets a bitrate at which data will appear to arrive progressively as time goes on. For example: mupdf -p 102400 pdf_reference17.pdf Details of the scheme used here are presented in docs/progressive.txt
Diffstat (limited to 'source/fitz/stream-prog.c')
-rw-r--r--source/fitz/stream-prog.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/source/fitz/stream-prog.c b/source/fitz/stream-prog.c
new file mode 100644
index 00000000..e35a1ac5
--- /dev/null
+++ b/source/fitz/stream-prog.c
@@ -0,0 +1,181 @@
+#include "mupdf/fitz/stream.h"
+#include "mupdf/fitz/string.h"
+
+#if defined(_WIN32) && !defined(NDEBUG)
+#include "windows.h"
+
+static void
+show_progress(int av, int pos)
+{
+ char text[80];
+ sprintf(text, "Have %d, Want %d\n", av, pos);
+ OutputDebugStringA(text);
+}
+#else
+#define show_progress(A,B) do {} while (0)
+#endif
+
+/* File stream - progressive reading to simulate http download */
+
+typedef struct prog_state
+{
+ int fd;
+ int length;
+ int available;
+ int bps;
+ clock_t start_time;
+} prog_state;
+
+static int read_prog(fz_stream *stm, unsigned char *buf, int len)
+{
+ prog_state *ps = (prog_state *)stm->state;
+ int n;
+
+ /* Simulate more data having arrived */
+ if (ps->available < ps->length)
+ {
+ int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8));
+ if (av > ps->length)
+ av = ps->length;
+ ps->available = av;
+ /* Limit any fetches to be within the data we have */
+ if (av < ps->length && len + stm->pos > av)
+ {
+ len = av - stm->pos;
+ if (len <= 0)
+ {
+ show_progress(av, stm->pos);
+ fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data yet");
+ }
+ }
+ }
+
+ n = (len > 0 ? read(ps->fd, buf, len) : 0);
+ if (n < 0)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno));
+ return n;
+}
+
+static void seek_prog(fz_stream *stm, int offset, int whence)
+{
+ prog_state *ps = (prog_state *)stm->state;
+ int n;
+
+ /* Simulate more data having arrived */
+ if (ps->available < ps->length)
+ {
+ int av = (int)((float)(clock() - ps->start_time) * ps->bps / (CLOCKS_PER_SEC*8));
+ if (av > ps->length)
+ av = ps->length;
+ ps->available = av;
+ }
+ if (ps->available < ps->length)
+ {
+ if (whence == SEEK_END)
+ {
+ show_progress(ps->available, ps->length);
+ fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to end yet");
+ }
+ }
+ if (whence == SEEK_CUR)
+ {
+ whence = SEEK_SET;
+ offset += stm->pos;
+ if (offset > ps->available)
+ {
+ show_progress(ps->available, offset);
+ fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek (relatively) to offset yet");
+ }
+ }
+ if (whence == SEEK_SET)
+ {
+ if (offset > ps->available)
+ {
+ show_progress(ps->available, offset);
+ fz_throw(stm->ctx, FZ_ERROR_TRYLATER, "Not enough data to seek to offset yet");
+ }
+ }
+
+ n = lseek(ps->fd, offset, whence);
+ if (n < 0)
+ fz_throw(stm->ctx, FZ_ERROR_GENERIC, "cannot lseek: %s", strerror(errno));
+ stm->pos = n;
+ stm->rp = stm->bp;
+ stm->wp = stm->bp;
+}
+
+static void close_prog(fz_context *ctx, void *state)
+{
+ prog_state *ps = (prog_state *)state;
+ int n = close(ps->fd);
+ if (n < 0)
+ fz_warn(ctx, "close error: %s", strerror(errno));
+ fz_free(ctx, state);
+}
+
+static int meta_prog(fz_stream *stm, int key, int size, void *ptr)
+{
+ prog_state *ps = (prog_state *)stm->state;
+ switch(key)
+ {
+ case FZ_STREAM_META_PROGRESSIVE:
+ return 1;
+ break;
+ case FZ_STREAM_META_LENGTH:
+ return ps->length;
+ }
+ return -1;
+}
+
+fz_stream *
+fz_open_fd_progressive(fz_context *ctx, int fd, int bps)
+{
+ fz_stream *stm;
+ prog_state *state;
+
+ state = fz_malloc_struct(ctx, prog_state);
+ state->fd = fd;
+ state->bps = bps;
+ state->start_time = clock();
+ state->available = 0;
+
+ state->length = lseek(state->fd, 0, SEEK_END);
+ lseek(state->fd, 0, SEEK_SET);
+
+ fz_try(ctx)
+ {
+ stm = fz_new_stream(ctx, state, read_prog, close_prog);
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, state);
+ fz_rethrow(ctx);
+ }
+ stm->seek = seek_prog;
+ stm->meta = meta_prog;
+
+ return stm;
+}
+
+fz_stream *
+fz_open_file_progressive(fz_context *ctx, const char *name, int bps)
+{
+#ifdef _WIN32
+ char *s = (char*)name;
+ wchar_t *wname, *d;
+ int c, fd;
+ d = wname = fz_malloc(ctx, (strlen(name)+1) * sizeof(wchar_t));
+ while (*s) {
+ s += fz_chartorune(&c, s);
+ *d++ = c;
+ }
+ *d = 0;
+ fd = _wopen(wname, O_BINARY | O_RDONLY, 0);
+ fz_free(ctx, wname);
+#else
+ int fd = open(name, O_BINARY | O_RDONLY, 0);
+#endif
+ if (fd == -1)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name);
+ return fz_open_fd_progressive(ctx, fd, bps);
+}