summaryrefslogtreecommitdiffstats
path: root/contrib/pgcrypto/mbuf.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/pgcrypto/mbuf.c562
1 files changed, 562 insertions, 0 deletions
diff --git a/contrib/pgcrypto/mbuf.c b/contrib/pgcrypto/mbuf.c
new file mode 100644
index 0000000..548ef62
--- /dev/null
+++ b/contrib/pgcrypto/mbuf.c
@@ -0,0 +1,562 @@
+/*
+ * mbuf.c
+ * Memory buffer operations.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/mbuf.c
+ */
+
+#include "postgres.h"
+
+#include "mbuf.h"
+#include "px.h"
+
+#define STEP (16*1024)
+
+struct MBuf
+{
+ uint8 *data;
+ uint8 *data_end;
+ uint8 *read_pos;
+ uint8 *buf_end;
+ bool no_write;
+ bool own_data;
+};
+
+int
+mbuf_avail(MBuf *mbuf)
+{
+ return mbuf->data_end - mbuf->read_pos;
+}
+
+int
+mbuf_size(MBuf *mbuf)
+{
+ return mbuf->data_end - mbuf->data;
+}
+
+int
+mbuf_tell(MBuf *mbuf)
+{
+ return mbuf->read_pos - mbuf->data;
+}
+
+int
+mbuf_free(MBuf *mbuf)
+{
+ if (mbuf->own_data)
+ {
+ px_memset(mbuf->data, 0, mbuf->buf_end - mbuf->data);
+ px_free(mbuf->data);
+ }
+ px_free(mbuf);
+ return 0;
+}
+
+static void
+prepare_room(MBuf *mbuf, int block_len)
+{
+ uint8 *newbuf;
+ unsigned newlen;
+
+ if (mbuf->data_end + block_len <= mbuf->buf_end)
+ return;
+
+ newlen = (mbuf->buf_end - mbuf->data)
+ + ((block_len + STEP + STEP - 1) & -STEP);
+
+ newbuf = px_realloc(mbuf->data, newlen);
+
+ mbuf->buf_end = newbuf + newlen;
+ mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data);
+ mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data);
+ mbuf->data = newbuf;
+}
+
+int
+mbuf_append(MBuf *dst, const uint8 *buf, int len)
+{
+ if (dst->no_write)
+ {
+ px_debug("mbuf_append: no_write");
+ return PXE_BUG;
+ }
+
+ prepare_room(dst, len);
+
+ memcpy(dst->data_end, buf, len);
+ dst->data_end += len;
+
+ return 0;
+}
+
+MBuf *
+mbuf_create(int len)
+{
+ MBuf *mbuf;
+
+ if (!len)
+ len = 8192;
+
+ mbuf = px_alloc(sizeof *mbuf);
+ mbuf->data = px_alloc(len);
+ mbuf->buf_end = mbuf->data + len;
+ mbuf->data_end = mbuf->data;
+ mbuf->read_pos = mbuf->data;
+
+ mbuf->no_write = false;
+ mbuf->own_data = true;
+
+ return mbuf;
+}
+
+MBuf *
+mbuf_create_from_data(uint8 *data, int len)
+{
+ MBuf *mbuf;
+
+ mbuf = px_alloc(sizeof *mbuf);
+ mbuf->data = (uint8 *) data;
+ mbuf->buf_end = mbuf->data + len;
+ mbuf->data_end = mbuf->data + len;
+ mbuf->read_pos = mbuf->data;
+
+ mbuf->no_write = true;
+ mbuf->own_data = false;
+
+ return mbuf;
+}
+
+
+int
+mbuf_grab(MBuf *mbuf, int len, uint8 **data_p)
+{
+ if (len > mbuf_avail(mbuf))
+ len = mbuf_avail(mbuf);
+
+ mbuf->no_write = true;
+
+ *data_p = mbuf->read_pos;
+ mbuf->read_pos += len;
+ return len;
+}
+
+int
+mbuf_rewind(MBuf *mbuf)
+{
+ mbuf->read_pos = mbuf->data;
+ return 0;
+}
+
+int
+mbuf_steal_data(MBuf *mbuf, uint8 **data_p)
+{
+ int len = mbuf_size(mbuf);
+
+ mbuf->no_write = true;
+ mbuf->own_data = false;
+
+ *data_p = mbuf->data;
+
+ mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL;
+
+ return len;
+}
+
+/*
+ * PullFilter
+ */
+
+struct PullFilter
+{
+ PullFilter *src;
+ const PullFilterOps *op;
+ int buflen;
+ uint8 *buf;
+ int pos;
+ void *priv;
+};
+
+int
+pullf_create(PullFilter **pf_p, const PullFilterOps *op, void *init_arg, PullFilter *src)
+{
+ PullFilter *pf;
+ void *priv;
+ int res;
+
+ if (op->init != NULL)
+ {
+ res = op->init(&priv, init_arg, src);
+ if (res < 0)
+ return res;
+ }
+ else
+ {
+ priv = init_arg;
+ res = 0;
+ }
+
+ pf = px_alloc(sizeof(*pf));
+ memset(pf, 0, sizeof(*pf));
+ pf->buflen = res;
+ pf->op = op;
+ pf->priv = priv;
+ pf->src = src;
+ if (pf->buflen > 0)
+ {
+ pf->buf = px_alloc(pf->buflen);
+ pf->pos = 0;
+ }
+ else
+ {
+ pf->buf = NULL;
+ pf->pos = 0;
+ }
+ *pf_p = pf;
+ return 0;
+}
+
+void
+pullf_free(PullFilter *pf)
+{
+ if (pf->op->free)
+ pf->op->free(pf->priv);
+
+ if (pf->buf)
+ {
+ px_memset(pf->buf, 0, pf->buflen);
+ px_free(pf->buf);
+ }
+
+ px_memset(pf, 0, sizeof(*pf));
+ px_free(pf);
+}
+
+/* may return less data than asked, 0 means eof */
+int
+pullf_read(PullFilter *pf, int len, uint8 **data_p)
+{
+ int res;
+
+ if (pf->op->pull)
+ {
+ if (pf->buflen && len > pf->buflen)
+ len = pf->buflen;
+ res = pf->op->pull(pf->priv, pf->src, len, data_p,
+ pf->buf, pf->buflen);
+ }
+ else
+ res = pullf_read(pf->src, len, data_p);
+ return res;
+}
+
+int
+pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf)
+{
+ int res,
+ total;
+ uint8 *tmp;
+
+ res = pullf_read(pf, len, data_p);
+ if (res <= 0 || res == len)
+ return res;
+
+ /* read was shorter, use tmpbuf */
+ memcpy(tmpbuf, *data_p, res);
+ *data_p = tmpbuf;
+ len -= res;
+ total = res;
+
+ while (len > 0)
+ {
+ res = pullf_read(pf, len, &tmp);
+ if (res < 0)
+ {
+ /* so the caller must clear only on success */
+ px_memset(tmpbuf, 0, total);
+ return res;
+ }
+ if (res == 0)
+ break;
+ memcpy(tmpbuf + total, tmp, res);
+ total += res;
+ len -= res;
+ }
+ return total;
+}
+
+/*
+ * caller wants exactly len bytes and don't bother with references
+ */
+int
+pullf_read_fixed(PullFilter *src, int len, uint8 *dst)
+{
+ int res;
+ uint8 *p;
+
+ res = pullf_read_max(src, len, &p, dst);
+ if (res < 0)
+ return res;
+ if (res != len)
+ {
+ px_debug("pullf_read_fixed: need=%d got=%d", len, res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ if (p != dst)
+ memcpy(dst, p, len);
+ return 0;
+}
+
+/*
+ * read from MBuf
+ */
+static int
+pull_from_mbuf(void *arg, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ MBuf *mbuf = arg;
+
+ return mbuf_grab(mbuf, len, data_p);
+}
+
+static const struct PullFilterOps mbuf_reader = {
+ NULL, pull_from_mbuf, NULL
+};
+
+int
+pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src)
+{
+ return pullf_create(mp_p, &mbuf_reader, src, NULL);
+}
+
+
+/*
+ * PushFilter
+ */
+
+struct PushFilter
+{
+ PushFilter *next;
+ const PushFilterOps *op;
+ int block_size;
+ uint8 *buf;
+ int pos;
+ void *priv;
+};
+
+int
+pushf_create(PushFilter **mp_p, const PushFilterOps *op, void *init_arg, PushFilter *next)
+{
+ PushFilter *mp;
+ void *priv;
+ int res;
+
+ if (op->init != NULL)
+ {
+ res = op->init(next, init_arg, &priv);
+ if (res < 0)
+ return res;
+ }
+ else
+ {
+ priv = init_arg;
+ res = 0;
+ }
+
+ mp = px_alloc(sizeof(*mp));
+ memset(mp, 0, sizeof(*mp));
+ mp->block_size = res;
+ mp->op = op;
+ mp->priv = priv;
+ mp->next = next;
+ if (mp->block_size > 0)
+ {
+ mp->buf = px_alloc(mp->block_size);
+ mp->pos = 0;
+ }
+ else
+ {
+ mp->buf = NULL;
+ mp->pos = 0;
+ }
+ *mp_p = mp;
+ return 0;
+}
+
+void
+pushf_free(PushFilter *mp)
+{
+ if (mp->op->free)
+ mp->op->free(mp->priv);
+
+ if (mp->buf)
+ {
+ px_memset(mp->buf, 0, mp->block_size);
+ px_free(mp->buf);
+ }
+
+ px_memset(mp, 0, sizeof(*mp));
+ px_free(mp);
+}
+
+void
+pushf_free_all(PushFilter *mp)
+{
+ PushFilter *tmp;
+
+ while (mp)
+ {
+ tmp = mp->next;
+ pushf_free(mp);
+ mp = tmp;
+ }
+}
+
+static int
+wrap_process(PushFilter *mp, const uint8 *data, int len)
+{
+ int res;
+
+ if (mp->op->push != NULL)
+ res = mp->op->push(mp->next, mp->priv, data, len);
+ else
+ res = pushf_write(mp->next, data, len);
+ if (res > 0)
+ return PXE_BUG;
+ return res;
+}
+
+/* consumes all data, returns len on success */
+int
+pushf_write(PushFilter *mp, const uint8 *data, int len)
+{
+ int need,
+ res;
+
+ /*
+ * no buffering
+ */
+ if (mp->block_size <= 0)
+ return wrap_process(mp, data, len);
+
+ /*
+ * try to empty buffer
+ */
+ need = mp->block_size - mp->pos;
+ if (need > 0)
+ {
+ if (len < need)
+ {
+ memcpy(mp->buf + mp->pos, data, len);
+ mp->pos += len;
+ return 0;
+ }
+ memcpy(mp->buf + mp->pos, data, need);
+ len -= need;
+ data += need;
+ }
+
+ /*
+ * buffer full, process
+ */
+ res = wrap_process(mp, mp->buf, mp->block_size);
+ if (res < 0)
+ return res;
+ mp->pos = 0;
+
+ /*
+ * now process directly from data
+ */
+ while (len > 0)
+ {
+ if (len > mp->block_size)
+ {
+ res = wrap_process(mp, data, mp->block_size);
+ if (res < 0)
+ return res;
+ data += mp->block_size;
+ len -= mp->block_size;
+ }
+ else
+ {
+ memcpy(mp->buf, data, len);
+ mp->pos += len;
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+pushf_flush(PushFilter *mp)
+{
+ int res;
+
+ while (mp)
+ {
+ if (mp->block_size > 0)
+ {
+ res = wrap_process(mp, mp->buf, mp->pos);
+ if (res < 0)
+ return res;
+ }
+
+ if (mp->op->flush)
+ {
+ res = mp->op->flush(mp->next, mp->priv);
+ if (res < 0)
+ return res;
+ }
+
+ mp = mp->next;
+ }
+ return 0;
+}
+
+
+/*
+ * write to MBuf
+ */
+static int
+push_into_mbuf(PushFilter *next, void *arg, const uint8 *data, int len)
+{
+ int res = 0;
+ MBuf *mbuf = arg;
+
+ if (len > 0)
+ res = mbuf_append(mbuf, data, len);
+ return res < 0 ? res : 0;
+}
+
+static const struct PushFilterOps mbuf_filter = {
+ NULL, push_into_mbuf, NULL, NULL
+};
+
+int
+pushf_create_mbuf_writer(PushFilter **res, MBuf *dst)
+{
+ return pushf_create(res, &mbuf_filter, dst, NULL);
+}