Added SILC_STR_STRING and SILC_STR_STRING_APPEND and support for
[crypto.git] / lib / silcutil / silcbuffer.h
index 9d35047082e03741eb6d0816f8a960f650b7c49f..3a3db7b679ffc7283060054813544fc80e2b69a7 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1998 - 2005 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
  *     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 {
+typedef struct SilcBufferObject {
   unsigned char *head;
   unsigned char *data;
   unsigned char *tail;
@@ -117,67 +120,114 @@ typedef struct {
 
 /* Macros */
 
-/****d* silcutil/SilcBufferAPI/silc_buffer_truelen
+/****f* silcutil/SilcBufferAPI/silc_buffer_data
  *
  * NAME
  *
- *    #define silc_buffer_truelen(buffer)
+ *    unsigned char *silc_buffer_data(SilcBuffer sb)
  *
  * DESCRIPTION
  *
- *    Returns the true length of the buffer.
+ *    Returns pointer to the data area of the buffer.
  *
  * SOURCE
  */
-#define silc_buffer_truelen(x) (SilcUInt32)((x)->end - (x)->head)
+#define silc_buffer_data(x) (x)->data
 /***/
 
-/****d* silcutil/SilcBufferAPI/silc_buffer_len
+/****f* silcutil/SilcBufferAPI/silc_buffer_datalen
  *
  * NAME
  *
- *    #define silc_buffer_len(buffer)
+ *    #define silc_buffer_datalen ...
  *
  * DESCRIPTION
  *
- *    Returns the current length of the data area of the buffer.
+ *    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_len(x) (SilcUInt32)((x)->tail - (x)->data)
+#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
  *
- *    #define silc_buffer_headlen(buffer)
+ *    SilcUInt32 silc_buffer_headlen(SilcBuffer sb)
  *
  * DESCRIPTION
  *
  *    Returns the current length of the head data area of the buffer.
  *
- * SOURCE
- */
-#define silc_buffer_headlen(x) (SilcUInt32)((x)->data - (x)->head)
-/***/
+ ***/
+static inline
+SilcUInt32 silc_buffer_headlen(SilcBuffer x)
+{
+  return (SilcUInt32)(x->data - x->head);
+}
 
 /****d* silcutil/SilcBufferAPI/silc_buffer_taillen
  *
  * NAME
  *
- *    #define silc_buffer_taillen(buffer)
+ *    SilcUInt32 silc_buffer_taillen(SilcBuffer sb)
  *
  * DESCRIPTION
  *
  *    Returns the current length of the tail data area of the buffer.
  *
- * SOURCE
- */
-#define silc_buffer_taillen(x) (SilcUInt32)((x)->end - (x)->tail)
-/***/
-
-/* Inline functions */
+ ***/
+static inline
+SilcUInt32 silc_buffer_taillen(SilcBuffer x)
+{
+  return (SilcUInt32)(x->end - x->tail);
+}
 
 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc
  *
@@ -188,7 +238,8 @@ typedef struct {
  *
  * DESCRIPTION
  *
- *    Allocates new SilcBuffer and returns it.  Returns NULL on error.
+ *    Allocates new SilcBuffer and returns it.  Returns NULL if system is
+ *    out of memory.
  *
  ***/
 
@@ -199,18 +250,68 @@ 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)
+  if (silc_likely(len)) {
+    /* Allocate the actual data area */
+    sb->head = (unsigned char *)silc_malloc(len * sizeof(*sb->head));
+    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
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new SilcBuffer and returns it.  Returns NULL 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.
+ *
+ ***/
+
+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;
 
-  /* Set pointers to the new buffer */
-  sb->data = sb->head;
-  sb->tail = sb->head;
-  sb->end = sb->head + len;
+  if (silc_likely(len)) {
+    /* Allocate the actual data area */
+    sb->head = (unsigned char *)silc_smalloc(stack, len * sizeof(*sb->head));
+    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;
 }
@@ -224,7 +325,13 @@ SilcBuffer silc_buffer_alloc(SilcUInt32 len)
  *
  * 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.
+ *    Call silc_buffer_sfree instead.
  *
  ***/
 
@@ -241,6 +348,37 @@ void silc_buffer_free(SilcBuffer sb)
   }
 }
 
+/****f* silcutil/SilcBufferAPI/silc_buffer_sfree
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    void silc_buffer_free(SilcStack stack, SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ *    Frees SilcBuffer.  If `stack' is NULL this calls silc_buffer_free.  Can
+ *    be called safely `sb' as NULL.
+ *
+ ***/
+
+static inline
+void silc_buffer_sfree(SilcStack stack, SilcBuffer sb)
+{
+  if (stack) {
+#ifdef SILC_DEBUG
+    if (sb) {
+      if (sb->head)
+       memset(sb->head, 'F', silc_buffer_truelen(sb));
+      memset(sb, 'F', sizeof(*sb));
+    }
+#endif /* SILC_DEBUG */
+    return;
+  }
+
+  silc_buffer_free(sb);
+}
+
 /****f* silcutil/SilcBufferAPI/silc_buffer_steal
  *
  * SYNOPSIS
@@ -269,6 +407,63 @@ unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len)
   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.
+ *    Use silc_buffer_spurge instead.
+ *
+ ***/
+
+static inline
+void silc_buffer_purge(SilcBuffer sb)
+{
+  silc_free(silc_buffer_steal(sb, NULL));
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_spurge
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    void silc_buffer_spurge(SilcStack stack, 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.  If `stack' is NULL
+ *    this calls silc_buffer_purge.
+ *
+ ***/
+
+static inline
+void silc_buffer_spurge(SilcStack stack, SilcBuffer sb)
+{
+  if (stack) {
+#ifdef SILC_DEBUG
+    if (sb && sb->head)
+      memset(silc_buffer_steal(sb, NULL), 'F', silc_buffer_truelen(sb));
+#endif /* SILC_DEBUG */
+    return;
+  }
+
+  silc_buffer_purge(sb);
+}
+
 /****f* silcutil/SilcBufferAPI/silc_buffer_set
  *
  * SYNOPSIS
@@ -310,7 +505,8 @@ void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
  *
  *    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. Returns NULL on error.
+ *    area before pulling. Returns NULL if the pull would lead to buffer
+ *    overflow or would go beyond the valid data area.
  *
  * EXAMPLE
  *
@@ -333,12 +529,15 @@ static inline
 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_data = sb->data;
-#if defined(SILC_DEBUG)
-  assert(len <= silc_buffer_len(sb));
-#else
-  if (len > silc_buffer_len(sb))
+
+#ifdef SILC_DIST_INPLACE
+  SILC_ASSERT(len <= silc_buffer_len(sb));
+#endif /* SILC_DIST_INPLACE */
+  if (silc_unlikely(len > silc_buffer_len(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
-#endif
+  }
+
   sb->data += len;
   return old_data;
 }
@@ -354,7 +553,8 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
  *
  *    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 on error.
+ *    data area before pushing. Returns NULL if the push would lead to
+ *    buffer underflow or would go under the valid data area.
  *
  * EXAMPLE
  *
@@ -377,12 +577,15 @@ 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);
-#else
-  if ((sb->data - len) < sb->head)
+
+#ifdef SILC_DIST_INPLACE
+  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);
     return NULL;
-#endif
+  }
+
   sb->data -= len;
   return old_data;
 }
@@ -398,7 +601,7 @@ unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
  *
  *    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. Returns NULL on error.
+ *    before pulling. Returns NULL if the pull would lead to buffer overflow.
  *
  * EXAMPLE
  *
@@ -421,12 +624,15 @@ static inline
 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_tail = sb->tail;
-#if defined(SILC_DEBUG)
-  assert(len <= silc_buffer_taillen(sb));
-#else
-  if (len > silc_buffer_taillen(sb))
+
+#ifdef SILC_DIST_INPLACE
+  SILC_ASSERT(len <= silc_buffer_taillen(sb));
+#endif /* SILC_DIST_INPLACE */
+  if (silc_unlikely(len > silc_buffer_taillen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
-#endif
+  }
+
   sb->tail += len;
   return old_tail;
 }
@@ -442,7 +648,8 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
  *
  *    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 on error.
+ *    tail section before pushing. Returns NULL if the push would lead to
+ *    buffer underflow or go under valid tail area.
  *
  * EXAMPLE
  *
@@ -465,12 +672,15 @@ 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);
-#else
-  if ((sb->tail - len) < sb->data)
+
+#ifdef SILC_DIST_INPLACE
+  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);
     return NULL;
-#endif
+  }
+
   sb->tail -= len;
   return old_tail;
 }
@@ -487,7 +697,8 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
  * DESCRIPTION
  *
  *    Puts data at the head of the buffer. Returns pointer to the copied
- *    data area. Returns NULL on error.
+ *    data area. Returns NULL if the data is longer that the current head
+ *    area.
  *
  * EXAMPLE
  *
@@ -506,12 +717,22 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
                                    const unsigned char *data,
                                    SilcUInt32 len)
 {
-#if defined(SILC_DEBUG)
-  assert(len <= silc_buffer_headlen(sb));
-#else
-  if (len > silc_buffer_headlen(sb))
+#ifdef SILC_DIST_INPLACE
+  SILC_ASSERT(len <= silc_buffer_headlen(sb));
+#endif /* SILC_DIST_INPLACE */
+  if (silc_unlikely(len > silc_buffer_headlen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
-#endif
+  }
+
+  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);
 }
 
@@ -527,7 +748,8 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
  * DESCRIPTION
  *
  *    Puts data at the start of the valid data area. Returns a pointer
- *    to the copied data area.  Returns NULL on error.
+ *    to the copied data area.  Returns NULL if the data is longer than the
+ *    current data area.
  *
  * EXAMPLE
  *
@@ -546,12 +768,22 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
                               const unsigned char *data,
                               SilcUInt32 len)
 {
-#if defined(SILC_DEBUG)
-  assert(len <= silc_buffer_len(sb));
-#else
-  if (len > silc_buffer_len(sb))
+#ifdef SILC_DIST_INPLACE
+  SILC_ASSERT(len <= silc_buffer_len(sb));
+#endif /* SILC_DIST_INPLACE */
+  if (silc_unlikely(len > silc_buffer_len(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
-#endif
+  }
+
+  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);
 }
 
@@ -567,7 +799,8 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
  * DESCRIPTION
  *
  *    Puts data at the tail of the buffer. Returns pointer to the copied
- *    data area.  Returns NULL on error.
+ *    data area.  Returns NULL if the data is longer than the current tail
+ *    area.
  *
  * EXAMPLE
  *
@@ -586,12 +819,22 @@ unsigned char *silc_buffer_put_tail(SilcBuffer sb,
                                    const unsigned char *data,
                                    SilcUInt32 len)
 {
-#if defined(SILC_DEBUG)
-  assert(len <= silc_buffer_taillen(sb));
-#else
-  if (len > silc_buffer_taillen(sb))
+#ifdef SILC_DIST_INPLACE
+  SILC_ASSERT(len <= silc_buffer_taillen(sb));
+#endif /* SILC_DIST_INPLACE */
+  if (silc_unlikely(len > silc_buffer_taillen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
-#endif
+  }
+
+  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);
 }
 
@@ -606,7 +849,7 @@ unsigned char *silc_buffer_put_tail(SilcBuffer sb,
  *
  *    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.  Returns NULL on error.
+ *    silc_buffer_pull_tail.  Returns NULL if system is out of memory.
  *
  ***/
 
@@ -614,7 +857,38 @@ static inline
 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_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.  Returns NULL 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.
+ *
+ ***/
+
+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;
@@ -662,6 +936,48 @@ void silc_buffer_clear(SilcBuffer 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;
+}
+
+/****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
  *
  * SYNOPSIS
@@ -673,7 +989,7 @@ void silc_buffer_clear(SilcBuffer sb)
  *
  *    Generates copy of a SilcBuffer. This copies everything inside the
  *    currently valid data area, nothing more. Use silc_buffer_clone to
- *    copy entire buffer.  Returns NULL on error.
+ *    copy entire buffer.  Returns NULL if system is out of memory.
  *
  ***/
 
@@ -683,7 +999,41 @@ SilcBuffer silc_buffer_copy(SilcBuffer sb)
   SilcBuffer sb_new;
 
   sb_new = silc_buffer_alloc_size(silc_buffer_len(sb));
-  if (!sb_new)
+  if (silc_unlikely(!sb_new))
+    return NULL;
+  silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
+
+  return sb_new;
+}
+
+/****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.  Returns NULL 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.
+ *
+ ***/
+
+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->data, silc_buffer_len(sb));
 
@@ -701,7 +1051,7 @@ SilcBuffer silc_buffer_copy(SilcBuffer sb)
  *
  *    Clones SilcBuffer. This generates new SilcBuffer and copies
  *    everything from the source buffer. The result is exact clone of
- *    the original buffer.  Returns NULL on error.
+ *    the original buffer.  Returns NULL if system is out of memory.
  *
  ***/
 
@@ -711,7 +1061,43 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
   SilcBuffer sb_new;
 
   sb_new = silc_buffer_alloc_size(silc_buffer_truelen(sb));
-  if (!sb_new)
+  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;
+}
+
+/****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.  Returns NULL 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.
+ *
+ ***/
+
+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);
@@ -732,7 +1118,7 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
  *    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.
+ *    was NULL. Returns NULL if system is out of memory.
  *
  ***/
 
@@ -745,14 +1131,67 @@ SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
   if (!sb)
     return silc_buffer_alloc(newsize);
 
-  if (newsize <= silc_buffer_truelen(sb))
+  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_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.  Returns NULL 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.
+ *
+ ***/
+
+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;
+
+  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)
     return NULL;
+
   sb->head = h;
   sb->data = sb->head + hlen;
   sb->tail = sb->data + dlen;
@@ -772,7 +1211,7 @@ SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
  *
  *    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.
+ *    silc_buffer_pull_tail.  Returns NULL if system is out of memory.
  *
  ***/
 
@@ -780,221 +1219,315 @@ static inline
 SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize)
 {
   sb = silc_buffer_realloc(sb, newsize);
-  if (!sb)
+  if (silc_unlikely(!sb))
     return NULL;
   silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
   return sb;
 }
 
-
-/* SilcStack aware SilcBuffer routines */
-
-/****f* silcutil/SilcBufferAPI/silc_buffer_salloc
+/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len);
+ *    SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+ *                                         SilcBuffer sb, SilcUInt32 newsize);
  *
  * DESCRIPTION
  *
- *    Allocates new SilcBuffer and returns it.
+ *    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.  Returns NULL if system is out of memory.
+ *
+ *    This routine use SilcStack are memory source.  If `stack' is NULL
+ *    reverts back to normal allocating routine.
  *
- *    This routine use SilcStack are memory source.
+ *    Note that this call consumes the `stack'.  The caller should push the
+ *    stack before calling the function and pop it later.
  *
  ***/
 
 static inline
-SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len)
+SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+                                    SilcBuffer sb, SilcUInt32 newsize)
 {
-  SilcBuffer sb;
-
-  /* Allocate new SilcBuffer */
-  sb = (SilcBuffer)silc_scalloc(stack, 1, sizeof(*sb));
-  if (!sb)
-    return NULL;
-
-  /* Allocate the actual data area */
-  sb->head = (unsigned char *)silc_smalloc_ua(stack, len);
-  if (!sb->head)
+  sb = silc_buffer_srealloc(stack, sb, newsize);
+  if (silc_unlikely(!sb))
     return NULL;
-
-  /* Set pointers to the new buffer */
-  sb->data = sb->head;
-  sb->tail = sb->head;
-  sb->end = sb->head + len;
-
+  silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
   return sb;
 }
 
-/****f* silcutil/SilcBufferAPI/silc_buffer_salloc_size
+/****f* silcutil/SilcBufferAPI/silc_buffer_enlarge
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len);
+ *    SilcBuffer silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size);
  *
  * 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.
+ *    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 if system is out of memory.
  *
  ***/
 
 static inline
-SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len)
+SilcBool silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size)
 {
-  SilcBuffer sb = silc_buffer_salloc(stack, len);
-  if (!sb)
-    return NULL;
-  silc_buffer_pull_tail(sb, len);
-  return sb;
+  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;
 }
 
-/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc
+/****f* silcutil/SilcBufferAPI/silc_buffer_senlarge
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_srealloc(SilcStack stack,
- *                                    SilcBuffer sb, SilcUInt32 newsize);
+ *    SilcBuffer silc_buffer_senlarge(SilcStack stack, SilcBuffer sb,
+ *                                    SilcUInt32 size);
  *
  * 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.
+ *    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 if system is out of memory.
+ *
+ *    This routine use SilcStack are memory source.  If `stack' is NULL
+ *    reverts back to normal allocating routine.
  *
- *    This routine use SilcStack are memory source.
+ *    Note that this call consumes the `stack'.  The caller should push the
+ *    stack before calling the function and pop it later.
  *
  ***/
 
 static inline
-SilcBuffer silc_buffer_srealloc(SilcStack stack,
-                               SilcBuffer sb, SilcUInt32 newsize)
+SilcBool silc_buffer_senlarge(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
 {
-  SilcUInt32 hlen, dlen;
-  unsigned char *h;
+  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;
+}
 
-  if (!sb)
-    return silc_buffer_salloc(stack, newsize);
+/****f* silcutil/SilcBufferAPI/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);
+ *
+ ***/
 
-  if (newsize <= silc_buffer_truelen(sb))
-    return sb;
+static inline
+SilcBool silc_buffer_append(SilcBuffer sb, SilcUInt32 size)
+{
+  if (silc_unlikely(!silc_buffer_realloc(sb, silc_buffer_truelen(sb) + size)))
+    return FALSE;
 
-  hlen = silc_buffer_headlen(sb);
-  dlen = silc_buffer_len(sb);
-  h = (unsigned char *)silc_srealloc_ua(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 = silc_smalloc_ua(stack, newsize);
-    if (!h)
-      return NULL;
-    memcpy(h, sb->head, silc_buffer_truelen(sb));
-  }
+  /* Enlarge data area */
+  silc_buffer_pull_tail(sb, size);
 
-  sb->head = h;
-  sb->data = sb->head + hlen;
-  sb->tail = sb->data + dlen;
-  sb->end = sb->head + newsize;
+  /* Copy old tail area to new tail area */
+  silc_buffer_put_tail(sb, sb->tail - size, silc_buffer_taillen(sb));
 
-  return sb;
+  return TRUE;
 }
 
-/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
+/****f* silcutil/SilcBufferAPI/silc_buffer_append
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
- *                                         SilcBuffer sb, SilcUInt32 newsize);
+ *    SilcBool silc_buffer_sappend(SilcStack stack, SilcBuffer sb,
+ *                                 SilcUInt32 size)
  *
  * 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.
+ *    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.
+ *    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
-SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
-                                    SilcBuffer sb, SilcUInt32 newsize)
+SilcBool silc_buffer_sappend(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
 {
-  sb = silc_buffer_srealloc(stack, sb, newsize);
-  if (!sb)
-    return NULL;
-  silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
-  return sb;
+  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/SilcBufferAPI/silc_buffer_scopy
+/****f* silcutil/SilcBufferAPI/silc_buffer_strchr
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb);
+ *    unsigned char *silc_buffer_strchr(SilcBuffer sb, int c, SilcBool first);
  *
  * 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.
+ *    Returns pointer to the occurence of the character `c' in the buffer
+ *    `sb'.  If the `first' is TRUE this finds the first occurene of `c',
+ *    if it is FALSE this finds the last occurence of `c'.  If the character
+ *    is found the `sb' data area is moved to that location and its pointer
+ *    is returned.  The silc_buffer_data call will return the same pointer.
+ *    Returns NULL if such character could not be located and the buffer
+ *    remains unmodified.
  *
- *    This routine use SilcStack are memory source.
+ *    This call is equivalent to strchr(), strrchr(), memchr() and memrchr()
+ *    except it works with SilcBuffer.
+ *
+ * NOTES
+ *
+ *    This searches only the data area of the buffer.  Head and tail area
+ *    are not searched.
+ *
+ *    The `sb' data need not be NULL terminated.
  *
  ***/
 
 static inline
-SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb)
+unsigned char *silc_buffer_strchr(SilcBuffer sb, int c, SilcBool first)
 {
-  SilcBuffer sb_new;
-
-  sb_new = silc_buffer_salloc_size(stack, silc_buffer_len(sb));
-  if (!sb_new)
-    return NULL;
-  silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
+  int i;
+
+  if (first) {
+    for (i = 0; i < silc_buffer_len(sb); i++) {
+      if (sb->data[i] == (unsigned char)c) {
+       sb->data = &sb->data[i];
+       return sb->data;
+      }
+    }
+  } else {
+    for (i = silc_buffer_len(sb) - 1; 1 >= 0; i--) {
+      if (sb->data[i] == (unsigned char)c) {
+       sb->data = &sb->data[i];
+       return sb->data;
+      }
+    }
+  }
 
-  return sb_new;
+  return NULL;
 }
 
-/****f* silcutil/SilcBufferAPI/silc_buffer_sclone
+/****f* silcutil/SilcBufferAPI/silc_buffer_cmp
  *
  * SYNOPSIS
  *
  *    static inline
- *    SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb);
+ *    SilcBool silc_buffer_equal(SilcBuffer sb1, SilcBuffer sb2)
  *
  * 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.
+ *    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
-SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb)
+SilcBool silc_buffer_equal(SilcBuffer sb1, SilcBuffer sb2)
 {
-  SilcBuffer sb_new;
+  if (silc_buffer_len(sb1) != silc_buffer_len(sb2))
+    return FALSE;
+  return memcmp(sb1->data, sb2->data, silc_buffer_len(sb1)) == 0;
+}
 
-  sb_new = silc_buffer_salloc_size(stack, silc_buffer_truelen(sb));
-  if (!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);
+/****f* silcutil/SilcBufferAPI/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.
+ *
+ ***/
 
-  return sb_new;
+static inline
+void silc_buffer_printf(SilcBuffer sb, SilcBool newline)
+{
+  silc_file_write(1, silc_buffer_data(sb), silc_buffer_len(sb));
+  if (newline)
+    printf("\n");
+  fflush(stdout);
 }
 
 #endif /* SILCBUFFER_H */