ofpbuf: New function ofpbuf_use_stack().
authorBen Pfaff <blp@nicira.com>
Thu, 23 Dec 2010 22:23:41 +0000 (14:23 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 27 Jan 2011 17:26:05 +0000 (09:26 -0800)
This new function is useful in a situation where a small stack-allocated
buffer is usually appropriate but occasionally it must be expanded.

Acked-by: Jesse Gross <jesse@nicira.com>
lib/ofpbuf.c
lib/ofpbuf.h

index 91ea36303f9f2d06be06d412f730f3d14eb1a4c4..bb8855bd619d81654a3e3c799a22cb538082dc1c 100644 (file)
 #include "dynamic-string.h"
 #include "util.h"
 
-/* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of
- * memory starting at 'base'.
- *
- * 'base' should ordinarily be the first byte of a region obtained from
- * malloc(), but in circumstances where it can be guaranteed that 'b' will
- * never need to be expanded or freed, it can be a pointer into arbitrary
- * memory. */
-void
-ofpbuf_use(struct ofpbuf *b, void *base, size_t allocated)
+static void
+ofpbuf_use__(struct ofpbuf *b, void *base, size_t allocated,
+             enum ofpbuf_source source)
 {
     b->base = b->data = base;
     b->allocated = allocated;
+    b->source = source;
     b->size = 0;
     b->l2 = b->l3 = b->l4 = b->l7 = NULL;
     list_poison(&b->list_node);
     b->private_p = NULL;
 }
 
+/* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of
+ * memory starting at 'base'.  'base' should be the first byte of a region
+ * obtained from malloc().  It will be freed (with free()) if 'b' is resized or
+ * freed. */
+void
+ofpbuf_use(struct ofpbuf *b, void *base, size_t allocated)
+{
+    ofpbuf_use__(b, base, allocated, OFPBUF_MALLOC);
+}
+
+/* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of
+ * memory starting at 'base'.  'base' should point to a buffer on the stack.
+ * If 'b' is resized, new memory will be allocated with malloc() and 'base'
+ * will not be freed.  This is useful when a small stack-allocated buffer is
+ * normally appropriate but sometimes it must be expanded.
+ *
+ * 'base' should be appropriately aligned.  Using an array of uint32_t or
+ * uint64_t for the buffer is a reasonable way to ensure appropriate alignment
+ * for 32- or 64-bit data.
+ *
+ * (Nothing actually relies on 'base' being allocated on the stack.  It could
+ * be static or malloc()'d memory.  But stack space is the most common use
+ * case.) */
+void
+ofpbuf_use_stack(struct ofpbuf *b, void *base, size_t allocated)
+{
+    ofpbuf_use__(b, base, allocated, OFPBUF_STACK);
+}
+
 /* Initializes 'b' as an ofpbuf whose data starts at 'data' and continues for
  * 'size' bytes.  This is appropriate for an ofpbuf that will be used to
  * inspect existing data, without moving it around or reallocating it, and
- * generally without modifying it at all. */
+ * generally without modifying it at all.
+ *
+ * An ofpbuf operation that requires reallocating data will assert-fail if this
+ * function was used to initialize it. */
 void
 ofpbuf_use_const(struct ofpbuf *b, const void *data, size_t size)
 {
-    ofpbuf_use(b, (void *) data, size);
+    ofpbuf_use__(b, (void *) data, size, OFPBUF_CONST);
     b->size = size;
 }
 
@@ -63,7 +90,7 @@ ofpbuf_init(struct ofpbuf *b, size_t size)
 void
 ofpbuf_uninit(struct ofpbuf *b)
 {
-    if (b) {
+    if (b && b->source == OFPBUF_MALLOC) {
         free(b->base);
     }
 }
@@ -176,8 +203,31 @@ ofpbuf_rebase__(struct ofpbuf *b, void *new_base)
 static void
 ofpbuf_resize_tailroom__(struct ofpbuf *b, size_t new_tailroom)
 {
-    b->allocated = ofpbuf_headroom(b) + b->size + new_tailroom;
-    ofpbuf_rebase__(b, xrealloc(b->base, b->allocated));
+    size_t new_allocated;
+    void *new_base;
+
+    new_allocated = ofpbuf_headroom(b) + b->size + new_tailroom;
+
+    switch (b->source) {
+    case OFPBUF_MALLOC:
+        new_base = xrealloc(b->base, new_allocated);
+        break;
+
+    case OFPBUF_STACK:
+        new_base = xmalloc(new_allocated);
+        memcpy(new_base, b->base, MIN(new_allocated, b->allocated));
+        b->source = OFPBUF_MALLOC;
+        break;
+
+    case OFPBUF_CONST:
+        NOT_REACHED();
+
+    default:
+        NOT_REACHED();
+    }
+
+    b->allocated = new_allocated;
+    ofpbuf_rebase__(b, new_base);
 }
 
 /* Ensures that 'b' has room for at least 'size' bytes at its tail end,
@@ -198,11 +248,14 @@ ofpbuf_prealloc_headroom(struct ofpbuf *b, size_t size)
 }
 
 /* Trims the size of 'b' to fit its actual content, reducing its tailroom to
- * 0.  Its headroom, if any, is preserved. */
+ * 0.  Its headroom, if any, is preserved.
+ *
+ * Buffers not obtained from malloc() are not resized, since that wouldn't save
+ * any memory. */
 void
 ofpbuf_trim(struct ofpbuf *b)
 {
-    if (ofpbuf_tailroom(b) > 0) {
+    if (b->source == OFPBUF_MALLOC && ofpbuf_tailroom(b) > 0) {
         ofpbuf_resize_tailroom__(b, 0);
     }
 }
index bd668c1e3f08f8030f810a3ee88260d8da607feb..15f0ab1626e2ee56935628aadd034358c8fd19f0 100644 (file)
 #define OFPBUF_H 1
 
 #include <stddef.h>
+#include <stdint.h>
 #include "list.h"
+#include "util.h"
 
 #ifdef  __cplusplus
 extern "C" {
 #endif
 
+enum ofpbuf_source {
+    OFPBUF_MALLOC,              /* Obtained via malloc(). */
+    OFPBUF_STACK,               /* Stack space or static buffer. */
+    OFPBUF_CONST                /* Must not be expanded. */
+};
+
 /* Buffer for holding arbitrary data.  An ofpbuf is automatically reallocated
  * as necessary if it grows too large for the available memory. */
 struct ofpbuf {
-    void *base;                 /* First byte of area malloc()'d area. */
+    void *base;                 /* First byte of allocated space. */
     size_t allocated;           /* Number of bytes allocated. */
+    enum ofpbuf_source source;  /* Source of memory allocated as 'base'. */
 
     void *data;                 /* First byte actually in use. */
     size_t size;                /* Number of bytes in use. */
@@ -42,7 +51,12 @@ struct ofpbuf {
     void *private_p;            /* Private pointer for use by owner. */
 };
 
+/* Declares NAME as a SIZE-byte array aligned properly for storing any kind of
+ * data.  For use with ofpbuf_use_stack(). */
+#define OFPBUF_STACK_BUFFER(NAME, SIZE) uint64_t NAME[DIV_ROUND_UP(SIZE, 8)]
+
 void ofpbuf_use(struct ofpbuf *, void *, size_t);
+void ofpbuf_use_stack(struct ofpbuf *, void *, size_t);
 void ofpbuf_use_const(struct ofpbuf *, const void *, size_t);
 
 void ofpbuf_init(struct ofpbuf *, size_t);