Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1998 - 2007 Pekka Riikonen
+ Copyright (C) 1998 - 2008 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
GNU General Public License for more details.
*/
-/* $Id$ */
-/****h* silcutil/SILC Buffer Interface
+/****h* silcutil/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
+ * Data buffer interface that provides buffer allocation and manipulation
+ * routines. SilcBuffer is 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.
+ * the allocated data area. See the SilcBuffer context for more information.
+ *
+ * The SilcBuffer context is not thread-safe and if same context must be
+ * used from multiple threads concurrency control must be employed.
*
***/
#ifndef SILCBUFFER_H
#define SILCBUFFER_H
-/****s* silcutil/SilcBufferAPI/SilcBuffer
+/****s* silcutil/SilcBuffer
*
* NAME
*
* SILC Buffer object. Following short description of the fields
* of the buffer.
*
- * EXAMPLE
- *
- * unsiged char *head;
+ * unsiged char *head;
*
* Head of the allocated buffer. This is the start of the allocated
* data area and remains as same throughout the lifetime of the buffer.
* However, the end of the head area or the start of the currently valid
- * data area is variable.
+ * data area is variable. Reallocating the buffer may change the
+ * pointer.
*
* --------------------------------
* | head | data | tail |
*
* Current head section in the buffer is sb->data - sb->head.
*
- * unsigned char *data;
+ * unsigned char *data;
*
* Currently valid data area. This is the start of the currently valid
* main data area. The data area is variable in all directions.
*
* Current valid data area in the buffer is sb->tail - sb->data.
*
- * unsigned char *tail;
+ * unsigned char *tail;
*
* Tail of the buffer. This is the end of the currently valid data area
* or start of the tail area. The start of the tail area is variable.
*
* Current tail section in the buffer is sb->end - sb->tail.
*
- * unsigned char *end;
+ * unsigned char *end;
*
* End of the allocated buffer. This is the end of the allocated data
* area and remains as same throughout the lifetime of the buffer.
*
* Length of the entire buffer is (ie. truelen) sb->end - sb->head.
*
- * Currently valid data area is considered to be the main data area in
- * the buffer. However, the entire buffer is of course valid data and can
- * be used as such. Usually head section of the buffer includes different
- * kind of headers or similar. Data section includes the main data of
- * the buffer. Tail section can be seen as a reserve space of the data
- * 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.
+ * Currently valid data area is considered to be the main data area in
+ * the buffer. However, the entire buffer is of course valid data and can
+ * be used as such. Usually head section of the buffer includes different
+ * kind of headers or similar. Data section includes the main data of
+ * the buffer. Tail section can be seen as a reserve space of the data
+ * section. Tail section can be pulled towards end, and thus the data
+ * section becomes larger.
*
* SOURCE
*/
typedef struct SilcBufferObject {
- unsigned char *head;
- unsigned char *data;
- unsigned char *tail;
- unsigned char *end;
+ unsigned char *head; /* Head of the allocated buffer area */
+ unsigned char *data; /* Start of the data area */
+ unsigned char *tail; /* Start of the tail area */
+ unsigned char *end; /* End of the buffer */
} *SilcBuffer, SilcBufferStruct;
/***/
/* Macros */
-/****f* silcutil/SilcBufferAPI/silc_buffer_data
+/****f* silcutil/silc_buffer_data
*
* NAME
*
#define silc_buffer_data(x) (x)->data
/***/
-/****f* silcutil/SilcBufferAPI/silc_buffer_datalen
+/****f* silcutil/silc_buffer_tail
+ *
+ * NAME
+ *
+ * unsigned char *silc_buffer_tail(SilcBuffer sb)
+ *
+ * DESCRIPTION
+ *
+ * Returns pointer to the tail area of the buffer.
+ *
+ * SOURCE
+ */
+#define silc_buffer_tail(x) (x)->tail
+/***/
+
+/****f* silcutil/silc_buffer_datalen
*
* NAME
*
/* Inline functions */
-/****d* silcutil/SilcBufferAPI/silc_buffer_truelen
+/****d* silcutil/silc_buffer_truelen
*
* NAME
*
return (SilcUInt32)(x->end - x->head);
}
-/****d* silcutil/SilcBufferAPI/silc_buffer_len
+/****d* silcutil/silc_buffer_len
*
* NAME
*
return (SilcUInt32)(x->tail - x->data);
}
-/****d* silcutil/SilcBufferAPI/silc_buffer_headlen
+/****d* silcutil/silc_buffer_headlen
*
* NAME
*
return (SilcUInt32)(x->data - x->head);
}
-/****d* silcutil/SilcBufferAPI/silc_buffer_taillen
+/****d* silcutil/silc_buffer_taillen
*
* NAME
*
return (SilcUInt32)(x->end - x->tail);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_alloc
+/****f* silcutil/silc_buffer_alloc
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_salloc
+/****f* silcutil/silc_buffer_salloc
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_free
+/****f* silcutil/silc_buffer_free
*
* SYNOPSIS
*
}
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_sfree
+/****f* silcutil/silc_buffer_sfree
*
* SYNOPSIS
*
silc_buffer_free(sb);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_steal
+/****f* silcutil/silc_buffer_steal
*
* SYNOPSIS
*
return buf;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_purge
+/****f* silcutil/silc_buffer_purge
*
* SYNOPSIS
*
silc_free(silc_buffer_steal(sb, NULL));
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_spurge
+/****f* silcutil/silc_buffer_spurge
*
* SYNOPSIS
*
silc_buffer_purge(sb);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_set
+/****f* silcutil/silc_buffer_set
*
* SYNOPSIS
*
sb->tail = sb->end = data + data_len;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_pull
+/****f* silcutil/silc_buffer_pull
*
* SYNOPSIS
*
return old_data;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_push
+/****f* silcutil/silc_buffer_push
*
* SYNOPSIS
*
* 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. Returns NULL if the push would lead to
- * buffer underflow or would go under the valid data area.
+ * go beyond the buffer boundaries or current data area.
*
* EXAMPLE
*
SILC_ASSERT((sb->data - len) >= sb->head);
#endif /* SILC_DIST_INPLACE */
if (silc_unlikely((sb->data - len) < sb->head)) {
- silc_set_errno(SILC_ERR_UNDERFLOW);
+ silc_set_errno(SILC_ERR_OVERFLOW);
return NULL;
}
return old_data;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_pull_tail
+/****f* silcutil/silc_buffer_pull_tail
*
* SYNOPSIS
*
return old_tail;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_push_tail
+/****f* silcutil/silc_buffer_push_tail
*
* SYNOPSIS
*
* 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. Returns NULL if the push would lead to
- * buffer underflow or go under valid tail area.
+ * go beyond buffer boundaries or current tail area.
*
* EXAMPLE
*
SILC_ASSERT((sb->tail - len) >= sb->data);
#endif /* SILC_DIST_INPLACE */
if (silc_unlikely((sb->tail - len) < sb->data)) {
- silc_set_errno(SILC_ERR_UNDERFLOW);
+ silc_set_errno(SILC_ERR_OVERFLOW);
return NULL;
}
return old_tail;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_put_head
+/****f* silcutil/silc_buffer_put_head
*
* SYNOPSIS
*
return NULL;
}
+ if (sb->head > data) {
+ if (sb->head - data <= len)
+ return (unsigned char *)memmove(sb->head, data, len);
+ } else {
+ if (data - sb->head <= len)
+ return (unsigned char *)memmove(sb->head, data, len);
+ }
+
return (unsigned char *)memcpy(sb->head, data, len);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_put
+/****f* silcutil/silc_buffer_put
*
* SYNOPSIS
*
return NULL;
}
+ if (sb->data > data) {
+ if (sb->data - data <= len)
+ return (unsigned char *)memmove(sb->data, data, len);
+ } else {
+ if (data - sb->data <= len)
+ return (unsigned char *)memmove(sb->data, data, len);
+ }
+
return (unsigned char *)memcpy(sb->data, data, len);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_put_tail
+/****f* silcutil/silc_buffer_put_tail
*
* SYNOPSIS
*
return NULL;
}
+ if (sb->tail > data) {
+ if (sb->tail - data <= len)
+ return (unsigned char *)memmove(sb->tail, data, len);
+ } else {
+ if (data - sb->tail <= len)
+ return (unsigned char *)memmove(sb->tail, data, len);
+ }
+
return (unsigned char *)memcpy(sb->tail, data, len);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_alloc_size
+/****f* silcutil/silc_buffer_alloc_size
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_salloc_size
+/****f* silcutil/silc_buffer_salloc_size
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_reset
+/****f* silcutil/silc_buffer_reset
*
* SYNOPSIS
*
sb->data = sb->tail = sb->head;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_clear
+/****f* silcutil/silc_buffer_clear
*
* SYNOPSIS
*
silc_buffer_reset(sb);
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_start
+/****f* silcutil/silc_buffer_start
*
* SYNOPSIS
*
sb->data = sb->head;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_end
+/****f* silcutil/silc_buffer_end
*
* SYNOPSIS
*
sb->tail = sb->end;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_copy
+/****f* silcutil/silc_buffer_copy
*
* SYNOPSIS
*
return sb_new;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_scopy
+/****f* silcutil/silc_buffer_scopy
*
* SYNOPSIS
*
return sb_new;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_clone
+/****f* silcutil/silc_buffer_clone
*
* SYNOPSIS
*
return sb_new;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_sclone
+/****f* silcutil/silc_buffer_sclone
*
* SYNOPSIS
*
return sb_new;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_realloc
+/****f* silcutil/silc_buffer_realloc
*
* SYNOPSIS
*
* 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
+ * is exact clone of the old one except that there is now more/less space
* at the end of buffer. This always returns the same `sb' unless `sb'
* was NULL. Returns NULL if system is out of memory.
*
+ * If the `newsize' is shorter than the current buffer size, the data
+ * and tail area of the buffer must be set to correct position before
+ * calling this function so that buffer overflow would not occur when
+ * the buffer size is reduced.
+ *
***/
static inline
if (!sb)
return silc_buffer_alloc(newsize);
- if (silc_unlikely(newsize <= silc_buffer_truelen(sb)))
+ if (silc_unlikely(newsize == silc_buffer_truelen(sb)))
return sb;
hlen = silc_buffer_headlen(sb);
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc
+/****f* silcutil/silc_buffer_srealloc
*
* SYNOPSIS
*
* 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. Returns NULL if system is out of memory.
+ * is exact clone of the old one except that there is now more/less space
+ * at the end of buffer. Returns NULL if system is out of memory. This
+ * always returns `sb' unless `sb' was NULL.
+ *
+ * If the `newsize' is shorter than the current buffer size, the data
+ * and tail area of the buffer must be set to correct position before
+ * calling this function so that buffer overflow would not occur when
+ * the buffer size is reduced.
*
* This routine use SilcStack are memory source. If `stack' is NULL
* reverts back to normal allocating routine.
if (!sb)
return silc_buffer_salloc(stack, newsize);
- if (newsize <= silc_buffer_truelen(sb))
+ if (newsize == silc_buffer_truelen(sb))
return sb;
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));
- }
+ if (!h)
+ return NULL;
sb->head = h;
sb->data = sb->head + hlen;
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_realloc_size
+/****f* silcutil/silc_buffer_realloc_size
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
+/****f* silcutil/silc_buffer_srealloc_size
*
* SYNOPSIS
*
return sb;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_enlarge
+/****f* silcutil/silc_buffer_enlarge
*
* SYNOPSIS
*
return TRUE;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_senlarge
+/****f* silcutil/silc_buffer_senlarge
*
* SYNOPSIS
*
return TRUE;
}
-/****f* silcutil/SilcBufferAPI/silc_buffer_strchr
+/****f* silcutil/silc_buffer_append
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBuffer silc_buffer_append(SilcBuffer sb, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ * Appends the current data area by the amount of `size'. The tail area
+ * of the buffer remains intact and contains the same data than the old
+ * tail area (the data is copied to the new tail area). After appending
+ * there is now `size' bytes more free area in the data area. Returns
+ * FALSE if system is out of memory.
+ *
+ * EXAMPLE
+ *
+ * Before appending:
+ * ---------------------------------
+ * | head | data | tail |
+ * ---------------------------------
+ *
+ * After appending:
+ * ------------------------------------
+ * | head | data | tail |
+ * -------------------------------------
+ *
+ * silc_buffer_append(sb, 5);
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_append(SilcBuffer sb, SilcUInt32 size)
+{
+ if (silc_unlikely(!silc_buffer_realloc(sb, silc_buffer_truelen(sb) + size)))
+ return FALSE;
+
+ /* Enlarge data area */
+ silc_buffer_pull_tail(sb, size);
+
+ /* Copy old tail area to new tail area */
+ silc_buffer_put_tail(sb, sb->tail - size, silc_buffer_taillen(sb));
+
+ return TRUE;
+}
+
+/****f* silcutil/silc_buffer_sappend
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBool silc_buffer_sappend(SilcStack stack, SilcBuffer sb,
+ * SilcUInt32 size)
+ *
+ * DESCRIPTION
+ *
+ * Appends the current data area by the amount of `size'. The tail area
+ * of the buffer remains intact and contains the same data than the old
+ * tail area (the data is copied to the new tail area). After appending
+ * there is now `size' bytes more free area in the data area. Returns
+ * FALSE if system is out of memory.
+ *
+ * This routine use SilcStack are memory source. If `stack' is NULL
+ * reverts back to normal allocating routine.
+ *
+ * Note that this call consumes the `stack'. The caller should push the
+ * stack before calling the function and pop it later.
+ *
+ * EXAMPLE
+ *
+ * Before appending:
+ * ---------------------------------
+ * | head | data | tail |
+ * ---------------------------------
+ *
+ * After appending:
+ * ------------------------------------
+ * | head | data | tail |
+ * -------------------------------------
+ *
+ * silc_buffer_append(sb, 5);
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_sappend(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
+{
+ if (silc_unlikely(!silc_buffer_srealloc(stack, sb,
+ silc_buffer_truelen(sb) + size)))
+ return FALSE;
+
+ /* Enlarge data area */
+ silc_buffer_pull_tail(sb, size);
+
+ /* Copy old tail area to new tail area */
+ silc_buffer_put_tail(sb, sb->tail - size, silc_buffer_taillen(sb));
+
+ return TRUE;
+}
+
+/****f* silcutil/silc_buffer_strchr
*
* SYNOPSIS
*
return NULL;
}
+/****f* silcutil/silc_buffer_equal
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBool silc_buffer_equal(SilcBuffer sb1, SilcBuffer sb2)
+ *
+ * DESCRIPTION
+ *
+ * Compares if the data area of the buffer `sb1' and `sb2' are identical.
+ * Returns TRUE if they match and FALSE if they differ.
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_equal(SilcBuffer sb1, SilcBuffer sb2)
+{
+ if (silc_buffer_len(sb1) != silc_buffer_len(sb2))
+ return FALSE;
+ return memcmp(sb1->data, sb2->data, silc_buffer_len(sb1)) == 0;
+}
+
+/****f* silcutil/silc_buffer_memcmp
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * SilcBool silc_buffer_memcmp(SilcBuffer buffer,
+ * const unsigned char *data,
+ * SilcUInt32 data_len)
+ *
+ * DESCRIPTION
+ *
+ * Compares the data area of the buffer with the `data'. Returns TRUE
+ * if the data area is identical to `data' or FALSE if they differ.
+ *
+ ***/
+
+static inline
+SilcBool silc_buffer_memcmp(SilcBuffer buffer, const unsigned char *data,
+ SilcUInt32 data_len)
+{
+ if (silc_buffer_len(buffer) != data_len)
+ return FALSE;
+ return memcmp(buffer->data, data, data_len) == 0;
+}
+
+/****f* silcutil/silc_buffer_printf
+ *
+ * SYNOPSIS
+ *
+ * static inline
+ * void silc_buffer_printf(SilcBuffer sb, SilcBool newline);
+ *
+ * DESCRIPTION
+ *
+ * Prints the current data area of `sb' into stdout. If `newline' is
+ * TRUE prints '\n' after the data in the buffer.
+ *
+ ***/
+
+static inline
+void silc_buffer_printf(SilcBuffer sb, SilcBool newline)
+{
+ silc_file_write(1, (const char *)silc_buffer_data(sb),
+ silc_buffer_len(sb));
+ if (newline)
+ printf("\n");
+ fflush(stdout);
+}
+
#endif /* SILCBUFFER_H */