493 lines
10 KiB
C++
493 lines
10 KiB
C++
/*
|
|
* 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 "knl/knl_variable.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 = NULL;
|
|
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 = (uint8*)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 = NULL;
|
|
|
|
if (!len)
|
|
len = 8192;
|
|
|
|
mbuf = (MBuf*)px_alloc(sizeof *mbuf);
|
|
mbuf->data = (uint8*)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 = NULL;
|
|
|
|
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 = NULL;
|
|
void* priv = NULL;
|
|
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 = (PullFilter*)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 = (uint8*)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 = NULL;
|
|
|
|
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 = NULL;
|
|
|
|
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 = (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 = NULL;
|
|
void* priv = NULL;
|
|
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 = (PushFilter*)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 = (uint8*)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 = NULL;
|
|
|
|
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 = (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);
|
|
}
|