struct subbuf {
cnode n;
- ucw_off_t begin, end;
+ ucw_off_t begin, end, offset;
int allow_close;
struct fastbuf *fb;
};
static void
fbmulti_subbuf_get_end(struct subbuf *s)
{
- if (s->fb->seek)
- {
- bseek(s->fb, 0, SEEK_END);
- s->end = s->begin + btell(s->fb);
- }
+ ASSERT(s->fb->seek);
+ bseek(s->fb, 0, SEEK_END);
+ s->end = s->begin + btell(s->fb);
+}
+
+static void
+fbmulti_get_ptrs(struct fastbuf *f)
+{
+ f->buffer = FB_MULTI(f)->cur->fb->buffer;
+ f->bptr = FB_MULTI(f)->cur->fb->bptr;
+ f->bstop = FB_MULTI(f)->cur->fb->bstop;
+ f->bufend = FB_MULTI(f)->cur->fb->bufend;
+ f->pos = FB_MULTI(f)->cur->begin + FB_MULTI(f)->cur->fb->pos - FB_MULTI(f)->cur->offset;
+}
+
+static void
+fbmulti_set_ptrs(struct fastbuf *f)
+{
+ FB_MULTI(f)->cur->fb->bptr = f->bptr;
}
static int
struct subbuf *next = clist_next(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
if (next == NULL)
return 0;
-
+
+ // Check the end of current buf
if (f->seek)
+ {
+ FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, FB_MULTI(f)->cur->end - FB_MULTI(f)->cur->begin, SEEK_SET);
+ fbmulti_get_ptrs(f);
+ ASSERT(FB_MULTI(f)->cur->end == f->pos);
+ }
+ else
+ FB_MULTI(f)->cur->end = f->pos;
+
+ // Set the beginning of the next buf
+ if (next->fb->seek)
{
bsetpos(next->fb, 0);
- next->begin = FB_MULTI(f)->cur->end;
+ next->offset = 0;
+ }
+ else
+ {
+ ASSERT(!f->seek);
+ next->offset = btell(next->fb);
}
+ next->begin = FB_MULTI(f)->cur->end;
+
+ // Set the pointers
FB_MULTI(f)->cur = next;
+ fbmulti_get_ptrs(f);
+
return 1;
}
static int
-fbmulti_refill(struct fastbuf *f)
+fbmulti_subbuf_prev(struct fastbuf *f)
{
- if (f->bufend == f->bstop)
- f->bptr = f->bstop = f->buffer;
+ // Called only when seeking, assuming everything seekable
+ struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
+ ASSERT(prev != NULL);
- uns len = bread(FB_MULTI(f)->cur->fb, f->bstop, (f->bufend - f->bstop));
- f->bstop += len;
- f->pos += len;
+ // Set pos to beginning, flush offset
+ bsetpos(prev->fb, 0);
+ prev->offset = 0;
+
+ // Set the pointers
+ FB_MULTI(f)->cur = prev;
+ fbmulti_get_ptrs(f);
+
+ return 1;
+}
+
+static int
+fbmulti_refill(struct fastbuf *f)
+{
+ fbmulti_set_ptrs(f);
+ // Refill the subbuf
+ uns len = FB_MULTI(f)->cur->fb->refill(FB_MULTI(f)->cur->fb);
if (len)
- return len;
+ {
+ fbmulti_get_ptrs(f);
+ return len;
+ }
// Current buf returned EOF
// Update the information on end of this buffer
fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
- // Take the next one if exists
+ // Take the next one if exists and redo
if (fbmulti_subbuf_next(f))
return fbmulti_refill(f);
else
static void
fbmulti_get_len(struct fastbuf *f)
{
+ ucw_off_t pos = btell(f);
ASSERT(f->seek);
FB_MULTI(f)->len = 0;
-
+
CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
{
n->begin = FB_MULTI(f)->len;
fbmulti_subbuf_get_end(n);
FB_MULTI(f)->len = n->end;
}
+ f->seek(f, pos, SEEK_SET); // XXX: f->seek is needed here instead of bsetpos as the FE assumptions about f's state may be completely wrong.
}
static int
fbmulti_seek(struct fastbuf *f, ucw_off_t pos, int whence)
{
+ fbmulti_set_ptrs(f);
switch(whence)
{
case SEEK_SET:
- if (f->pos > pos)
- {
- FB_MULTI(f)->cur = clist_head(FB_MULTI(f)->subbufs);
- FB_MULTI(f)->cur->begin = 0;
- f->pos = 0;
- return fbmulti_seek(f, pos, SEEK_SET);
- }
+ if (pos > FB_MULTI(f)->len)
+ bthrow(f, "seek", "Seek out of range");
- do
- {
- fbmulti_subbuf_get_end(FB_MULTI(f)->cur);
- if (pos < FB_MULTI(f)->cur->end)
- break;
+ while (pos > FB_MULTI(f)->cur->end) // Moving forward
+ ASSERT(fbmulti_subbuf_next(f));
- if (!fbmulti_subbuf_next(f))
- {
- if (pos == FB_MULTI(f)->cur->end)
- break;
- else
- bthrow(f, "seek", "Seek out of range");
- }
+ while (pos < FB_MULTI(f)->cur->begin) // Moving backwards
+ ASSERT(fbmulti_subbuf_prev(f));
- }
- while (1);
+ // Now cur is the right buffer.
+ FB_MULTI(f)->cur->fb->seek(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin), SEEK_SET);
- bsetpos(FB_MULTI(f)->cur->fb, (pos - FB_MULTI(f)->cur->begin));
- f->pos = pos;
- f->bptr = f->bstop = f->buffer;
+ fbmulti_get_ptrs(f);
return 1;
break;
case SEEK_END:
- fbmulti_get_len(f);
return fbmulti_seek(f, FB_MULTI(f)->len+pos, SEEK_SET);
break;
va_end(args);
+ fbmulti_update_capability(fb_out);
+
FB_MULTI(fb_out)->cur = clist_head(subbufs);
+ bsetpos(FB_MULTI(fb_out)->cur->fb, 0);
+
+ fbmulti_get_ptrs(fb_out);
+
+ // If seekable, get the length of each subbuf, the total length and boundaries
+ if (fb_out->seek)
+ {
+ fbmulti_get_len(fb_out);
+ }
- fb_out->buffer = mp_alloc(mp, bufsize);
- fb_out->bptr = fb_out->bstop = fb_out->buffer;
- fb_out->bufend = fb_out->buffer + bufsize;
fb_out->name = FB_MULTI_NAME;
- fbmulti_update_capability(fb_out);
fb_out->close = fbmulti_close;
return fb_out;
void
fbmulti_remove(struct fastbuf *f, struct fastbuf *fb)
{
+ bflush(f);
+ uns pos = f->pos;
if (fb)
{
CLIST_FOR_EACH(struct subbuf *, n, *(FB_MULTI(f)->subbufs))
if (fb == n->fb)
{
- // TODO: fix seek positions
+ // Move the pointers to another buffer if this one was the active.
+ if (FB_MULTI(f)->cur == n)
+ {
+ pos = n->begin;
+ if (!fbmulti_subbuf_next(f))
+ {
+ struct subbuf *prev = clist_prev(FB_MULTI(f)->subbufs, &FB_MULTI(f)->cur->n);
+ if (prev == NULL)
+ goto cleanup;
+
+ FB_MULTI(f)->cur = prev;
+ fbmulti_get_ptrs(f);
+ }
+ }
+
+ if (n->end < pos)
+ pos -= (n->end - n->begin);
+
clist_remove(&(n->n));
fbmulti_update_capability(f);
+ fbmulti_get_len(f);
+ fbmulti_get_ptrs(f);
return;
- }
+ };
die("Given fastbuf %p not in given fbmulti %p.", fb, f);
}
else
clist_init(FB_MULTI(f)->subbufs);
+cleanup:
+ // The fbmulti is empty now, do some cleanup
fbmulti_update_capability(f);
+ fbmulti_get_len(f);
+ f->buffer = f->bufend = f->bptr = f->bstop = NULL;
+ f->pos = 0;
}
static void fbmulti_flatten_internal(struct fastbuf *f, clist *c, int allow_close)