view src/lib/buffer.c @ 2708:f1e9f3ec8135 HEAD

Buffer API change: we no longer support limited sized buffers where writes past limit wouldn't kill the process. They weren't used hardly anywhere, they could have hidden bugs and the code for handling them was too complex. This also changed base64 and hex-binary APIs.
author Timo Sirainen <tss@iki.fi>
date Fri, 08 Oct 2004 20:51:47 +0300
parents 21bb4bb2ca39
children 7cf652f2a33a
line wrap: on
line source

/* Copyright (c) 2002-2003 Timo Sirainen */

/* @UNSAFE: whole file */

#include "lib.h"
#include "buffer.h"

struct real_buffer {
	/* public: */
	const unsigned char *r_buffer;
	size_t used;

	/* private: */
	unsigned char *w_buffer;
	size_t dirty, alloc;

	pool_t pool;

	unsigned int alloced:1;
	unsigned int dynamic:1;
};

static void buffer_alloc(struct real_buffer *buf, size_t size)
{
	i_assert(buf->w_buffer == NULL || buf->alloced);

	if (size == buf->alloc)
		return;

	i_assert(size > buf->alloc);

	buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, size);
	buf->alloc = size;

	buf->r_buffer = buf->w_buffer;
	buf->alloced = TRUE;
}

static inline void
buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size)
{
	size_t new_size;

	if ((size_t)-1 - pos < data_size) {
		i_panic("Buffer write out of range (%"PRIuSIZE_T
			" + %"PRIuSIZE_T")", pos, data_size);
	}
	new_size = pos + data_size;

	if (new_size > buf->alloc) {
		if (!buf->dynamic) {
			i_panic("Buffer full (%"PRIuSIZE_T" > %"PRIuSIZE_T")",
				pos + data_size, buf->alloc);
		}

		buffer_alloc(buf, nearest_power(new_size));
	}

	if (new_size > buf->used)
		buf->used = new_size;
}

buffer_t *buffer_create_static_hard(pool_t pool, size_t size)
{
	struct real_buffer *buf;

	buf = p_new(pool, struct real_buffer, 1);
	buf->pool = pool;
	buffer_alloc(buf, size);
	return (buffer_t *)buf;
}

buffer_t *buffer_create_data(pool_t pool, void *data, size_t size)
{
	struct real_buffer *buf;

	buf = p_new(pool, struct real_buffer, 1);
	buf->pool = pool;
	buf->alloc = size;
	buf->r_buffer = buf->w_buffer = data;
	return (buffer_t *)buf;
}

buffer_t *buffer_create_const_data(pool_t pool, const void *data, size_t size)
{
	struct real_buffer *buf;

	buf = p_new(pool, struct real_buffer, 1);
	buf->pool = pool;
	buf->used = buf->alloc = size;
	buf->r_buffer = data;
	return (buffer_t *)buf;
}

buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size)
{
	struct real_buffer *buf;

	buf = p_new(pool, struct real_buffer, 1);
	buf->pool = pool;
	buf->dynamic = TRUE;
	buffer_alloc(buf, init_size);
	return (buffer_t *)buf;
}

void buffer_free(buffer_t *_buf)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	if (buf->alloced)
		p_free(buf->pool, buf->w_buffer);
	p_free(buf->pool, buf);
}

void *buffer_free_without_data(buffer_t *_buf)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;
	void *data;

	data = buf->w_buffer;
	p_free(buf->pool, buf);
	return data;
}

void buffer_reset(buffer_t *_buf)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	memset(buf->w_buffer, 0, I_MAX(buf->used, buf->dirty));
	buf->dirty = 0;
	buf->used = 0;
}

void buffer_write(buffer_t *_buf, size_t pos,
		  const void *data, size_t data_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	buffer_check_limits(buf, pos, data_size);
	memcpy(buf->w_buffer + pos, data, data_size);
}

void buffer_append(buffer_t *buf, const void *data, size_t data_size)
{
	buffer_write(buf, buf->used, data, data_size);
}

void buffer_append_c(buffer_t *buf, unsigned char chr)
{
	buffer_append(buf, &chr, 1);
}

void buffer_insert(buffer_t *_buf, size_t pos,
		   const void *data, size_t data_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	if (pos >= buf->used)
		return buffer_write(_buf, pos, data, data_size);

	buffer_copy(_buf, pos + data_size, _buf, pos, (size_t)-1);
	memcpy(buf->w_buffer + pos, data, data_size);
}

void buffer_delete(buffer_t *_buf, size_t pos, size_t size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;
	size_t end_size;

	if (pos >= buf->used)
		return;
	end_size = buf->used - pos;

	if (size < end_size) {
		/* delete from between */
		end_size -= size;
		memmove(buf->w_buffer + pos,
			buf->w_buffer + pos + size, end_size);
	} else {
		/* delete the rest of the buffer */
		end_size = 0;
	}

	buffer_set_used_size(_buf, pos + end_size);
}

void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	buffer_check_limits(buf, pos, data_size);
	memset(buf->w_buffer + pos, 0, data_size);
}

void buffer_append_zero(buffer_t *buf, size_t data_size)
{
	buffer_write_zero(buf, buf->used, data_size);
}

void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	if (pos >= buf->used)
		return buffer_write_zero(_buf, pos, data_size);

	buffer_copy(_buf, pos + data_size, _buf, pos, (size_t)-1);
	memset(buf->w_buffer + pos, 0, data_size);
}

void buffer_copy(buffer_t *_dest, size_t dest_pos,
		 const buffer_t *_src, size_t src_pos, size_t copy_size)
{
	struct real_buffer *dest = (struct real_buffer *)_dest;
	struct real_buffer *src = (struct real_buffer *)_src;
	size_t max_size;

	i_assert(src_pos <= src->used);

	max_size = src->used - src_pos;
	if (copy_size > max_size)
		copy_size = max_size;

	buffer_check_limits(dest, dest_pos, copy_size);
	if (src == dest) {
		memmove(dest->w_buffer + dest_pos,
			src->r_buffer + src_pos, copy_size);
	} else {
		memcpy(dest->w_buffer + dest_pos,
		       src->r_buffer + src_pos, copy_size);
	}
}

void buffer_append_buf(buffer_t *dest, const buffer_t *src,
		       size_t src_pos, size_t copy_size)
{
	buffer_copy(dest, dest->used, src, src_pos, copy_size);
}

void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	buffer_check_limits(buf, pos, size);
	return buf->w_buffer + pos;
}

void *buffer_append_space_unsafe(buffer_t *buf, size_t size)
{
	return buffer_get_space_unsafe(buf, buf->used, size);
}

void *buffer_get_modifyable_data(const buffer_t *_buf, size_t *used_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	if (used_size != NULL)
		*used_size = buf->used;
	return buf->w_buffer;
}

void buffer_set_used_size(buffer_t *_buf, size_t used_size)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	i_assert(used_size <= buf->alloc);

	if (used_size < buf->used && buf->used > buf->dirty)
		buf->dirty = buf->used;

	buf->used = used_size;
}

size_t buffer_get_size(const buffer_t *_buf)
{
	struct real_buffer *buf = (struct real_buffer *)_buf;

	return buf->alloc;
}