From: Pavel Charvat Date: Thu, 15 Feb 2024 22:27:13 +0000 (+0000) Subject: Fastbufs: Some improvements in documented rules for back-ends. X-Git-Tag: v6.5.16~1 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=f9bc702dde9353b8a19287ec71b2204f50a4b8a6;p=libucw.git Fastbufs: Some improvements in documented rules for back-ends. --- diff --git a/ucw/fastbuf.h b/ucw/fastbuf.h index 0a04176a..0da6258c 100644 --- a/ucw/fastbuf.h +++ b/ucw/fastbuf.h @@ -96,65 +96,79 @@ * situation. Back-ends then differ just in the definition of the callbacks. * * The state of the fastbuf is represented by a <>, - * which is a simple structure describing the state of the buffer (the pointers - * `buffer`, `bufend`), the front-end cursor (`bptr`), the back-end cursor (`bstop`), + * which is a simple structure describing the allocated buffer (`buffer`, `bufend`), + * the front-end cursor (`bptr`), the back-end cursor (`bstop`), * position of the back-end cursor in the file (`pos`), some flags (`flags`) * and pointers to the callback functions. * - * The buffer can be in one of the following states: + * Fastbuf can be in one of the following logical modes. Transitions between + * them are controlled by application. * * 1. Flushed: * + * * Initial state of newly created fastbuf, after @bflush(), seeking or similar functions. + * * There is no cached data and application is free to decide whether to continue with + * reading or writing (well, only if the back-end supports it). Any such operation + * commits fastbuf to the corresponding non-flushed mode. + * + * Buffer layout: * +------------------------------------+---------------------------+ * | unused | free space | * +------------------------------------+---------------------------+ * ^ ^ ^ ^ * buffer <= bstop (BE pos) <= bptr (FE pos) <= bufend * - * * This schema describes a fastbuf after its initialization or @bflush(). - * * There is no cached data and we are ready for any read or write operation - * (well, only if the back-end supports it). - * * The interval `[bptr, bufend]` can be used by front-ends - * for writing. If it is empty, the `spout` callback gets called - * upon the first write attempt to allocate a new buffer. Otherwise - * the fastbuf silently comes to the writing mode. - * * When a front-end needs to read something, it calls the `refill` callback. - * * The pointers can be either all non-`NULL` or all NULL. + * * The pointers can be either all non-`NULL` (possibly equal) or all NULL. * * `bstop == bptr` in most back-ends, but it is not necessary. Some * in-memory streams take advantage of this. + * * Reading (transition to reading mode) calls `refill` callback. + * * Writing (transition to writing mode) can immediately call `spout` + * callback, but not necessarily if `bptr < bufend` (notice that the + * flushed buffer layout is compatible with writing). * * 2. Reading: * + * * Application has committed to reading. + * * No writing is allowed until a flush operation (but see caveat below). + * + * Buffer layout: * +------------------------------------+---------------------------+ * | read data | unused | * +------------------------------------+---------------------------+ * ^ ^ ^ ^ * buffer <= bptr (FE pos) <= bstop (BE pos) <= bufend * - * * If we try to read something, we get to the reading mode. - * * No writing is allowed until a flush operation. But note that @bflush() - * will simply set `bptr` to `bstop` before `spout` - * and it breaks the position of the front-end's cursor, - * so the user should seek afwards. - * * The interval `[buffer, bstop]` contains a block of data read by the back-end. - * `bptr` is the front-end's cursor which points to the next character to be read. - * After the last character is read, `bptr == bstop` and the `refill` callback - * gets called upon the next read attempt to bring further data. - * This gives us an easy way how to implement @bungetc(). + * * Interval `[buffer, bstop]` contains a block of data read by the back-end. + * `bptr` is front-end's cursor which points to the next character to + * be read by application. + * * If we reach `bptr == bstop` and need to read at least one more byte, + * `refill` is called to bring more (or to detect EOF). Notice that this gives + * us an easy way to implement @bungetc(). + * * CAVEAT: Switching to writing by @bflush() simply sets `bptr` to `bstop` + * and calls `spout`. This can, depending on the type of back-end, lead to + * loss of any remaining buffered data and/or breaking position of + * front-end's cursor. You can seek afterwards to fix that, but it's + * not possible in unseekable back-ends like network sockets. In such + * cases it may be useful to create pair of fastbufs, one for reading + * and one for writing. Also beware that switching may be unsupported/buggy + * in some types of back-ends. * * 3. Writing: * + * * Application has committed to writing. + * * No reading is allowed until a flush operation. + * * You can also use @blush() to explicitly flush any buffered data. + * + * Buffer layout: * +-----------------------+----------------+-----------------------+ * | unused | written data | free space | * +-----------------------+----------------+-----------------------+ * ^ ^ ^ ^ * buffer <= bstop (BE pos) < bptr (FE pos) <= bufend * - * * This schema corresponds to the situation after a write attempt. - * * No reading is allowed until a flush operation. - * * The `bptr` points at the position where the next character - * will be written to. When we want to write, but `bptr == bufend`, we call - * the `spout` hook to flush the witten data and get an empty buffer. + * * `bptr` points at the position where the next character will be written to. + * If we reach `bptr == bufend` and want to write more, `spout` is called + * to flush the written data and get some free space. * * `bstop` usually points at the beginning of the written data, * but it is not necessary. * @@ -163,17 +177,18 @@ * * - Front-ends are only allowed to change the value of `bptr`, some flags * and if a fatal error occurs, then also `bstop`. Back-ends can rely on it. - * - `buffer <= bstop <= bufend` and `buffer <= bptr <= bufend`. - * - `pos` should be the real position in the file corresponding to the location of `bstop` in the buffer. + * - `buffer <= bstop <= bufend` and `buffer <= bptr <= bufend` at any time. + * - `pos` corresponds to `bstop` and usually contains the real offset of that + * cursor in file (but back-ends can define the exact meaning of `pos` differently). * It can be modified by any back-end's callback, but the position of `bptr` (`pos + (bptr - bstop)`) - * must stay unchanged after `refill` or `spout`. + * should stay unchanged after `refill` or `spout`. * - Failed callbacks (except `close`) should use @bthrow(). - * - Any callback pointer may be NULL in case the callback is not implemented. + * - Any callback may be NULL if not implemented. * - Callbacks can change not only `bptr` and `bstop`, but also the location and size of the buffer; * the fb-mem back-end takes advantage of it. * * - Initialization: - * * out: `buffer <= bstop <= bptr <= bufend` (flushed). + * * out: `buffer <= bstop <= bptr <= bufend`, possibly all equal or even NULL (flushed). * * @fb_tie() should be called on the newly created fastbuf. * * - `refill`: @@ -185,7 +200,7 @@ * * - `spout`: * * in: `buffer <= bstop <= bptr <= bufend` (writing or flushed). - * * out: `buffer <= bstop <= bptr < bufend` (flushed). + * * out: `buffer <= bstop <= bptr < bufend` (writing or flushed; at least 1 byte of free space). * * - `seek`: * * in: `buffer <= bstop <= bptr <= bufend` (flushed).