+ f->close(f); /* Should always free all internal resources, even if it throws an exception */
+ }
+}
+
+void NONRET bthrow(struct fastbuf *f, const char *id, const char *fmt, ...)
+{
+ DBG("FB: throwing %s", full_id);
+ char full_id[16];
+ snprintf(full_id, sizeof(full_id), "ucw.fb.%s", id);
+ ASSERT(!(f->flags & FB_DEAD)); /* Only one bthrow() is allowed before bclose() */
+ va_list args;
+ va_start(args, fmt);
+ if (!f->res)
+ die("Fastbuf %s error: %s", f->name ? : "<fb>", stk_vprintf(fmt, args));
+ f->flags |= FB_DEAD;
+ f->bptr = f->bstop = f->bufend; /* Reset the buffer to guard consecutive seek/read/write */
+ trans_vthrow(full_id, f, fmt, args);
+}
+
+int brefill(struct fastbuf *f, int allow_eof)
+{
+ DBG("FB: refill");
+ ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
+ if (!f->refill)
+ bthrow(f, "read", "Stream not readable");
+ if (f->refill(f))
+ {
+ ASSERT(f->buffer <= f->bptr && f->bptr < f->bstop && f->bstop <= f->bufend);
+ return 1;
+ }
+ else
+ {
+ ASSERT(f->buffer <= f->bptr && f->bptr == f->bstop && f->bstop <= f->bufend);
+ if (!allow_eof && (f->flags & FB_DIE_ON_EOF))
+ bthrow(f, "eof", "Unexpected EOF");
+ return 0;
+ }
+}
+
+static void do_spout(struct fastbuf *f)
+{
+ DBG("FB: spout");
+ ASSERT(!(f->flags & FB_DEAD) && f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend); /* Check write mode possibly with unflushed data */
+ if (!f->spout)
+ bthrow(f, "write", "Stream not writeable");
+ f->spout(f);
+ ASSERT(f->buffer <= f->bstop && f->bstop <= f->bptr && f->bptr <= f->bufend);
+}
+
+void bspout(struct fastbuf *f)
+{
+ do_spout(f);
+ if (f->bstop == f->bufend)
+ {
+ do_spout(f);
+ ASSERT(f->bstop < f->bufend);