From 3c515a6ce9dbf81a4a7377b55a80bfac6e710b61 Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Wed, 4 Mar 2009 10:35:06 -0800 Subject: Very rough (doesn't even compile) pbstruct. --- pbstruct.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pbstruct.h | 114 +++++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 189 insertions(+), 26 deletions(-) diff --git a/pbstruct.c b/pbstruct.c index 34e7880..a580a2f 100644 --- a/pbstruct.c +++ b/pbstruct.c @@ -10,6 +10,25 @@ #define alignof(t) offsetof(struct { char c; t x; }, x) #define ALIGN_UP(p, t) (alignof(t) + ((p - 1) & ~(alignof(t) - 1))) +void pbstruct_init(struct pbstruct *s, struct pbstruct_definition *d) +{ + /* "set" flags start as unset, dynamic pointers start as NULL. */ + memset(s, 0, d->struct_size); + s->definition = d; +} + +pbstruct *pbstruct_new(struct pbstruct_defintion *d, struct pballoc *a) +{ + pbstruct *s = a->realloc(NULL, d->struct_size, d->user_data); + pbstruct_init(s, d); +} + +void pbstruct_free(struct pbstruct *s, struct pballoc *a, bool free_substructs) +{ + pbstruct_free_all_fields(s, free_substructs); + a->free(s, s->user_data); +} + bool pbstruct_is_set(struct pbstruct *s, struct pbstruct_field *f) { return s->data[f->isset_byte_offset] & f->isset_byte_mask; @@ -25,6 +44,88 @@ void pbstruct_set(struct pbstruct *s, struct pbstruct_field *f) s->data[f->isset_byte_offset] |= f->isset_byte_mask; } +void pbstruct_unset_all(struct pbstruct *s) +{ + for(int i = 0; i < s->definition->num_fields; i++) + pbstruct_unset(s, &s->definition->fields[i]); +} + +void pbstruct_resize_str(struct pbstruct *s, struct pbstruct_field *f, + size_t len, struct pballoc *a) +{ + assert(f->type & PBSTRUCT_DELIMITED); + size_t size = next_pow2(len); + struct pbstruct_dynamic **dyn = (struct pbstruct_dynamic**)get_addr(s, f); + if(!*dyn || (*dyn)->size < size) { + size_t old_len = *dyn ? (*dyn)->len : 0; + *dyn = a->realloc(*dyn, size*type_size, s->definition->user_data); + (*dyn)->size = size; + (*dyn)->len = len; + } +} + +size_t pbstruct_append_array(struct pbstruct *s, struct pbstruct_field *f, + struct pballoc *a) +{ + assert(f->type & PBSTRUCT_ARRAY); + struct pbstruct_dynamic **dyn = (struct pbstruct_dynamic**)get_addr(s, f); + size_t new_len, new_size; + if(!*dyn) { + new_len = 1; + new_size = 4; /* Initial size. */ + } else { + new_len = (*dyn)->len + 1; + new_size = next_pow2(new_len); + } + *dyn = a->realloc(*dyn, ARRSIZE(f->type, new_size), s->defintion->user_data); + memset(ARRADDR(*dyn, f->type, new_len-1), 0, SIZE(f->type)); +} + +void pbstruct_clear_array(struct pbstruct *s, struct pbstruct_field *f, + struct pballoc *a, bool free_substructs) +{ + assert(f->type & PBSTRUCT_ARRAY); + struct pbstruct_dynamic *dyn = *(struct pbstruct_dynamic**)get_addr(s, f); + if(dyn) { + if((f->type & PBSTRUCT_DELIMITED) || free_substructs) { + for(int i = 0; i < dyn->len; i++) + FREE(ARRADDR(dyn, f->type, i), a, s->definition->user_data); + } + dyn->size = 0; + } +} + +/* Resizes a delimited field (string or bytes) string that is within an array. + * The array must already be large enough that str_offset is a valid member. */ +void pbstruct_resize_arraystr(struct pbstruct *s, struct pbstruct_field *f, + size_t str_offset, size_t len, struct pballoc *a) +{ + +} + +/* Like pbstruct_append_array, but appends a string of the specified length. */ +size_t pbstruct_append_arraystr(struct pbstruct *s, struct pbstruct_field *f, + size_t len, struct pballoc *a) +{ +} + +void pbstruct_free_field(struct pbstruct *s, struct pbstruct_field *f, + bool free_substructs) +{ + pbstruct_clear_array(s, f, free_substructs); + struct pbstruct_dynamic **dyn = (struct pbstruct_dynamic**)get_addr(s, f); + if(*dyn) { + FREE(*dyn, s); + *dyn = NULL; + } +} + +void pbstruct_free_all_fields(struct pbstruct *s, bool free_substructs) +{ + for(int i = 0; i < s->definition->num_fields; i++) + pbstruct_free_field(s, &s->definition->fields[i]); +} + #define DEFINE_GETTERS(ctype, name) \ ctype *pbstruct_get_ ## name(struct pbstruct *s, struct pbstruct_field *f) { \ return (ctype*)(s->data + f->byte_offset); \ diff --git a/pbstruct.h b/pbstruct.h index 227d7bb..db4d242 100644 --- a/pbstruct.h +++ b/pbstruct.h @@ -39,6 +39,14 @@ * This proto will take N times more memory if structures are by value than if * they are by reference. To avoid such bad cases, submessages are by * reference. + * + * Ownership semantics: + * - Each pbstruct absolutely owns any data allocated for its arrays or + * strings. If you want a copy of this data that will outlive the struct, + * you must copy it out. + * - Each pbstruct optionally owns any data allocated for substructs, based + * on whether you pass the free_substructs parameter to any of several + * functions. */ #ifndef PBSTRUCT_H_ @@ -52,24 +60,25 @@ extern "C" { #endif typedef enum pbstruct_type { - PBSTRUCT_TYPE_DOUBLE = 0, - PBSTRUCT_TYPE_FLOAT = 1, - PBSTRUCT_TYPE_INT32 = 2, - PBSTRUCT_TYPE_UINT32 = 3, - PBSTRUCT_TYPE_INT64 = 4, - PBSTRUCT_TYPE_UINT64 = 5, - PBSTRUCT_TYPE_BOOL = 6, + PBSTRUCT_TYPE_DOUBLE = 1, + PBSTRUCT_TYPE_FLOAT = 2, + PBSTRUCT_TYPE_INT32 = 3, + PBSTRUCT_TYPE_UINT32 = 4, + PBSTRUCT_TYPE_INT64 = 5, + PBSTRUCT_TYPE_UINT64 = 6, + PBSTRUCT_TYPE_BOOL = 7, + /* Main struct contains a pointer to a pbstruct. */ - PBSTRUCT_TYPE_SUBSTRUCT = 7, + PBSTRUCT_TYPE_SUBSTRUCT = 8, /* Main struct contains a pointer to a pbstruct_delimited. */ - PBSTRUCT_DELIMITED = 8, /* Not itself a real type. */ - PBSTRUCT_TYPE_BYTES = PBSTRUCT_DELIMITED | 1, - PBSTRUCT_TYPE_STRING = PBSTRUCT_DELIMITED | 2, + PBSTRUCT_DYNAMIC = 16, + PBSTRUCT_TYPE_BYTES = PBSTRUCT_DYNAMIC | 1, + PBSTRUCT_TYPE_STRING = PBSTRUCT_DYNAMIC | 2, /* The main struct contains a pointer to a pbstruct_array, for which each * element is identical to the non-array form. */ - PBSTRUCT_ARRAY = 16, /* Not itself a real type. */ + PBSTRUCT_ARRAY = PBSTRUCT_DYNAMIC | 32, /* Not itself a real type. */ PBSTRUCT_TYPE_DOUBLE_ARRAY = PBSTRUCT_ARRAY | PBSTRUCT_TYPE_DOUBLE, PBSTRUCT_TYPE_FLOAT_ARRAY = PBSTRUCT_ARRAY | PBSTRUCT_TYPE_FLOAT, PBSTRUCT_TYPE_INT32_ARRAY = PBSTRUCT_ARRAY | PBSTRUCT_TYPE_INT32, @@ -90,13 +99,9 @@ struct pbstruct_field { uint8_t isset_byte_mask; }; -struct pbstruct_delimited { - size_t len; /* Measured in bytes. */ - uint8_t data[]; -}; - -struct pbstruct_array { - size_t len; /* Measured in elements. */ +struct pbstruct_dynamic { + size_t len; /* Number of present elements (bytes for string/bytes). */ + size_t size; /* Total size in present elements. */ uint8_t data[]; }; @@ -113,17 +118,74 @@ struct pbstruct { uint8_t data[]; /* layout is described by definition. */ }; -/* Initializes everything to unset. */ -void pbstruct_init(struct pbstruct *s, struct pbstruct_definition *definition); +struct pballoc { + void *(*realloc)(void *ptr, size_t size, void *user_data); + void (*free)(void *ptr, void *user_data); +}; + +/* Initializes everything to unset, with no dynamic memory alocated. */ +void pbstruct_init(struct pbstruct *s, struct pbstruct_definition *d); +pbstruct *pbstruct_new(struct pbstruct_defintion *d, struct pballoc *a); +/* Frees the struct itself and any dynamic string or array data, and substructs + * also if free_substructs is true. */ +void pbstruct_free(struct pbstruct *s, struct pballoc *a, bool free_substructs); + +/* For setting, clearing, and testing the "set" flag for each field. + * Clearing the "set" flag does not free any dynamically allocated + * storage -- it will be reused again if the field becomes set again. */ bool pbstruct_is_set(struct pbstruct *s, struct pbstruct_field *f); -/* Doesn't check whether the field is holding allocated memory, and will leak - * the memory if it is. */ void pbstruct_unset(struct pbstruct *s, struct pbstruct_field *f); +void pbstruct_unset_all(struct pbstruct *s); void pbstruct_set(struct pbstruct *s, struct pbstruct_field *f); -/* These do no existence checks or type checks. */ +/* Resizes the given string field to len, triggering a realloc() if + * necessary. Never reallocs() the string to be shorter. */ +void pbstruct_resize_str(struct pbstruct *s, struct pbstruct_field *f, + size_t len, struct pballoc *a); + +/* Appends an element to the given array field, triggering a realloc() if + * necessary. Returns the offset of the new array element. */ +size_t pbstruct_append_array(struct pbstruct *s, struct pbstruct_field *f, + struct pballoc *a); + +/* Sets the size of the given array field to zero, but does not free the + * array itself (call pbstruct_free_field() for that). Does free any + * strings that were in the array (for string arrays), and any substructs + * if free_substructs is true. Does not affect the "set" flag (if the + * field was previously set, it will remain set, but as zero elements long). */ +void pbstruct_clear_array(struct pbstruct *s, struct pbstruct_field *f, + struct pballoc *a, bool free_substructs); + +/* Resizes a delimited field (string or bytes) string that is within an array. + * The array must already be large enough that str_offset is a valid member. */ +void pbstruct_resize_arraystr(struct pbstruct *s, struct pbstruct_field *f, + size_t str_offset, size_t len, struct pballoc *a); + +/* Like pbstruct_append_array, but appends a string of the specified length. */ +size_t pbstruct_append_arraystr(struct pbstruct *s, struct pbstruct_field *f, + size_t len, struct pballoc *a); + +/* Clears all data associated with f (like pbstruct_clear_array()), but also + * frees all underlying storage. For string array fields, also frees the + * individual strings. Clears the "set" flag since it is an error for an + * array field to be set if it has no dynamic data allocated. */ +void pbstruct_free_field(struct pbstruct *s, struct pbstruct_field *f, + bool free_substructs); +void pbstruct_free_all_fields(struct pbstruct *s, bool free_substructs); + +/* These do no existence checks, bounds checks, or type checks. */ #define DECLARE_GETTERS(ctype, name) \ - ctype *pbstruct_get_ ## name(struct pbstruct *s, struct pbstruct_field *f); + ctype pbstruct_get_ ## name(struct pbstruct *s, \ + struct pbstruct_field *f); \ + ctype *pbstruct_get_ ## name ## _ptr(struct pbstruct *s, \ + struct pbstruct_field *f); \ + ctype pbstruct_get_ ## name ## _array(struct pbstruct *s, \ + struct pbstruct_field *f, \ + int i); \ + ctype *pbstruct_get_ ## name ## _array_ptr(struct pbstruct *s, \ + struct pbstruct_field *f, \ + int i); \ + DECLARE_GETTERS(double, double) DECLARE_GETTERS(float, float) @@ -132,9 +194,9 @@ DECLARE_GETTERS(int64_t, int64) DECLARE_GETTERS(uint32_t, uint32) DECLARE_GETTERS(uint64_t, uint64) DECLARE_GETTERS(bool, bool) +DECLARE_GETTERS(struct pbstruct*, substruct) DECLARE_GETTERS(struct pbstruct_delimited*, bytes) DECLARE_GETTERS(struct pbstruct_delimited*, string) -DECLARE_GETTERS(struct pbstruct*, substruct) #ifdef __cplusplus } /* extern "C" */ -- cgit v1.2.3