| /* |
| * 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 "px.h" |
| #include "mbuf.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) |
| { |
| 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; |
| |
| return; |
| } |
| |
| 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) |
| { |
| memset(pf->buf, 0, pf->buflen); |
| px_free(pf->buf); |
| } |
| |
| 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 */ |
| memset(tmpbuf, 0, total); |
| return res; |
| } |
| if (res == 0) |
| break; |
| memcpy(tmpbuf + total, tmp, res); |
| total += res; |
| } |
| return total; |
| } |
| |
| /* |
| * caller wants exatly len bytes and dont 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_MBUF_SHORT_READ; |
| } |
| 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) |
| { |
| memset(mp->buf, 0, mp->block_size); |
| px_free(mp->buf); |
| } |
| |
| 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); |
| } |