chiark / gitweb /
@@@ so much mess
[mLib] / struct / buf.h
index 97dbd105b8aff5c4b5a36547f2b783696f5cfcd6..389d038c65b303ab53b69f852d3956852aa7dfcf 100644 (file)
 
 /*----- Header files ------------------------------------------------------*/
 
+#include <stdarg.h>
 #include <stddef.h>
 
 #ifndef MLIB_BITS_H
 #  include "bits.h"
 #endif
 
+#ifndef MLIB_CONTROL_H
+#  include "control.h"
+#endif
+
 #ifndef MLIB_DSTR_H
 #  include "dstr.h"
 #endif
@@ -62,6 +67,19 @@ typedef struct buf {
 } buf;
 
 #define BF_BROKEN 1u                   /* Buffer is broken */
+#define BF_ALLOC 2u                    /* Buffer is dynamic */
+#define BF_WRITE 4u                    /* Currently writing to buffer */
+
+typedef struct dbuf {
+  buf _b;
+  arena *a;                            /* Allocation arena */
+  size_t sz;                           /* Allocated size */
+} dbuf;
+
+#define DBUF_INIT { { 0, 0, 0, BF_ALLOC | BF_WRITE}, &arena_stdlib, 0 }
+#define DBUF_BUF(db) (&(db)->_b)
+
+extern const struct gprintf_ops buf_printops;
 
 /*----- Useful macros -----------------------------------------------------*/
 
@@ -78,13 +96,31 @@ typedef struct buf {
 #if GCC_VERSION_P(8, 0)
 #  define BENSURE(b, sz)                                               \
      MUFFLE_WARNINGS_EXPR(GCC_WARNING("-Wint-in-bool-context"),                \
-       (BBAD(b) ? -1 : (sz) > BLEFT(b) ? (b)->f |= BF_BROKEN, -1 : 0))
+       (BBAD(b) ? -1 : (sz) > BLEFT(b) ? buf_tryextend(b, sz) : 0))
 #else
 #  define BENSURE(b, sz)                                               \
-     (BBAD(b) ? -1 : (sz) > BLEFT(b) ? (b)->f |= BF_BROKEN, -1 : 0)
+     (BBAD(b) ? -1 : (sz) > BLEFT(b) ? buf_tryextend(b, sz) : 0)
 #endif
 
-#define BUF_DOSUFFIXES(_) DOUINTCONV(_) _(z, z, z)
+#define DBBASE(db) BBASE(DBUF_BUF(db))
+#define DBLIM(db) BLIM(DBUF_BUF(db))
+#define DBCUR(db) BCUR(DBUF_BUF(db))
+#define DBSZ(db) BSZ(DBUF_BUF(db))
+#define DBLEN(db) BLEN(DBUF_BUF(db))
+#define DBLEFT(db) BLEFT(DBUF_BUF(db))
+#define DBSTEP(db, sz) BSTEP(DBUF_BUF(db), (sz))
+#define DBBAD(db) BBAD(DBUF_BUF(db))
+#define DBOK(db) BOK(DBUF_BUF(db))
+#define DBENSURE(b, sz) BENSURE(DBUF_BUF(db), (sz))
+
+#ifdef HAVE_UINT64
+#  define BUF_DOKLUDGESUFFIXES(_)
+#else
+#  define BUF_DOKLUDGESUFFIXES(_)                                      \
+       _(64, 64, 64) _(64, 64_B, 64b) _(64, 64_L, 64l)
+#endif
+
+#define BUF_DOSUFFIXES(_) DOUINTCONV(_) BUF_DOKLUDGESUFFIXES(_) _(z, z, z)
 
 /*----- Functions provided ------------------------------------------------*/
 
@@ -101,6 +137,40 @@ typedef struct buf {
 
 extern void buf_init(buf */*b*/, void */*p*/, size_t /*sz*/);
 
+/* --- @dbuf_init@ --- *
+ *
+ * Arguments:  @dbuf *db@ = pointer to a dynamic buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a dynamic buffer.  The buffer is initially empty,
+ *             and ready for writing.
+ */
+
+extern void dbuf_init(dbuf */*db*/);
+
+/* --- @dbuf_reset@ --- *
+ *
+ * Arguments:  @dbuf *db@ = pointer to a buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Resets a buffer so that it can be written again.
+ */
+
+extern void dbuf_reset(dbuf */*db*/);
+
+/* --- @dbuf_destroy@ --- *
+ *
+ * Arguments:  @dbuf *db@ = pointer to a buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Release all of the resources held by a dynamic buffer.
+ */
+
+extern void dbuf_destroy(dbuf */*db*/);
+
 /* --- @buf_break@ --- *
  *
  * Arguments:  @buf *b@ = pointer to a buffer block
@@ -136,6 +206,19 @@ extern void buf_flip(buf */*b*/);
 
 extern int buf_ensure(buf */*b*/, size_t /*sz*/);
 
+/* --- @buf_tryextend@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @size_t sz@ = size of data wanted
+ *
+ * Returns:    Zero if it worked, nonzero if the buffer won't grow.
+ *
+ * Use:                Extend the buffer so that at least @sz@ bytes are available.
+ *             This only works if the buffer is allocated.
+ */
+
+extern int buf_tryextend(buf */*b*/, size_t /*sz*/);
+
 /* --- @buf_get@ --- *
  *
  * Arguments:  @buf *b@ = pointer to a buffer block
@@ -346,6 +429,146 @@ BUF_DOSUFFIXES(BUF_DECL_PUTDSTR_)
   extern int buf_putstr##w(buf */*b*/, const char */*p*/);
 BUF_DOSUFFIXES(BUF_DECL_PUTSTR_)
 
+/* --- @buf_putf64{,b,l} --- *
+ *
+ * Arguments:  @buf *b@ = a buffer to write to
+ *             @double x@ = a number to write
+ *
+ * Returns:    Zero on success, @-1@ on failure (and the buffer is broken).
+ *
+ *             On C89, this function can't detect negative zero so these
+ *             will be silently written as positive zero.
+ *
+ *             This function doesn't distinguish NaNs.  Any NaN is written
+ *             as a quiet NaN with all payload bits zero.
+ *
+ *             A finite value with too large a magnitude to be represented
+ *             is rounded to the appropriate infinity.  Other finite values
+ *             are rounded as necessary, in the usual IEEE 754 round-to-
+ *             nearest-or-even way.
+ */
+
+extern int buf_putf64(buf */*b*/, double /*x*/);
+extern int buf_putf64b(buf */*b*/, double /*x*/);
+extern int buf_putf64l(buf */*b*/, double /*x*/);
+
+/* --- @buf_getf64{,b,l} --- *
+ *
+ * Arguments:  @buf *b@ = a buffer to read from
+ *             @double *x_out@ = where to put the result
+ *
+ * Returns:    Zero on success, @-1@ on failure (and the buffer is broken).
+ *
+ *             If the system supports NaNs, then any encoded NaN is returned
+ *             as the value of @NAN@ in @<math.h>@; otherwise, this function
+ *             reports failure.
+ *
+ *             In general, values are rounded to the nearest available
+ *             value, in the way that the system usually rounds.  If the
+ *             system doesn't support infinities, then any encoded infinity
+ *             is reported as the largest-possible-magnitude finite value
+ *             instead.
+ */
+
+extern int buf_getf64(buf */*b*/, double *x_/*out*/);
+extern int buf_getf64b(buf */*b*/, double *x_/*out*/);
+extern int buf_getf64l(buf */*b*/, double *x_/*out*/);
+
+#define BUF_ENCLOSETAG(tag, buf, mk, check, poke, lensz)               \
+  MC_BEFORE(tag##__save,                                               \
+    { (mk) = BLEN(buf);                                                        \
+      if (!BENSURE(buf, lensz)) (buf)->p += (lensz); })                        \
+  MC_AFTER(tag##__poke,                                                        \
+    { size_t _delta = BLEN(buf) - (mk) + (lensz);                      \
+      assert(check);                                                   \
+      if (BOK(buf)) poke((buf)->base + (mk), _delta);  })
+
+#define BUF_ENCLOSEZTAG(tag, buf)                                      \
+  MC_AFTER(tag##__zero, { buf_putbyte(buf, 0); })
+
+#define BUF_ENCLOSENATIVETAG(tag, buf, mk, W)                          \
+  BUF_ENCLOSETAG(tag, buf, mk, (_delta <= MASK##W), STORE##W, SZ_##W)
+
+#define BUF_STORESZK64(p, sz)                                          \
+  do { kludge64 _k; ASSIGN64(_k, (sz)); STORE64_((p), _k); } while (0)
+#define BUF_STORESZK64_B(p, sz)                                                \
+  do { kludge64 _k; ASSIGN64(_k, (sz)); STORE64_B_((p), _k); } while (0)
+#define BUF_STORESZK64_L(p, sz)                                                \
+  do { kludge64 _k; ASSIGN64(_k, (sz)); STORE64_L_((p), _k); } while (0)
+#define BUF_ENCLOSEK64TAG(tag, buf, mk, W)                             \
+  BUF_ENCLOSE(tag, buf, mk, 1, BUF_STORESZK##W, 8)
+
+#define BUF_ENCLOSE8(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 8)
+#define BUF_ENCLOSE16(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 16)
+#define BUF_ENCLOSE16_B(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 16_B)
+#define BUF_ENCLOSE16_L(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 16_L)
+#define BUF_ENCLOSE24(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 24)
+#define BUF_ENCLOSE24_B(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 24_B)
+#define BUF_ENCLOSE24_L(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 24_L)
+#define BUF_ENCLOSE32(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 32)
+#define BUF_ENCLOSE32_B(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 32_B)
+#define BUF_ENCLOSE32_L(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 32_L)
+#ifdef HAVE_UINT64
+#  define BUF_ENCLOSE64(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 64)
+#  define BUF_ENCLOSE64_B(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 64_B)
+#  define BUF_ENCLOSE64_L(buf, mk) BUF_ENCLOSENATIVETAG(encl, buf, mk, 64_L)
+#else
+#  define BUF_ENCLOSE64(buf, mk) BUF_ENCLOSEK64TAG(encl, buf, mk, 64)
+#  define BUF_ENCLOSE64_B(buf, mk) BUF_ENCLOSEK64TAG(encl, buf, mk, 64_B)
+#  define BUF_ENCLOSE64_L(buf, mk) BUF_ENCLOSEK64TAG(encl, buf, mk, 64_L)
+#endif
+#define BUF_ENCLOSEZ(buf) BUF_ENCLOSEZTAG(encl, buf)
+
+/* --- @buf_vputstrf@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer
+ *             @const char *p@ = pointer to @printf@-style format string
+ *             @va_list *ap@ = argument handle
+ *
+ * Returns:    The number of characters written to the string, or @-1@ on
+ *             failure.
+ *
+ * Use:                As for @buf_putstrf@, but may be used as a back-end to user-
+ *             supplied functions with @printf@-style interfaces.
+ */
+
+extern int buf_vputstrf(buf */*b*/, const char */*p*/, va_list */*ap*/);
+
+/* --- @buf_putstrf@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer
+ *             @const char *p@ = pointer to @printf@-style format string
+ *             @...@ = argument handle
+ *
+ * Returns:    The number of characters written to the string, or @-1@ on
+ *             failure.
+ *
+ * Use:                Format a string to a buffer.  The resulting output is not
+ *             null-terminated.
+ */
+
+extern PRINTF_LIKE(2, 3) int buf_putstrf(buf */*b*/, const char */*p*/, ...);
+
+/* --- @buf_{,v}putstrf{8,{16,24,32,64}{,b,l},z}@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer
+ *             @const char *p@ = pointer to @printf@-style format string
+ *             @va_list *ap@ = argument handle
+ *
+ * Returns:    The number of characters written to the string, or @-1@ on
+ *             failure.
+ *
+ * Use:                As for @buf_putstr@, but using a format string.
+ */
+
+#define BUF_DECL_PUTSTRF_(n, W, w)                                     \
+  extern int buf_vputstrf##w(buf */*b*/,                               \
+                            const char */*p*/, va_list */*ap*/);       \
+  extern PRINTF_LIKE(2, 3)                                             \
+    int buf_putstrf##w(buf */*b*/, const char */*p*/, ...);
+BUF_DOSUFFIXES(BUF_DECL_PUTSTRF_)
+#undef BUF_DECL_PUTSTRF_
+
 /*----- That's all, folks -------------------------------------------------*/
 
 #ifdef __cplusplus