From 31ac1e590b9bb2a2ce7c70cd7c8fd2db0fb8e481 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 23 Dec 2010 14:23:41 -0800 Subject: [PATCH] ofpbuf: New function ofpbuf_use_stack(). 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 --- lib/ofpbuf.c | 85 ++++++++++++++++++++++++++++++++++++++++++---------- lib/ofpbuf.h | 16 +++++++++- 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c index 91ea3630..bb8855bd 100644 --- a/lib/ofpbuf.c +++ b/lib/ofpbuf.c @@ -22,32 +22,59 @@ #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); } } diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h index bd668c1e..15f0ab16 100644 --- a/lib/ofpbuf.h +++ b/lib/ofpbuf.h @@ -18,17 +18,26 @@ #define OFPBUF_H 1 #include +#include #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); -- 2.30.2