]> mj.ucw.cz Git - libucw.git/commitdiff
Fastbuf: fbmulti now uses transparently the underlying buffer
authorJan 'Moskyt' Matejka <mq@ucw.cz>
Mon, 16 Jul 2012 15:25:30 +0000 (17:25 +0200)
committerJan 'Moskyt' Matejka <mq@ucw.cz>
Thu, 19 Jul 2012 13:25:28 +0000 (15:25 +0200)
ucw/fastbuf.h
ucw/fb-multi.c

index 40b60dcac5e2075a601e3d755f95a42a3ac06b9f..0c0ea427ad0c04adc1d7d498fe6b3772713a599c 100644 (file)
@@ -513,6 +513,11 @@ static inline void fbatomic_commit(struct fastbuf *b)
  * If you want to remove a fastbuf from the chain, just call @fbmulti_remove
  * where the second parameter is a pointer to the removed fastbuf. If you pass
  * NULL, all the underlying fastbufs are removed.
+ *
+ * When a fastbuf is removed from the chain, the overall position may change:
+ * If bstop pointed into it, after removal it points to the boundary of the
+ * previous and next fastbufs. Length of the removed fastbuf is subtracted from
+ * the overall offset of all the fastbufs after the removed fb in the chain.
  ***/
 
 struct fastbuf *fbmulti_create(uns bufsize, ...) SENTINEL_CHECK;
index 8071aa0cbed0e5cf70d6499066a16acb29b56580..e5835574e84ff5be9f58d79b8acc6a4bb3bf39a5 100644 (file)
@@ -28,7 +28,7 @@ struct fb_multi {
 
 struct subbuf {
   cnode n;
-  ucw_off_t begin, end;
+  ucw_off_t begin, end, offset;
   int allow_close;
   struct fastbuf *fb;
 };
@@ -36,11 +36,25 @@ struct subbuf {
 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
@@ -49,34 +63,73 @@ fbmulti_subbuf_next(struct fastbuf *f)
   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
@@ -86,56 +139,43 @@ fbmulti_refill(struct fastbuf *f)
 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;
 
@@ -190,14 +230,21 @@ fbmulti_create(uns bufsize, ...)
   
   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;
@@ -216,23 +263,49 @@ fbmulti_append(struct fastbuf *f, struct fastbuf *fb, int allow_close)
 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)