Added SILC_STR_STRING and SILC_STR_STRING_APPEND and support for
[crypto.git] / lib / silcutil / silcbuffer.h
index d98877bf812cf5220cabfe33ebc93bb6af41325b..3a3db7b679ffc7283060054813544fc80e2b69a7 100644 (file)
@@ -4,7 +4,7 @@
 
   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
@@ -238,7 +238,8 @@ SilcUInt32 silc_buffer_taillen(SilcBuffer x)
  *
  * 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.
  *
  ***/
 
@@ -276,7 +277,8 @@ SilcBuffer silc_buffer_alloc(SilcUInt32 len)
  *
  * DESCRIPTION
  *
- *    Allocates new SilcBuffer and returns it.
+ *    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.
@@ -503,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
  *
@@ -530,8 +533,10 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT(len <= silc_buffer_len(sb));
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely(len > silc_buffer_len(sb)))
+  if (silc_unlikely(len > silc_buffer_len(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
+  }
 
   sb->data += len;
   return old_data;
@@ -548,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
  *
@@ -575,8 +581,10 @@ unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT((sb->data - len) >= sb->head);
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely((sb->data - len) < sb->head))
+  if (silc_unlikely((sb->data - len) < sb->head)) {
+    silc_set_errno(SILC_ERR_UNDERFLOW);
     return NULL;
+  }
 
   sb->data -= len;
   return old_data;
@@ -593,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
  *
@@ -620,8 +628,10 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT(len <= silc_buffer_taillen(sb));
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely(len > silc_buffer_taillen(sb)))
+  if (silc_unlikely(len > silc_buffer_taillen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     return NULL;
+  }
 
   sb->tail += len;
   return old_tail;
@@ -638,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
  *
@@ -665,8 +676,10 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT((sb->tail - len) >= sb->data);
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely((sb->tail - len) < sb->data))
+  if (silc_unlikely((sb->tail - len) < sb->data)) {
+    silc_set_errno(SILC_ERR_UNDERFLOW);
     return NULL;
+  }
 
   sb->tail -= len;
   return old_tail;
@@ -684,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
  *
@@ -706,8 +720,18 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT(len <= silc_buffer_headlen(sb));
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely(len > silc_buffer_headlen(sb)))
+  if (silc_unlikely(len > silc_buffer_headlen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     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);
 }
@@ -724,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
  *
@@ -746,8 +771,18 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT(len <= silc_buffer_len(sb));
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely(len > silc_buffer_len(sb)))
+  if (silc_unlikely(len > silc_buffer_len(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     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);
 }
@@ -764,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
  *
@@ -786,8 +822,18 @@ unsigned char *silc_buffer_put_tail(SilcBuffer sb,
 #ifdef SILC_DIST_INPLACE
   SILC_ASSERT(len <= silc_buffer_taillen(sb));
 #endif /* SILC_DIST_INPLACE */
-  if (silc_unlikely(len > silc_buffer_taillen(sb)))
+  if (silc_unlikely(len > silc_buffer_taillen(sb))) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     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);
 }
@@ -803,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.
  *
  ***/
 
@@ -828,7 +874,7 @@ SilcBuffer silc_buffer_alloc_size(SilcUInt32 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 if system is out of memory.
  *
  *    This routine use SilcStack are memory source.  If `stack' is NULL
  *    reverts back to normal allocating routine.
@@ -943,7 +989,7 @@ void silc_buffer_end(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.
  *
  ***/
 
@@ -971,7 +1017,7 @@ SilcBuffer silc_buffer_copy(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.
+ *    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.
@@ -1005,7 +1051,7 @@ SilcBuffer silc_buffer_scopy(SilcStack stack, 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.
  *
  ***/
 
@@ -1035,7 +1081,7 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
  *
  *    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 if system is out of memory.
  *
  *    This routine use SilcStack are memory source.  If `stack' is NULL
  *    reverts back to normal allocating routine.
@@ -1072,7 +1118,7 @@ SilcBuffer silc_buffer_sclone(SilcStack stack, 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.
  *
  ***/
 
@@ -1113,7 +1159,7 @@ SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
  *
  *    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.
+ *    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.
@@ -1143,14 +1189,8 @@ SilcBuffer silc_buffer_srealloc(SilcStack stack,
   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;
@@ -1171,7 +1211,7 @@ SilcBuffer silc_buffer_srealloc(SilcStack stack,
  *
  *    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.
  *
  ***/
 
@@ -1197,7 +1237,7 @@ SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize)
  *
  *    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.
+ *    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.
@@ -1233,7 +1273,7 @@ SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
  *    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.
+ *    the extra space.  Returns FALSE if system is out of memory.
  *
  ***/
 
@@ -1267,7 +1307,7 @@ SilcBool silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size)
  *    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.
+ *    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.
@@ -1292,6 +1332,106 @@ SilcBool silc_buffer_senlarge(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
   return TRUE;
 }
 
+/****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);
+ *
+ ***/
+
+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/SilcBufferAPI/silc_buffer_append
+ *
+ * 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/SilcBufferAPI/silc_buffer_strchr
  *
  * SYNOPSIS
@@ -1345,4 +1485,49 @@ unsigned char *silc_buffer_strchr(SilcBuffer sb, int c, SilcBool first)
   return NULL;
 }
 
+/****f* silcutil/SilcBufferAPI/silc_buffer_cmp
+ *
+ * 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/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.
+ *
+ ***/
+
+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 */