Initial version of asn1c
[com/asn1c.git] / libasn1common / asn1_buffer.c
diff --git a/libasn1common/asn1_buffer.c b/libasn1common/asn1_buffer.c
new file mode 100644 (file)
index 0000000..cd67b81
--- /dev/null
@@ -0,0 +1,171 @@
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "config.h"
+
+#include "asn1_buffer.h"
+
+#if !defined(HAVE_DECL_VASPRINTF) || (HAVE_DECL_VASPRINTF == 0)
+int vasprintf(char **ret, const char *fmt, va_list args);
+#endif
+
+/*
+ * Create and destroy the buffer.
+ */
+abuf *
+abuf_new() {
+    abuf *ab = calloc(1, sizeof(abuf));
+    assert(ab);
+    ab->length = 0;
+    ab->size = 32;
+    ab->buffer = calloc(1, ab->size);
+    assert(ab->buffer);
+    return ab;
+}
+
+void abuf_free(abuf *ab) {
+    if(ab) {
+        union {
+            const char *c_buf;
+            char *nc_buf;
+        } const_cast;
+        const_cast.c_buf = ab->buffer;
+        free(const_cast.nc_buf);
+        free(ab);
+    }
+}
+
+/*
+ * Erase contents of the buffer (without destroying it).
+ */
+void
+abuf_clear(abuf *ab) {
+    union {
+        const char *c_buf;
+        char *nc_buf;
+    } const_cast;
+    if(!ab->buffer) {
+        ab->size = 32;
+        ab->buffer = calloc(1, ab->size);
+        assert(ab->buffer);
+    }
+    const_cast.c_buf = ab->buffer;
+    ab->length = 0;
+    const_cast.nc_buf[0] = '\0';
+}
+
+static void
+abuf_resize_by(abuf *ab, size_t size) {
+    union {
+        const char *c_buf;
+        char *nc_buf;
+    } const_cast;
+    const_cast.c_buf = ab->buffer;
+
+    assert(!ab->buffer || ab->buffer[ab->length] == '\0');
+
+    size_t new_size = ab->length + size;
+    char *p = realloc(const_cast.nc_buf, new_size);
+    assert(p);
+    if(!ab->buffer) {
+        assert(ab->length == 0);
+        *p = '\0';
+    }
+    ab->buffer = p;
+    assert(ab->buffer[ab->length] == '\0');
+    ab->size = new_size;
+}
+
+void abuf_add_bytes(abuf *ab, const char *str, size_t size) {
+    abuf_resize_by(ab, size + 1);
+    union {
+        const char *c_buf;
+        char *nc_buf;
+    } const_cast;
+    const_cast.c_buf = ab->buffer;
+    memcpy(&const_cast.nc_buf[ab->length], str, size);
+    ab->length += size;
+    const_cast.nc_buf[ab->length] = '\0';
+}
+
+void abuf_str(abuf *ab, const char *str) {
+    abuf_add_bytes(ab, str, strlen(str));
+}
+
+void abuf_buf(abuf *ab, const abuf *buf) {
+    abuf_add_bytes(ab, buf->buffer, buf->length);
+}
+
+int abuf_printf(abuf *ab, const char *fmt, ...) {
+    va_list ap;
+
+    for(;;) {
+        union {
+            const char *c_buf;
+            char *nc_buf;
+        } const_cast;
+        const_cast.c_buf = ab->buffer;
+        va_start(ap, fmt);
+        int ret = vsnprintf(&const_cast.nc_buf[ab->length],
+                            ab->size - ab->length, fmt, ap);
+        va_end(ap);
+        assert(ret >= 0);
+        if((size_t)ret < ab->size - ab->length) {
+            ab->length += ret;
+            assert(ab->buffer[ab->length] == '\0');
+            return ret;
+        }
+        const_cast.nc_buf[ab->length] = '\0'; /* Restore order */
+        abuf_resize_by(ab, ret + 1);
+    }
+}
+
+int abuf_vprintf(abuf *ab, const char *fmt, va_list ap) {
+    int ret;
+    char *str = 0;
+
+    ret = vasprintf(&str, fmt, ap);
+    assert(ret >= 0);
+    assert(str != NULL);
+
+    abuf_add_bytes(ab, str, ret);
+
+    free(str);
+
+    return ret;
+}
+
+#if !defined(HAVE_DECL_VASPRINTF) || (HAVE_DECL_VASPRINTF == 0)
+/* Solaris doesn't have vasprintf(3). */
+int
+vasprintf(char **ret, const char *fmt, va_list args) {
+    int actual_length = -1;
+    va_list copy;
+    va_copy(copy, args);
+
+    int suggested = vsnprintf(NULL, 0, fmt, args);
+    if(suggested >= 0) {
+        *ret = malloc(suggested + 1);
+        if(*ret) {
+            int actual_length = vsnprintf(*ret, suggested + 1, fmt, copy);
+            if(actual_length >= 0) {
+                assert(actual_length == suggested);
+                assert((*ret)[actual_length] == '\0');
+            } else {
+                free(*ret);
+                *ret = 0;
+            }
+        }
+    } else {
+        *ret = NULL;
+        assert(suggested >= 0); /* Can't function like this */
+    }
+    va_end(args);
+
+    return actual_length;
+}
+#endif