From 7dd113baa80af735073ce7131e46c66a6b69d01f Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Mon, 21 Dec 2009 12:52:35 -0800 Subject: Added upb_data.*, left out of last commit. --- src/upb_data.c | 78 ++++++++++++++++ src/upb_data.h | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 src/upb_data.c create mode 100644 src/upb_data.h diff --git a/src/upb_data.c b/src/upb_data.c new file mode 100644 index 0000000..66e9885 --- /dev/null +++ b/src/upb_data.c @@ -0,0 +1,78 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2009 Joshua Haberman. See LICENSE for details. + */ + +#include "upb_data.h" + +static uint32_t round_up_to_pow2(uint32_t v) +{ + /* cf. http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +upb_string *upb_string_new() { + upb_string *s = malloc(sizeof(*s)); + s->byte_size = 0; + s->byte_len = 0; + s->ptr = NULL; + s->is_heap_allocated = true; + s->is_frozen = false; + return s; +} + +static void _upb_string_free(upb_string *s) +{ + if(s->byte_size != 0) free(s->ptr); + free(s); +} + +INLINE upb_string *upb_string_getref(upb_string *s, upb_flags_t ref_flags) { + if((ref_flags == UPB_REF_FROZEN && !s->is_frozen && s + upb_atomic_read((void*)(s + 1)) > 1) || + (ref_flags == UPB_REF_MUTABLE && s->is_frozen && +} + +char *upb_string_getrwbuf(upb_string *s, upb_strlen_t byte_len) +{ + if(s->byte_size < byte_len) { + // Need to resize. + s->byte_size = round_up_to_pow2(byte_len); + s->ptr = realloc(s->ptr, s->byte_size); + } + s->byte_len = byte_len; + return s->ptr; +} + +void upb_msg_destroy(struct upb_msg *msg) { + for(upb_field_count_t i = 0; i < msg->def->num_fields; i++) { + struct upb_fielddef *f = &msg->def->fields[i]; + if(!upb_msg_isset(msg, f) || !upb_field_ismm(f)) continue; + upb_mm_destroy(upb_msg_getptr(msg, f), upb_field_ptrtype(f)); + } + upb_def_unref(UPB_UPCAST(msg->def)); + free(msg); +} + +void upb_array_destroy(struct upb_array *arr) +{ + if(upb_elem_ismm(arr->fielddef)) { + upb_arraylen_t i; + /* Unref elements. */ + for(i = 0; i < arr->size; i++) { + union upb_value_ptr p = upb_array_getelementptr(arr, i); + upb_mm_destroy(p, upb_elem_ptrtype(arr->fielddef)); + } + } + if(arr->size != 0) free(arr->elements._void); + free(arr); +} + diff --git a/src/upb_data.h b/src/upb_data.h new file mode 100644 index 0000000..a15cb35 --- /dev/null +++ b/src/upb_data.h @@ -0,0 +1,279 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2009 Joshua Haberman. See LICENSE for details. + * + * This file defines the in-memory format for messages, arrays, and strings + * (which are the three dynamically-allocated structures that make up all + * protobufs). */ + +#ifndef UPB_DATA_H +#define UPB_DATA_H + +#include +#include "upb.h" +#include "upb_def.h" + +// Set if the object itself was allocated with malloc() and should be freed +// with free(). This flag would be false if the object was allocated on the +// stack or is data from the static segment of an object file. Note that this +// flag does not apply to the data being referenced by a string or array. +// +// If this flag is false, UPB_FLAG_HAS_REFCOUNT must be false also; there is +// no sense refcounting something that does not need to be freed. +#define UPB_FLAG_IS_HEAP_ALLOCATED (1<<2) + +// Set if the object is frozen against modification. While an object is +// frozen, it is suitable for concurrent access. Note that this flag alone is +// not a sufficient mechanism for preventing any kind of writes to the object's +// memory, because the object could still have a refcount and/or reflist. +#define UPB_FLAG_IS_FROZEN (1<<3) + +// Set if the object has an embedded refcount. +#define UPB_FLAG_HAS_REFCOUNT (1<<4) + +// Specifies the type of ref that is requested based on the kind of access the +// caller needs to the object. +enum upb_ref_flags { + // Use when the client plans to perform read-only access to the object, and + // only in one thread at a time. This imposes the least requirements on the + // object; it can be either frozen or not. As a result, requesting a + // reference of this type never performs a copy unless the object has no + // refcount. + UPB_REF_THREADUNSAFE_READONLY = 0, + + // Use when the client plans to perform read-only access, but from multiple + // threads concurrently. This will force the object to eagerly perform any + // parsing that may have been lazily deferred, and will force a copy if the + // object is not current frozen and there are any other referents (who expect + // the object to stay writable). + // + // Asking for a reference of this type is equivalent to: + // x = getref(y, UPB_REF_THREADUNSAFE_READONLY); + // x = freeze(x); + // ...except it is more efficient. + UPB_REF_FROZEN = 1, + + // Use when the client plans to perform read/write access. As a result, the + // reference will not be thread-safe for concurrent reading *or* writing; the + // object must be externally synchronized if it is being accessed from more + // than one thread. This will force a copy if the object is not currently + // frozen and there are any other referents (who expect the object to stay + // safe for unsynchronized reads). + // + // Asking for a reference of this type is equivalent to: + // x = getref(y, UPB_REF_THREADUNSAFE_READONLY); + // x = thaw(x); + // ...except it is more efficient. + UPB_REF_MUTABLE = 2 +} + +typedef uint8_t upb_flags_t; +struct upb_mmhead {}; + +/* upb_string *****************************************************************/ + +typedef uint32_t upb_strlen_t; + +// The members of this struct are private. Access should only be through the +// associated functions. +typedef struct upb_string { + unsigned int byte_size:29; // How many bytes we own, 0 if we don't own. + bool is_heap_allocated:1; + bool is_frozen:1; + // TODO. At the moment, all dynamically allocated strings have refcounts. + bool has_refcount:1; + upb_strlen_t byte_len; + // We expect the data to be 8-bit clean (uint8_t), but char* is such an + // ingrained convention that we follow it. + char *ptr; +} upb_string; + +typedef struct { + upb_string s; + upb_atomic_refcount_t refcount; +} upb_refcounted_string; + +// Returns a newly constructed string, which starts out empty. Caller owns one +// ref on it. +upb_string *upb_string_new(void); + +// Returns a string to which caller owns a ref, and contains the same contents +// as src. The returned value may be a copy of src, if the requested flags +// were incompatible with src's. +INLINE upb_string *upb_string_getref(upb_string *_s, upb_flags_t ref_flags) { + upb_refcount_string *s = _s; + if((ref_flags == UPB_REF_FROZEN && !s->is_frozen) || + (ref_flags == UPB_REF_MUTABLE && s->is_frozen) || + !s->has_refcount) { + // Copy should always be refcounted. Should be frozen iff a frozen ref was req. + return upb_strdup(s, flags); + } + // Take a ref on the existing object. + if(s->is_frozen) upb_atomic_ref(s->refcount); + else s->refcount.val++; + return s; +} + +// The caller releases a ref on src, which it must previously have owned a ref +// on. +INLINE void upb_string_unref(upb_string *s) { + void *refcount = s + 1; + if(s->is_frozen) { +} + +// Returns a buffer to which the caller may write. The string is resized to +// byte_len (which may or may not trigger a reallocation). The src string must +// not be frozen otherwise the program will assert-fail or abort(). +char *upb_string_getrwbuf(upb_string *s, upb_strlen_t byte_len); + +// Returns a buffer that the caller may use to read the current contents of +// the string. The number of bytes available is upb_strlen(s). +INLINE const char *upb_string_getrobuf(upb_string *s) { + return s->ptr; +} + +// Returns the current length of the string. +INLINE size_t upb_strlen(upb_string *s) { + return s->byte_len; +} + +/* upb_string library functions ***********************************************/ + +// Named like their counterparts, these are all safe against buffer +// overflow. These only use the public upb_string interface. + +// More efficient than upb_strcmp if all you need is to test equality. +INLINE bool upb_streql(upb_string *s1, upb_string *s2) { + upb_strlen_t len = upb_strlen(s1); + if(len != upb_strlen(s2)) { + return false; + } else { + return memcmp(upb_string_getrobuf(s1), upb_string_getrobuf(s2), len) == 0; + } +} + +INLINE int upb_strcmp(upb_string *s1, upb_string *s2) { + upb_strlen_t common_length = UPB_MIN(upb_strlen(s1), upb_strlen(s2)); + int common_diff = memcmp(upb_string_getrobuf(s1), upb_string_getrobuf(s2), + common_length); + return common_diff == + 0 ? ((int)upb_strlen(s1) - (int)upb_strlen(s2)) : common_diff; +} + +INLINE void upb_strcpy(upb_string *dest, upb_string *src) { + upb_strlen_t src_len = upb_strlen(src); + memcpy(upb_string_getrwbuf(dest, src_len), upb_string_getrobuf(src), src_len); +} + +// Reads an entire file into a newly-allocated string (caller owns one ref). +upb_string *upb_strreadfile(const char *filename); + +// Typedef for a read-only string that is allocated statically or on the stack. +// Initialize with the given macro, which must resolve to a const char*. You +// must not dynamically allocate this type. +typedef upb_string upb_static_string; +#define UPB_STRLIT(str) {sizeof(str), false, true, false, 0, str} + +// Allows using upb_strings in printf, ie: +// upb_string str = UPB_STRLIT("Hello, World!\n"); +// printf("String is: " UPB_STRFMT, UPB_STRARG(str)); */ +#define UPB_STRARG(str) (str)->byte_len, (str)->ptr +#define UPB_STRFMT "%.*s" + +/* upb_array ******************************************************************/ + +typedef uint32_t upb_arraylen_t; + +// The members of this struct are private. Access should only be through the +// associated functions. +typedef struct { + unsigned int size:29; // How many bytes we own, 0 if we don't own. + bool is_heap_allocated:1; + bool is_frozen:1; + bool has_refcount:1; + upb_arraylen_t len; + union upb_value_ptr *elements; +} upb_array; + +#define UPB_DEFINE_MSG_ARRAY(type) \ +typedef struct type ## array { \ + unsigned int size:29; \ + bool is_heap_allocated:1; \ + bool is_frozen:1;\ + bool has_refcount:1;\ + upb_arraylen_t len;\ + type **elements; \ +} type ## array; \ + +#define UPB_MSG_ARRAY(type) struct type ## array + +// Constructs a newly-allocated array, which starts out empty. Caller owns one +// ref on it. +upb_array *upb_array_new(void); + +// Returns an array to which caller owns a ref, and contains the same contents +// as src. The returned value may be a copy of src, if the requested flags +// were incompatible with src's. +INLINE upb_array *upb_array_getref(upb_array *src, upb_flags_t flags); + +// The caller releases a ref on the given array, which it must previously have +// owned a ref on. +INLINE void upb_array_unref(upb_array *a, struct upb_fielddef *f); + +// Sets the given element in the array to val. The current length of the array +// must be greater than elem. If the field type is dynamic, the array will +// take a ref on val and release a ref on what was previously in the array. +INLINE void upb_array_set(upb_array *a, struct upb_fielddef *f, int elem, + union upb_value val); + +// Note that the caller does *not* own a ref on the returned value. +INLINE union upb_value upb_array_get(upb_array *a, struct upb_fielddef *f, + int elem); +INLINE union upb_value upb_array_getmutable(upb_array *a, + struct upb_fielddef *f, int elem, + union upb_value val); + +// Note that array_append will attempt to take a reference on the given value, +// so to avoid a copy use append_default and get. +INLINE void upb_array_append(upb_array *a, struct upb_fielddef *f, + union upb_value val); +INLINE void upb_array_append_default(upb_array *a, struct upb_fielddef *f, + union upb_value val); + +// Returns the current number of elements in the array. +INLINE size_t upb_array_len(upb_array *a) { + return a->len; +} + +/* upb_msg ********************************************************************/ + +typedef struct { + uint8_t data[1]; +} upb_msg; + +// Creates a new msg of the given type. +upb_msg *upb_msg_new(struct upb_msgdef *md); + +void upb_msg_unref(upb_msg *msg, struct upb_msgdef *md); + +// Tests whether the given field is explicitly set, or whether it will return +// a default. +bool upb_msg_isset(upb_msg *msg, struct upb_fielddef *f); + +// Returns the current value if set, or the default value if not set, of the +// specified field. The mutable version will first replace the value with a +// mutable copy if it is not already mutable. +union upb_value upb_msg_get(upb_msg *msg, struct upb_fielddef *f); +union upb_value upb_msg_getmutable(upb_msg *msg, struct upb_fielddef *f); + +// Sets the given field to the given value. The msg will take a ref on val, +// and will drop a ref on whatever was there before. +void upb_msg_set(upb_msg *msg, struct upb_fielddef *f, union upb_value val); + +void upb_msg_clear(upb_msg *msg, struct upb_msgdef *md); + +void upb_msg_parsestr(upb_msg *msg, struct upb_msgdef *md, upb_string *data, + struct upb_status *status); + +#endif -- cgit v1.2.3