base/rsvr.[ch]: New hack for buffering input to block-oriented functions.
[catacomb] / base / rsvr.h
diff --git a/base/rsvr.h b/base/rsvr.h
new file mode 100644 (file)
index 0000000..9fb9c0a
--- /dev/null
@@ -0,0 +1,195 @@
+/* -*-c-*-
+ *
+ * Reservoir and buffer handling
+ *
+ * (c) 2017 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb.  If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef CATACOMB_RSVR_H
+#define CATACOMB_RSVR_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct rsvr_policy {
+  unsigned f;                          /* Flags... */
+#define RSVRF_FULL 1u                  /*   Hold back a full reservoir */
+  unsigned blksz;                      /* Block size */
+  unsigned rsvrsz;                     /* Reservoir size; multiple of
+                                        * @blksz@ */
+} rsvr_policy;
+
+typedef struct rsvr_plan {
+  unsigned head;                       /* First, accumulate @head@ bytes
+                                        * into the reservoir */
+  unsigned from_rsvr;                  /* Next, process @from_rsvr@ bytes
+                                        * from the reservoir */
+  size_t from_input;                   /* Then, process @from_input@ bytes
+                                        * directly from the input */
+  unsigned tail;                       /* Finally, accumulate the remaining
+                                        * @tail@ bytes of input into the
+                                        * reservoir */
+} rsvr_plan;
+
+enum { RSVRSRC_RSVR, RSVRSRC_INPUT, RSVRSRC_DONE };
+
+typedef struct rsvr_state {
+  rsvr_plan plan;
+  unsigned *used;
+  unsigned char *rsvr;
+  const unsigned char *in, *p;
+  size_t sz;
+  unsigned src;
+} rsvr_state;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rsvr_mkplan@ --- *
+ *
+ * Arguments:  @rsvr_plan *plan@ = pointer to plan to fill in
+ *             @const rsvr_policy *pol@ = reservoir policy to follow
+ *             @size_t used@ = amount of data in the reservoir
+ *             @size_t insz@ = amount of fresh input data arriving
+ *
+ * Returns:    ---
+ *
+ * Use:                Prepares a plan for feeding input data into a block-oriented
+ *             operation.
+ *
+ *             The caller's code for following the plan proceeds in four
+ *             parts.
+ *
+ *               1. Insert the first @plan->head@ input items into the
+ *                  reservoir; there will be sufficient space, and
+ *                  @plan->head@ will be at most @pol->blksz@.
+ *
+ *               2. Process the first @plan->from_rsvr@ items from the
+ *                  reservoir, shifting the remaining items forward;
+ *                  @plan->from_rsvr@ will be a multiple of @pol->blksz@.
+ *
+ *               3. Process the next @plan->from_input@ items directly from
+ *                  the input; @plan->from_input@ will be a multiple of
+ *                  @pol->blksz@.
+ *
+ *               4. Insert the remaining @plan->tail@ input items into the
+ *                  reservoir for next time.
+ */
+
+extern void rsvr_mkplan(rsvr_plan */*plan*/, const rsvr_policy */*pol*/,
+                       size_t /*used*/, size_t /*insz*/);
+
+/* --- @rsvr_setup@ --- *
+ *
+ * Arguments:  @rsvr_state *st@ = pointer to state structure to fill in
+ *             @const rsvr_policy *pol@ = reservoir policy to follow
+ *             @void *rsvr@ = pointer to the actual reservoir
+ *             @unsigned *used@ = pointer to the reservoir level
+ *             @const void *in@ = pointer to the input data
+ *             @size_t insz@ = size of the input
+ *
+ * Returns:    ---
+ *
+ * Use:                Prepares for a simple operation.  This performs the initial
+ *             copy of input data into the reservoir, and prepares for the
+ *             next step.
+ *
+ *             After this, the calling code should usually proceed as
+ *             follows.
+ *
+ *               1. Call @RSVR_NEXT@ in a sequence of loops, with
+ *                  successively smaller values of @n@, to process waiting
+ *                  data from the reservoir.  Usually, each @n@ will be some
+ *                  multiple of the block size @pol->blksz@, and the final
+ *                  loop will have @n = pol->blksz@.
+ *
+ *               2. Call @rsvr_done@ to indicate that this has been done.
+ *
+ *               3. Call @RSVR_NEXT@ in a sequence of loops, as in step 1,
+ *                  to process the remaining data from the input buffer.
+ *
+ *               4. Call @rsvr_done@ to indicate that the job is complete.
+ */
+
+extern void rsvr_setup(rsvr_state */*st*/, const rsvr_policy */*pol*/,
+                      void */*rsvr*/, unsigned */*used*/,
+                      const void */*in*/, size_t /*insz*/);
+
+/* --- @RSVR_NEXT@, @rsvr_next@ --- *
+ *
+ * Arguments:  @rsvr_state *st@ = pointer to the state structure
+ *             @size_t n@ = amount of input data required, in bytes; should
+ *                     usually be a multiple of @pol->blksz@
+ *
+ * Returns:    A pointer to the next @n@ bytes of input, or null if there is
+ *             insufficient data remaining.
+ */
+
+#define RSVR_NEXT(st, n)                                               \
+       ((n) > (st)->sz                                                 \
+               ? 0                                                     \
+               : ((st)->sz -= (n),                                     \
+                  (st)->p += (n),                                      \
+                  (const void *)((st)->p - (n))))
+extern const void *rsvr_next(rsvr_state */*st*/, size_t /*n*/);
+
+/* --- @rsvr_done@ --- *
+ *
+ * Arguments:  @rsvr_state *st@ = pointer to the state structure
+ *
+ * Returns:    Zero after the first pass, nonzero after the second.
+ *
+ * Use:                Reports that the first or second stage (see @rsvr_setup@
+ *             above) of an operation has been completed.
+ *
+ *             If the first stage is complete, then this shifts stuff about
+ *             in the reservoir and prepares for the second stage; if the
+ *             second stage is complete, then it copies the remaining input
+ *             into the reservoir and marks the state as complete.
+ */
+
+extern int rsvr_done(rsvr_state */*st*/);
+
+/* --- @RSVR_DO@ --- *
+ *
+ * Arguments:  @st@ = pointer to state structure
+ *
+ * Use:                Invoke as @RSVR_DO(st) stmt@: performs two passes of @stmt@
+ *             over the reservoir and input buffers respectively.
+ */
+
+#define RSVR_DO(st) switch (0) while (!rsvr_done(st)) case 0:
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif