/*----- 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
} 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 -----------------------------------------------------*/
#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 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(_) _(z, z, z)
+#define BUF_DOSUFFIXES(_) DOUINTCONV(_) BUF_DOKLUDGESUFFIXES(_) _(z, z, z)
/*----- Functions provided ------------------------------------------------*/
extern void buf_init(buf */*b*/, void */*p*/, size_t /*sz*/);
+/* --- @dbuf_create@ --- *
+ *
+ * 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_create(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
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
extern int buf_getu##w(buf */*b*/, uint##n */*w*/);
DOUINTCONV(BUF_DECL_GETU_)
+/* --- @buf_getk64{,l,b}@ --- *
+ *
+ * Arguments: @buf *b@ = pointer to a buffer block
+ * @kludge64 *w@ = where to put the word
+ *
+ * Returns: Zero if OK, or nonzero if there wasn't a word there.
+ *
+ * Use: Gets a word of appropriate size and order from a buffer.
+ */
+
+extern int buf_getk64(buf */*b*/, kludge64 */*w*/);
+extern int buf_getk64l(buf */*b*/, kludge64 */*w*/);
+extern int buf_getk64b(buf */*b*/, kludge64 */*w*/);
+
/* --- @buf_putu{8,{16,24,32,64}{,l,b}}@ --- *
*
* Arguments: @buf *b@ = pointer to a buffer block
extern int buf_putu##w(buf */*b*/, uint##n /*w*/);
DOUINTCONV(BUF_DECL_PUTU_)
+/* --- @buf_putk64{,l,b}@ --- *
+ *
+ * Arguments: @buf *b@ = pointer to a buffer block
+ * @kludge64 w@ = word to write
+ *
+ * Returns: Zero if OK, or nonzero if there wasn't enough space
+ *
+ * Use: Gets a word of appropriate size and order from a buffer.
+ */
+
+extern int buf_putk64(buf */*b*/, kludge64 /*w*/);
+extern int buf_putk64l(buf */*b*/, kludge64 /*w*/);
+extern int buf_putk64b(buf */*b*/, kludge64 /*w*/);
+
/* --- @buf_getmem{8,{16,24,32,64}{,l,b},z} --- *
*
* Arguments: @buf *b@ = pointer to a buffer block
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