/*
- silcbuffer.h
+ silcbuffer.h
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1998 - 2002 Pekka Riikonen
+ Copyright (C) 1998 - 2007 Pekka Riikonen
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
*/
/* $Id$ */
-/* Optimized buffer managing routines. These are short inline functions. */
-
-#ifndef SILCBUFFER_H
-#define SILCBUFFER_H
/****h* silcutil/SILC Buffer Interface
*
* DESCRIPTION
*
- * SilcBuffer is very simple and easy to use, yet you can do to the
- * buffer almost anything you want with its method functions. The buffer
- * is constructed of four different data sections that in whole creates
- * the allocated data area.
- *
- * This buffer scheme is based on Linux kernel's Socket Buffer, the
- * idea were taken directly from there and credits should go there.
+ * SilcBuffer is very simple and easy to use, yet you can do to the
+ * buffer almost anything you want with its method functions. The buffer
+ * is constructed of four different data sections that in whole creates
+ * the allocated data area.
*
***/
+#ifndef SILCBUFFER_H
+#define SILCBUFFER_H
+
/****s* silcutil/SilcBufferAPI/SilcBuffer
*
* NAME
*
* EXAMPLE
*
- * SilcUInt32 truelen;
- *
- * True length of the buffer. This is set at the allocation of the
- * buffer and it should not be touched after that. This field should
- * be considered read-only.
- *
- * SilcUInt32 len;
- *
- * Length of the currently valid data area. Tells the length of the
- * data at the buffer. This is set to zero at the allocation of the
- * buffer and should not be updated by hand. Method functions of the
- * buffer automatically updates this field. However, it is not
- * read-only field and can be updated manually if necessary.
- *
* unsiged char *head;
*
* Head of the allocated buffer. This is the start of the allocated
* section. Tail section can be pulled towards end, and thus the data
* section becomes larger.
*
+ * SILC Buffer is not thread-safe. If the same SilcBuffer context must be
+ * used in multithreaded environment concurrency control must be employed.
+ *
* SOURCE
*/
-typedef struct {
- SilcUInt32 truelen;
- SilcUInt32 len;
+typedef struct SilcBufferObject {
unsigned char *head;
unsigned char *data;
unsigned char *tail;
/* Macros */
-/****d* silcutil/SilcBufferAPI/SILC_BUFFER_END
- *
+/****f* silcutil/SilcBufferAPI/silc_buffer_data
+ *
* NAME
*
- * #define SILC_BUFFER_END(...)
+ * unsigned char *silc_buffer_data(SilcBuffer sb)
*
* DESCRIPTION
*
- * Returns the true length of the buffer. This is used to pull
- * the buffer area to the end of the buffer.
+ * Returns pointer to the data area of the buffer.
*
* SOURCE
*/
-#define SILC_BUFFER_END(x) ((x)->end - (x)->head)
+#define silc_buffer_data(x) (x)->data
+/***/
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_datalen
+ *
+ * NAME
+ *
+ * #define silc_buffer_datalen ...
+ *
+ * DESCRIPTION
+ *
+ * Macro that can be used in function argument list to give the data
+ * pointer and the data length, instead of calling both silc_buffer_data
+ * and silc_buffer_len separately.
+ *
+ * EXAMPLE
+ *
+ * // Following are the same thing
+ * silc_foo_function(foo, silc_buffer_datalen(buf));
+ * silc_foo_function(foo, silc_buffer_data(buf), silc_buffer_len(buf));
+ *
+ * SOURCE
+ */
+#define silc_buffer_datalen(x) (x) ? silc_buffer_data((x)) : NULL, \
+ (x) ? silc_buffer_len((x)) : 0
/***/
/* Inline functions */
+/****d* silcutil/SilcBufferAPI/silc_buffer_truelen
+ *
+ * NAME
+ *
+ * SilcUInt32 silc_buffer_truelen(SilcBuffer sb)
+ *
+ * DESCRIPTION
+ *
+ * Returns the true length of the buffer.
+ *
+ ***/
+static inline
+SilcUInt32 silc_buffer_truelen(SilcBuffer x)
+{
+ return (SilcUInt32)(x->end - x->head);
+}
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_len
+ *
+ * NAME
+ *
+ * SilcUInt32 silc_buffer_len(SilcBuffer sb)
+ *
+ * DESCRIPTION
+ *
+ * Returns the current length of the data area of the buffer.
+ *
+ ***/
+static inline
+SilcUInt32 silc_buffer_len(SilcBuffer x)
+{
+ return (SilcUInt32)(x->tail - x->data);
+}
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_headlen
+ *
+ * NAME
+ *
+ * SilcUInt32 silc_buffer_headlen(SilcBuffer sb)
+ *
+ * DESCRIPTION
+ *
+ * Returns the current length of the head data area of the buffer.
+ *
+ ***/
+static inline
+SilcUInt32 silc_buffer_headlen(SilcBuffer x)
+{
+ return (SilcUInt32)(x->data - x->head);
+}
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_taillen
+ *
+ * NAME
+ *
+ * SilcUInt32 silc_buffer_taillen(SilcBuffer sb)
+ *
+ * DESCRIPTION
+ *
+ * Returns the current length of the tail data area of the buffer.
+ *
+ ***/
+static inline
+SilcUInt32 silc_buffer_taillen(SilcBuffer x)
+{
+ return (SilcUInt32)(x->end - x->tail);
+}
+
/****f* silcutil/SilcBufferAPI/silc_buffer_alloc
*
* SYNOPSIS
*
* DESCRIPTION
*
- * Allocates new SilcBuffer and returns it.
+ * Allocates new SilcBuffer and returns it. Returns NULL on error.
*
***/
-
+
static inline
SilcBuffer silc_buffer_alloc(SilcUInt32 len)
{
/* Allocate new SilcBuffer */
sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
- if (!sb)
+ if (silc_unlikely(!sb))
return NULL;
- /* Allocate the actual data area */
- sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
- if (!sb->head)
- return NULL;
+ if (silc_likely(len)) {
+ /* Allocate the actual data area */
+ sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
+ if (silc_unlikely(!sb->head))
+ return NULL;
- /* Set pointers to the new buffer */
- sb->truelen = len;
- sb->data = sb->head;
- sb->tail = sb->head;
- sb->end = sb->head + sb->truelen;
+ /* Set pointers to the new buffer */
+ sb->data = sb->head;
+ sb->tail = sb->head;
+ sb->end = sb->head + len;
+ }
return sb;
}
*
* DESCRIPTION
*
- * Frees SilcBuffer.
+ * Frees SilcBuffer. Can be called safely `sb' as NULL.
+ *
+ * NOTES
+ *
+ * Must not be called for buffers allocated with silc_buffer_salloc,
+ * silc_buffer_salloc_size, silc_buffer_scopy and silc_buffer_sclone.
*
***/
if (sb) {
#if defined(SILC_DEBUG)
if (sb->head)
- memset(sb->head, 'F', sb->truelen);
+ memset(sb->head, 'F', silc_buffer_truelen(sb));
#endif
silc_free(sb->head);
silc_free(sb);
{
unsigned char *buf = sb->head;
if (data_len)
- *data_len = sb->truelen;
+ *data_len = silc_buffer_truelen(sb);
sb->head = sb->data = sb->tail = sb->end = NULL;
- sb->len = sb->truelen = 0;
return buf;
}
+/****f* silcutil/SilcBufferAPI/silc_buffer_purge
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * void silc_buffer_purge(SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Same as silc_buffer_free but free's only the contents of the buffer
+ * not the buffer itself. The `sb' remains intact, data is freed. Buffer
+ * is ready for re-use after calling this function.
+ *
+ * NOTES
+ *
+ * Must not be called for buffers allocated with silc_buffer_salloc,
+ * silc_buffer_salloc_size, silc_buffer_scopy and silc_buffer_sclone.
+ *
+ ***/
+
+static inline
+void silc_buffer_purge(SilcBuffer sb)
+{
+ silc_free(silc_buffer_steal(sb, NULL));
+}
+
/****f* silcutil/SilcBufferAPI/silc_buffer_set
*
* SYNOPSIS
* can be used to set the data to static buffer without needing any
* memory allocations. The `data' will not be copied to the buffer.
*
+ * EXAMPLE
+ *
+ * SilcBufferStruct buf;
+ * silc_buffer_set(&buf, data, data_len);
+ *
***/
static inline
{
sb->data = sb->head = data;
sb->tail = sb->end = data + data_len;
- sb->len = sb->truelen = data_len;
}
/****f* silcutil/SilcBufferAPI/silc_buffer_pull
*
* Pulls current data area towards end. The length of the currently
* valid data area is also decremented. Returns pointer to the data
- * area before pulling.
+ * area before pulling. Returns NULL on error.
*
* EXAMPLE
*
* | head | data | tail |
* ---------------------------------
* ^
+ *
+ * silc_buffer_pull(sb, 20);
+ *
***/
static inline
unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
{
unsigned char *old_data = sb->data;
-
#if defined(SILC_DEBUG)
- assert(len <= (SilcUInt32)(sb->tail - sb->data));
+ SILC_ASSERT(len <= silc_buffer_len(sb));
+#else
+ if (silc_unlikely(len > silc_buffer_len(sb)))
+ return NULL;
#endif
-
sb->data += len;
- sb->len -= len;
-
return old_data;
}
* DESCRIPTION
*
* Pushes current data area towards beginning. Length of the currently
- * valid data area is also incremented. Returns a pointer to the
- * data area before pushing.
+ * valid data area is also incremented. Returns a pointer to the
+ * data area before pushing. Returns NULL on error.
*
* EXAMPLE
*
* ---------------------------------
* ^
*
+ * silc_buffer_push(sb, 20);
+ *
***/
static inline
unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
{
unsigned char *old_data = sb->data;
-
#if defined(SILC_DEBUG)
- assert((sb->data - len) >= sb->head);
+ SILC_ASSERT((sb->data - len) >= sb->head);
+#else
+ if (silc_unlikely((sb->data - len) < sb->head))
+ return NULL;
#endif
-
sb->data -= len;
- sb->len += len;
-
return old_data;
}
*
* SYNOPSIS
*
- * static inline
+ * static inline
* unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
*
* DESCRIPTION
*
* Pulls current tail section towards end. Length of the current valid
- * data area is also incremented. Returns a pointer to the data area
- * before pulling.
+ * data area is also incremented. Returns a pointer to the data area
+ * before pulling. Returns NULL on error.
*
* EXAMPLE
*
* ---------------------------------
* ^
*
+ * silc_buffer_pull_tail(sb, 23);
+ *
***/
static inline
unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
{
unsigned char *old_tail = sb->tail;
-
#if defined(SILC_DEBUG)
- assert((SilcUInt32)(sb->end - sb->tail) >= len);
+ SILC_ASSERT(len <= silc_buffer_taillen(sb));
+#else
+ if (silc_unlikely(len > silc_buffer_taillen(sb)))
+ return NULL;
#endif
-
sb->tail += len;
- sb->len += len;
-
return old_tail;
}
*
* Pushes current tail section towards beginning. Length of the current
* valid data area is also decremented. Returns a pointer to the
- * tail section before pushing.
+ * tail section before pushing. Returns NULL on error.
*
* EXAMPLE
*
* ---------------------------------
* ^
*
+ * silc_buffer_push_tail(sb, 23);
+ *
***/
static inline
unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
{
unsigned char *old_tail = sb->tail;
-
#if defined(SILC_DEBUG)
- assert((sb->tail - len) >= sb->data);
+ SILC_ASSERT((sb->tail - len) >= sb->data);
+#else
+ if (silc_unlikely((sb->tail - len) < sb->data))
+ return NULL;
#endif
-
sb->tail -= len;
- sb->len -= len;
-
return old_tail;
}
* SYNOPSIS
*
* static inline
- * unsigned char *silc_buffer_put_head(SilcBuffer sb,
+ * unsigned char *silc_buffer_put_head(SilcBuffer sb,
* const unsigned char *data,
* SilcUInt32 len);
*
* DESCRIPTION
*
* Puts data at the head of the buffer. Returns pointer to the copied
- * data area.
+ * data area. Returns NULL on error.
*
* EXAMPLE
*
* | head | data | tail |
* ---------------------------------
* ^
- * Puts data to the head section.
+ * Puts data to the head section.
+ *
+ * silc_buffer_put_head(sb, data, data_len);
*
***/
SilcUInt32 len)
{
#if defined(SILC_DEBUG)
- assert((SilcUInt32)(sb->data - sb->head) >= len);
+ SILC_ASSERT(len <= silc_buffer_headlen(sb));
+#else
+ if (silc_unlikely(len > silc_buffer_headlen(sb)))
+ return NULL;
#endif
return (unsigned char *)memcpy(sb->head, data, len);
}
* DESCRIPTION
*
* Puts data at the start of the valid data area. Returns a pointer
- * to the copied data area.
+ * to the copied data area. Returns NULL on error.
*
* EXAMPLE
*
* ^
* Puts data to the data section.
*
+ * silc_buffer_put(sb, data, data_len);
+ *
***/
static inline
SilcUInt32 len)
{
#if defined(SILC_DEBUG)
- assert((SilcUInt32)(sb->tail - sb->data) >= len);
+ SILC_ASSERT(len <= silc_buffer_len(sb));
+#else
+ if (silc_unlikely(len > silc_buffer_len(sb)))
+ return NULL;
#endif
return (unsigned char *)memcpy(sb->data, data, len);
}
* DESCRIPTION
*
* Puts data at the tail of the buffer. Returns pointer to the copied
- * data area.
+ * data area. Returns NULL on error.
*
* EXAMPLE
*
* ^
* Puts data to the tail section.
*
+ * silc_buffer_put_tail(sb, data, data_len);
+ *
***/
static inline
SilcUInt32 len)
{
#if defined(SILC_DEBUG)
- assert((SilcUInt32)(sb->end - sb->tail) >= len);
+ SILC_ASSERT(len <= silc_buffer_taillen(sb));
+#else
+ if (silc_unlikely(len > silc_buffer_taillen(sb)))
+ return NULL;
#endif
return (unsigned char *)memcpy(sb->tail, data, len);
}
*
* Allocates `len' bytes size buffer and moves the tail area automatically
* `len' bytes so that the buffer is ready to use without calling the
- * silc_buffer_pull_tail.
+ * silc_buffer_pull_tail. Returns NULL on error.
*
***/
SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
{
SilcBuffer sb = silc_buffer_alloc(len);
- if (!sb)
+ if (silc_unlikely(!sb))
return NULL;
silc_buffer_pull_tail(sb, len);
return sb;
}
+/****f* silcutil/SilcBufferAPI/silc_buffer_reset
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * void silc_buffer_reset(SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Resets the buffer to the state as if it was just allocated by
+ * silc_buffer_alloc. This does not clear the data area. Use
+ * silc_buffer_clear if you also want to clear the data area.
+ *
+ ***/
+
+static inline
+void silc_buffer_reset(SilcBuffer sb)
+{
+ sb->data = sb->tail = sb->head;
+}
+
/****f* silcutil/SilcBufferAPI/silc_buffer_clear
*
* SYNOPSIS
static inline
void silc_buffer_clear(SilcBuffer sb)
{
- memset(sb->head, 0, sb->truelen);
+ memset(sb->head, 0, silc_buffer_truelen(sb));
+ silc_buffer_reset(sb);
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_start
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * void silc_buffer_start(SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Moves the data area at the start of the buffer. The tail area remains
+ * as is.
+ *
+ ***/
+
+static inline
+void silc_buffer_start(SilcBuffer sb)
+{
sb->data = sb->head;
- sb->tail = sb->head;
- sb->len = 0;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_end
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * void silc_buffer_end(SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Moves the end of the data area to the end of the buffer. The start
+ * of the data area remains same. If the start of data area is at the
+ * start of the buffer, after this function returns the buffer's data
+ * area length is the length of the entire buffer.
+ *
+ ***/
+
+static inline
+void silc_buffer_end(SilcBuffer sb)
+{
+ sb->tail = sb->end;
}
/****f* silcutil/SilcBufferAPI/silc_buffer_copy
*
* Generates copy of a SilcBuffer. This copies everything inside the
* currently valid data area, nothing more. Use silc_buffer_clone to
- * copy entire buffer.
+ * copy entire buffer. Returns NULL on error.
*
***/
{
SilcBuffer sb_new;
- sb_new = silc_buffer_alloc_size(sb->len);
- if (!sb_new)
+ sb_new = silc_buffer_alloc_size(silc_buffer_len(sb));
+ if (silc_unlikely(!sb_new))
return NULL;
- silc_buffer_put(sb_new, sb->data, sb->len);
+ silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
return sb_new;
}
*
* Clones SilcBuffer. This generates new SilcBuffer and copies
* everything from the source buffer. The result is exact clone of
- * the original buffer.
+ * the original buffer. Returns NULL on error.
*
***/
{
SilcBuffer sb_new;
- sb_new = silc_buffer_alloc_size(sb->truelen);
- if (!sb_new)
+ sb_new = silc_buffer_alloc_size(silc_buffer_truelen(sb));
+ if (silc_unlikely(!sb_new))
return NULL;
- silc_buffer_put(sb_new, sb->head, sb->truelen);
- sb_new->data = sb_new->head + (sb->data - sb->head);
- sb_new->tail = sb_new->data + sb->len;
- sb_new->len = sb->len;
+ silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
+ sb_new->data = sb_new->head + silc_buffer_headlen(sb);
+ sb_new->tail = sb_new->data + silc_buffer_len(sb);
return sb_new;
}
*
* DESCRIPTION
*
- * Reallocates buffer. Old data is saved into the new buffer. Returns
- * new SilcBuffer pointer. The buffer is exact clone of the old one
- * except that there is now more space at the end of buffer.
+ * Reallocates buffer. Old data is saved into the new buffer. The buffer
+ * is exact clone of the old one except that there is now more space
+ * at the end of buffer. This always returns the same `sb' unless `sb'
+ * was NULL. Returns NULL on error.
*
***/
static inline
SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
{
- SilcBuffer sb_new;
+ SilcUInt32 hlen, dlen;
+ unsigned char *h;
if (!sb)
return silc_buffer_alloc(newsize);
- if (newsize <= sb->truelen)
+ if (silc_unlikely(newsize <= silc_buffer_truelen(sb)))
+ return sb;
+
+ hlen = silc_buffer_headlen(sb);
+ dlen = silc_buffer_len(sb);
+ h = (unsigned char *)silc_realloc(sb->head, newsize);
+ if (silc_unlikely(!h))
+ return NULL;
+ sb->head = h;
+ sb->data = sb->head + hlen;
+ sb->tail = sb->data + dlen;
+ sb->end = sb->head + newsize;
+
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_realloc_size
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ * Same as silc_buffer_realloc but moves moves the tail area
+ * automatically so that the buffer is ready to use without calling the
+ * silc_buffer_pull_tail. Returns NULL on error.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize)
+{
+ sb = silc_buffer_realloc(sb, newsize);
+ if (silc_unlikely(!sb))
+ return NULL;
+ silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_enlarge
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ * Enlarges the buffer by the amount of `size' if it doesn't have that
+ * must space in the data area and in the tail area. Moves the tail
+ * area automatically after enlarging so that the current data area
+ * is at least the size of `size'. If there is more space than `size'
+ * in the data area this does not do anything. If there is enough
+ * space in the tail area this merely moves the tail area to reveal
+ * the extra space. Returns FALSE on error.
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size)
+{
+ if (size > silc_buffer_len(sb)) {
+ if (size > silc_buffer_taillen(sb) + silc_buffer_len(sb))
+ if (silc_unlikely(!silc_buffer_realloc(sb, silc_buffer_truelen(sb) +
+ (size - silc_buffer_taillen(sb) -
+ silc_buffer_len(sb)))))
+ return FALSE;
+ silc_buffer_pull_tail(sb, size - silc_buffer_len(sb));
+ }
+ return TRUE;
+}
+
+
+/* SilcStack aware SilcBuffer routines */
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_salloc
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len);
+ *
+ * DESCRIPTION
+ *
+ * Allocates new SilcBuffer and returns it.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len)
+{
+ SilcBuffer sb;
+
+ if (!stack)
+ return silc_buffer_alloc(len);
+
+ /* Allocate new SilcBuffer */
+ sb = (SilcBuffer)silc_scalloc(stack, 1, sizeof(*sb));
+ if (silc_unlikely(!sb))
+ return NULL;
+
+ /* Allocate the actual data area */
+ sb->head = (unsigned char *)silc_smalloc(stack, len);
+ if (silc_unlikely(!sb->head))
+ return NULL;
+
+ /* Set pointers to the new buffer */
+ sb->data = sb->head;
+ sb->tail = sb->head;
+ sb->end = sb->head + len;
+
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_salloc_size
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len);
+ *
+ * DESCRIPTION
+ *
+ * Allocates `len' bytes size buffer and moves the tail area automatically
+ * `len' bytes so that the buffer is ready to use without calling the
+ * silc_buffer_pull_tail.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len)
+{
+ SilcBuffer sb = silc_buffer_salloc(stack, len);
+ if (silc_unlikely(!sb))
+ return NULL;
+ silc_buffer_pull_tail(sb, len);
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_srealloc(SilcStack stack,
+ * SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ * Reallocates buffer. Old data is saved into the new buffer. The buffer
+ * is exact clone of the old one except that there is now more space
+ * at the end of buffer.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_srealloc(SilcStack stack,
+ SilcBuffer sb, SilcUInt32 newsize)
+{
+ SilcUInt32 hlen, dlen;
+ unsigned char *h;
+
+ if (!stack)
+ return silc_buffer_realloc(sb, newsize);
+
+ if (!sb)
+ return silc_buffer_salloc(stack, newsize);
+
+ if (newsize <= silc_buffer_truelen(sb))
return sb;
- sb_new = silc_buffer_alloc_size(newsize);
- if (!sb_new)
+ hlen = silc_buffer_headlen(sb);
+ dlen = silc_buffer_len(sb);
+ h = (unsigned char *)silc_srealloc(stack, silc_buffer_truelen(sb),
+ sb->head, newsize);
+ if (!h) {
+ /* Do slow and stack wasting realloc. The old sb->head is lost and
+ is freed eventually. */
+ h = (unsigned char *)silc_smalloc(stack, newsize);
+ if (silc_unlikely(!h))
+ return NULL;
+ memcpy(h, sb->head, silc_buffer_truelen(sb));
+ }
+
+ sb->head = h;
+ sb->data = sb->head + hlen;
+ sb->tail = sb->data + dlen;
+ sb->end = sb->head + newsize;
+
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+ * SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ * Same as silc_buffer_srealloc but moves moves the tail area
+ * automatically so that the buffer is ready to use without calling the
+ * silc_buffer_pull_tail.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+ SilcBuffer sb, SilcUInt32 newsize)
+{
+ sb = silc_buffer_srealloc(stack, sb, newsize);
+ if (silc_unlikely(!sb))
+ return NULL;
+ silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
+ return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_senlarge
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_senlarge(SilcStack stack, SilcBuffer sb,
+ * SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ * Enlarges the buffer by the amount of `size' if it doesn't have that
+ * must space in the data area and in the tail area. Moves the tail
+ * area automatically after enlarging so that the current data area
+ * is at least the size of `size'. If there is more space than `size'
+ * in the data area this does not do anything. If there is enough
+ * space in the tail area this merely moves the tail area to reveal
+ * the extra space. Returns FALSE on error.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_senlarge(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
+{
+ if (size > silc_buffer_len(sb)) {
+ if (size > silc_buffer_taillen(sb) + silc_buffer_len(sb))
+ if (silc_unlikely(!silc_buffer_srealloc(stack, sb,
+ silc_buffer_truelen(sb) +
+ (size - silc_buffer_taillen(sb) -
+ silc_buffer_len(sb)))))
+ return FALSE;
+ silc_buffer_pull_tail(sb, size - silc_buffer_len(sb));
+ }
+ return TRUE;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_scopy
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Generates copy of a SilcBuffer. This copies everything inside the
+ * currently valid data area, nothing more. Use silc_buffer_clone to
+ * copy entire buffer.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb)
+{
+ SilcBuffer sb_new;
+
+ sb_new = silc_buffer_salloc_size(stack, silc_buffer_len(sb));
+ if (silc_unlikely(!sb_new))
return NULL;
- silc_buffer_put(sb_new, sb->head, sb->truelen);
- sb_new->data = sb_new->head + (sb->data - sb->head);
- sb_new->tail = sb_new->data + sb->len;
- sb_new->len = sb->len;
+ silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
- silc_buffer_free(sb);
+ return sb_new;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_sclone
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ * Clones SilcBuffer. This generates new SilcBuffer and copies
+ * everything from the source buffer. The result is exact clone of
+ * the original buffer.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb)
+{
+ SilcBuffer sb_new;
+
+ sb_new = silc_buffer_salloc_size(stack, silc_buffer_truelen(sb));
+ if (silc_unlikely(!sb_new))
+ return NULL;
+ silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
+ sb_new->data = sb_new->head + silc_buffer_headlen(sb);
+ sb_new->tail = sb_new->data + silc_buffer_len(sb);
return sb_new;
}
-#endif
+#endif /* SILCBUFFER_H */