/*
$Id: memblock.c 2065 2006-04-10 20:38:36Z paul $
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
Copyright (c) 2005-2006 NFG Net Facilities Group BV support@nfg.nl
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* memblock.c
*
* implementations of functions declared in memblock.h
*/
#include "dbmail.h"
#define MAX_ERROR_SIZE 128
enum __M_ERRORS { M_NOERROR, M_NOMEM, M_BADMEM, M_BADDATA, M_BADWHENCE,
M_LASTERR };
const char *__m_error_desc[M_LASTERR] = {
"no error", "not enough memory", "bad memory structure specified",
"bad data block specified", "bad whence indicator specified"
};
int __m_errno;
char __m_error_str[MAX_ERROR_SIZE];
/* internal use only */
int __m_blkadd(MEM * m);
/*
* mopen()
*
* opens a mem-structure
*/
MEM *mopen()
{
MEM *mp = (MEM *) dm_malloc(sizeof(MEM));
if (!mp) {
__m_errno = M_NOMEM;
return NULL;
}
memset(mp, 0, sizeof(*mp));
mp->firstblk = (memblock_t *) dm_malloc(sizeof(memblock_t));
if (!mp->firstblk) {
__m_errno = M_NOMEM;
dm_free(mp);
return NULL;
}
mp->firstblk->nextblk = NULL;
mp->firstblk->prevblk = NULL;
mp->lastblk = mp->firstblk;
mp->currblk = mp->firstblk;
mp->nblocks = 1;
__m_errno = M_NOERROR;
return mp;
}
/*
* mclose()
*
* closes a mem structure
*
*/
void mclose(MEM ** m)
{
memblock_t *tmp, *next;
__m_errno = M_NOERROR;
if (!m || !(*m))
return;
tmp = (*m)->firstblk;
while (tmp) {
next = tmp->nextblk; /* save address */
dm_free(tmp);
tmp = next;
}
dm_free(*m);
*m = NULL;
return;
}
/*
* mwrite()
*
* writes size bytes of data to the memory associated with m
*/
int mwrite(const void *data, int size, MEM * m)
{
long left;
if (!m) {
__m_errno = M_BADMEM;
return 0;
}
if (!data) {
__m_errno = M_BADDATA;
return 0;
}
if (size <= 0)
return 0;
left = _MEMBLOCK_SIZE - m->mpos;
if (size <= left) {
/* entire fit */
memmove(&m->currblk->data[m->mpos], data, size);
m->mpos += size;
if (size == left) {
/* update */
if (m->currblk == m->lastblk) {
if (!__m_blkadd(m)) {
m->mpos--;
m->eom = m->mpos;
return size - 1;
}
}
m->currblk = m->currblk->nextblk;
m->mpos = 0;
}
if (m->currblk == m->lastblk && m->mpos > m->eom)
m->eom = m->mpos;
return size;
}
/* copy everything that can be placed */
memmove(&m->currblk->data[m->mpos], data, left);
m->mpos += left;
if (m->currblk == m->lastblk) {
/* need a new block */
if (!__m_blkadd(m))
return left;
m->eom = 0;
}
m->currblk = m->currblk->nextblk; /* advance current block */
m->mpos = 0;
return left + mwrite(&((char *) data)[left], size - left, m);
}
/*
* mread()
*
* reads up to size bytes from m into data
*
* returns the number of bytes actually read
*/
int mread(void *data, int size, MEM * m)
{
long left;
if (!m) {
__m_errno = M_BADMEM;
return 0;
}
if (!data) {
__m_errno = M_BADDATA;
return 0;
}
if (size <= 0)
return 0;
if (m->lastblk == m->currblk)
left = m->eom - m->mpos;
else
left = _MEMBLOCK_SIZE - m->mpos;
if (left <= 0)
return 0;
if (size < left) {
/* entire fit */
memmove(data, &m->currblk->data[m->mpos], size);
m->mpos += size;
return size;
}
/* copy everything that can be placed */
memmove(data, &m->currblk->data[m->mpos], left);
m->mpos += left;
if (m->currblk == m->lastblk) {
/* no more data */
return left;
}
m->currblk = m->currblk->nextblk; /* advance current block */
m->mpos = 0;
return left + mread(&((char *) data)[left], size - left, m);
}
/*
* mseek()
*
* moves the current pos in m offset bytes according to whence:
* SEEK_SET seek from the beginning
* SEEK_CUR seek from the current pos
* SEEK_END seek from the end
*
* returns 0 on succes, -1 on error
*/
int mseek(MEM * m, long offset, int whence)
{
long left;
if (!m) {
__m_errno = M_BADMEM;
return -1;
}
switch (whence) {
case SEEK_SET:
m->currblk = m->firstblk;
m->mpos = 0;
if (offset <= 0)
return 0;
return mseek(m, offset, SEEK_CUR);
case SEEK_CUR:
if (offset == 0)
return 0;
if (offset > 0) {
left = _MEMBLOCK_SIZE - m->mpos;
if (offset >= left) {
if (m->currblk == m->lastblk) {
m->mpos = m->eom;
return 0;
}
m->currblk = m->currblk->nextblk;
m->mpos = 0;
return mseek(m, offset - left, SEEK_CUR);
} else {
m->mpos += offset;
if (m->currblk == m->lastblk
&& m->mpos > m->eom)
m->mpos = m->eom;
return 0;
}
} else {
/* offset < 0, walk backwards */
left = -m->mpos;
if (offset <= left) {
if (m->currblk == m->firstblk) {
m->mpos = 0;
return 0;
}
m->currblk = m->currblk->prevblk;
m->mpos = _MEMBLOCK_SIZE;
return mseek(m, offset - left, SEEK_CUR);
} else {
m->mpos += offset; /* remember: offset<0 */
return 0;
}
}
case SEEK_END:
m->currblk = m->lastblk;
m->mpos = m->eom;
if (offset >= 0)
return 0;
return mseek(m, offset, SEEK_CUR);
default:
__m_errno = M_BADWHENCE;
return -1;
}
return 0;
}
/*
* mtell()
*
* gives the current position in bytes (absolute cnt)
*/
long mtell(MEM * m)
{
memblock_t *tmp;
long pos = 0;
if (!m) {
__m_errno = M_BADMEM;
return -1;
}
tmp = m->firstblk;
while (tmp && tmp != m->currblk) {
pos += _MEMBLOCK_SIZE;
tmp = tmp->nextblk;
}
if (!tmp) {
__m_errno = M_BADMEM;
return -1;
}
pos += m->mpos;
return pos;
}
/*
* mrewind()
*
* equivalent to mseek(m, 0, SEEK_SET)
*/
void mrewind(MEM * m)
{
mseek(m, 0, SEEK_SET);
__m_errno = M_NOERROR;
}
/*
* merror()
*
* returns a ptr to a string describing the status of the last operation
*/
char *merror()
{
if (__m_errno >= 0 && __m_errno < M_LASTERR) {
strncpy(__m_error_str, __m_error_desc[__m_errno],
MAX_ERROR_SIZE);
return __m_error_str;
} else
return NULL;
}
/*
* mreset()
*
* restores a memory block to the state just after it was created with mopen()
*/
void mreset(MEM * m)
{
memblock_t *tmp, *next;
__m_errno = M_NOERROR;
if (!m)
return;
tmp = m->firstblk;
if (tmp)
tmp = tmp->nextblk;
while (tmp) {
next = tmp->nextblk; /* save address */
dm_free(tmp);
tmp = next;
m->nblocks--;
}
m->firstblk->nextblk = NULL;
m->mpos = 0;
m->eom = 0;
m->currblk = m->firstblk;
m->lastblk = m->firstblk;
}
/*
* __m_blkadd()
* adds a block to m
* returns 0 on failure, 1 on succes
*/
int __m_blkadd(MEM * m)
{
memblock_t *newblk;
if (!m) {
__m_errno = M_BADMEM;
return 0;
}
newblk = (memblock_t *) dm_malloc(sizeof(memblock_t));
if (!newblk) {
__m_errno = M_NOMEM;
return 0;
}
newblk->prevblk = m->lastblk;
newblk->nextblk = NULL;
m->nblocks++;
m->lastblk->nextblk = newblk;
m->lastblk = newblk;
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1