buffer.c revision 3281669db44d09a087a203201248abbc81b3cc1a
0N/A/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
1135N/A
0N/A/* @UNSAFE: whole file */
0N/A
0N/A#include "lib.h"
0N/A#include "buffer.h"
0N/A
0N/Astruct real_buffer {
0N/A /* public: */
0N/A const unsigned char *r_buffer;
0N/A size_t used;
0N/A
0N/A /* private: */
0N/A unsigned char *w_buffer;
0N/A size_t dirty, alloc;
0N/A
0N/A pool_t pool;
0N/A
553N/A unsigned int alloced:1;
553N/A unsigned int dynamic:1;
553N/A};
0N/A
0N/Astatic void buffer_alloc(struct real_buffer *buf, size_t size)
0N/A{
0N/A i_assert(buf->w_buffer == NULL || buf->alloced);
1135N/A
0N/A if (size == buf->alloc)
0N/A return;
0N/A
0N/A i_assert(size > buf->alloc);
0N/A
0N/A buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, size);
0N/A buf->alloc = size;
0N/A
0N/A buf->r_buffer = buf->w_buffer;
0N/A buf->alloced = TRUE;
1135N/A}
0N/A
0N/Astatic inline void
0N/Abuffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size)
0N/A{
0N/A size_t new_size;
0N/A
0N/A if (unlikely((size_t)-1 - pos < data_size)) {
0N/A i_panic("Buffer write out of range (%"PRIuSIZE_T
0N/A " + %"PRIuSIZE_T")", pos, data_size);
0N/A }
0N/A new_size = pos + data_size;
0N/A
0N/A if (new_size > buf->used && buf->used < buf->dirty) {
0N/A /* clear used..dirty area */
0N/A size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size);
0N/A
0N/A memset(buf->w_buffer + buf->used, 0, max - buf->used);
0N/A }
0N/A if (new_size > buf->alloc) {
926N/A if (unlikely(!buf->dynamic)) {
926N/A i_panic("Buffer full (%"PRIuSIZE_T" > %"PRIuSIZE_T", "
926N/A "pool %s)", pos + data_size, buf->alloc,
926N/A buf->pool == NULL ? "<none>" :
926N/A pool_get_name(buf->pool));
926N/A }
926N/A
926N/A buffer_alloc(buf, pool_get_exp_grown_size(buf->pool, buf->alloc,
926N/A new_size));
926N/A }
1135N/A#if 0
1135N/A else if (new_size > buf->used && buf->alloced &&
1135N/A !buf->pool->alloconly_pool && !buf->pool->datastack_pool) {
1135N/A void *new_buf;
1135N/A
1135N/A /* buffer's size increased: move the buffer's memory elsewhere.
1135N/A this should help catch bugs where old pointers are tried to
0N/A be used to access the buffer's memory */
0N/A new_buf = p_malloc(buf->pool, buf->alloc);
0N/A memcpy(new_buf, buf->w_buffer, buf->alloc);
0N/A p_free(buf->pool, buf->w_buffer);
0N/A
0N/A buf->w_buffer = new_buf;
0N/A buf->r_buffer = new_buf;
0N/A }
926N/A#endif
926N/A
926N/A if (new_size > buf->used)
926N/A buf->used = new_size;
1135N/A i_assert(buf->used <= buf->alloc);
1135N/A}
1135N/A
1135N/Avoid buffer_create_from_data(buffer_t *buffer, void *data, size_t size)
0N/A{
0N/A struct real_buffer *buf;
0N/A
0N/A i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
0N/A
0N/A buf = (struct real_buffer *)buffer;
0N/A memset(buf, 0, sizeof(*buf));
0N/A buf->alloc = size;
0N/A buf->r_buffer = buf->w_buffer = data;
0N/A /* clear the whole memory area. unnecessary usually, but if the
0N/A buffer is used by e.g. str_c() it tries to access uninitialized
0N/A memory */
0N/A memset(data, 0, size);
0N/A}
0N/A
0N/Avoid buffer_create_from_const_data(buffer_t *buffer,
0N/A const void *data, size_t size)
0N/A{
0N/A struct real_buffer *buf;
0N/A
0N/A i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
0N/A
0N/A buf = (struct real_buffer *)buffer;
0N/A memset(buf, 0, sizeof(*buf));
0N/A
0N/A buf->used = buf->alloc = size;
0N/A buf->r_buffer = data;
0N/A i_assert(buf->w_buffer == NULL);
0N/A}
0N/A
0N/Abuffer_t *buffer_create_dynamic(pool_t pool, size_t init_size)
926N/A{
1135N/A struct real_buffer *buf;
926N/A
0N/A buf = p_new(pool, struct real_buffer, 1);
0N/A buf->pool = pool;
0N/A buf->dynamic = TRUE;
0N/A buffer_alloc(buf, init_size);
0N/A return (buffer_t *)buf;
0N/A}
0N/A
0N/Avoid buffer_free(buffer_t **_buf)
0N/A{
0N/A struct real_buffer *buf = (struct real_buffer *)*_buf;
0N/A
0N/A *_buf = NULL;
0N/A if (buf->alloced)
0N/A p_free(buf->pool, buf->w_buffer);
0N/A if (buf->pool != NULL)
0N/A p_free(buf->pool, buf);
}
void *buffer_free_without_data(buffer_t **_buf)
{
struct real_buffer *buf = (struct real_buffer *)*_buf;
void *data;
*_buf = NULL;
data = buf->w_buffer;
p_free(buf->pool, buf);
return data;
}
pool_t buffer_get_pool(const buffer_t *_buf)
{
const struct real_buffer *buf = (const struct real_buffer *)_buf;
return buf->pool;
}
void buffer_reset(buffer_t *buf)
{
buffer_set_used_size(buf, 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)
buffer_write(_buf, pos, data, data_size);
else {
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)
buffer_write_zero(_buf, pos, data_size);
else {
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;
const struct real_buffer *src = (const 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_modifiable_data(const buffer_t *_buf, size_t *used_size_r)
{
const struct real_buffer *buf = (const struct real_buffer *)_buf;
if (used_size_r != NULL)
*used_size_r = 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 (buf->used > buf->dirty)
buf->dirty = buf->used;
buf->used = used_size;
}
size_t buffer_get_size(const buffer_t *_buf)
{
const struct real_buffer *buf = (const struct real_buffer *)_buf;
return buf->alloc;
}
bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2)
{
if (buf1->used != buf2->used)
return FALSE;
return memcmp(buf1->data, buf2->data, buf1->used) == 0;
}