From 2ef013126c682a44d15554ea7a04144fc9a10fed Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Sat, 10 Jul 2010 13:28:47 -0700 Subject: Fleshed out upb_string further. Now upb_def's only unresolved references are upb_src. --- Makefile | 3 ++- core/upb_def.c | 11 +++++------ core/upb_string.c | 30 +++++++++++++++++++++++++++++- core/upb_string.h | 46 +++++++++++++++++++++++++++++++++++++--------- tests/test_string.c | 17 +++++++++++++++-- 5 files changed, 88 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 1f977b4..2abe0c7 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,8 @@ tests/test.proto.pb: tests/test.proto protoc tests/test.proto -otests/test.proto.pb TESTS=tests/test_string \ - tests/test_table + tests/test_table \ + tests/test_def tests: $(TESTS) OTHER_TESTS=tests/tests \ diff --git a/core/upb_def.c b/core/upb_def.c index bfab738..1f57c70 100644 --- a/core/upb_def.c +++ b/core/upb_def.c @@ -44,13 +44,12 @@ static void upb_deflist_push(upb_deflist *l, upb_def *d) { * join("", "Baz") -> "Baz" * Caller owns a ref on the returned string. */ static upb_string *upb_join(upb_string *base, upb_string *name) { - upb_string *joined = upb_strdup(base); - upb_strlen_t len = upb_string_len(joined); - if(len > 0) { - upb_string_getrwbuf(joined, len + 1)[len] = UPB_SYMBOL_SEPARATOR; + if (upb_string_len(base) == 0) { + return upb_string_getref(name); + } else { + return upb_string_asprintf(UPB_STRFMT "." UPB_STRFMT, + UPB_STRARG(base), UPB_STRARG(name)); } - upb_strcat(joined, name); - return joined; } // Qualify the defname for all defs starting with offset "start" with "str". diff --git a/core/upb_string.c b/core/upb_string.c index f9af9e9..2f487aa 100644 --- a/core/upb_string.c +++ b/core/upb_string.c @@ -82,7 +82,7 @@ char *upb_string_getrwbuf(upb_string *str, upb_strlen_t len) { } str->len = len; str->ptr = str->cached_mem; - return str->ptr; + return str->cached_mem; } void upb_string_substr(upb_string *str, upb_string *target_str, @@ -92,3 +92,31 @@ void upb_string_substr(upb_string *str, upb_string *target_str, str->ptr = upb_string_getrobuf(target_str) + start; str->len = len; } + +void upb_string_vprintf(upb_string *str, const char *format, va_list args) { + // Try once without reallocating. We have to va_copy because we might have + // to call vsnprintf again. + uint32_t size = UPB_MAX(upb_string_size(str), 16); + char *buf = upb_string_getrwbuf(str, size); + va_list args_copy; + va_copy(args_copy, args); + uint32_t true_size = vsnprintf(buf, size, format, args_copy); + va_end(args_copy); + + if (true_size > size) { + // Need to reallocate. + str = upb_string_tryrecycle(str); + buf = upb_string_getrwbuf(str, true_size); + vsnprintf(buf, true_size, format, args); + } + str->len = true_size; +} + +upb_string *upb_string_asprintf(const char *format, ...) { + upb_string *str = upb_string_new(); + va_list args; + va_start(args, format); + upb_string_vprintf(str, format, args); + va_end(args); + return str; +} diff --git a/core/upb_string.h b/core/upb_string.h index 7ec3d48..5cc0eaf 100644 --- a/core/upb_string.h +++ b/core/upb_string.h @@ -25,6 +25,7 @@ #include #include +#include #include "upb_atomic.h" #include "upb.h" @@ -37,7 +38,7 @@ extern "C" { struct _upb_string { // The pointer to our currently active data. This may be memory we own // or a pointer into memory we don't own. - char *ptr; + const char *ptr; // If non-NULL, this is a block of memory we own. We keep this cached even // if "ptr" is currently aliasing memory we don't own. @@ -111,16 +112,25 @@ INLINE void upb_string_endread(upb_string *str) { (void)str; } // } upb_string *upb_string_tryrecycle(upb_string *str); -// The three options for setting the contents of a string. These may only be -// called when a string is first created or recycled; once other functions have -// been called on the string, these functions are not allowed until the string -// is recycled. +// The options for setting the contents of a string. These may only be called +// when a string is first created or recycled; once other functions have been +// called on the string, these functions are not allowed until the string is +// recycled. // Gets a pointer suitable for writing to the string, which is guaranteed to // have at least "len" bytes of data available. The size of the string will // become "len". char *upb_string_getrwbuf(upb_string *str, upb_strlen_t len); +// Replaces the contents of str with the contents of the given printf. +void upb_string_vprintf(upb_string *str, const char *format, va_list args); +INLINE void upb_string_printf(upb_string *str, const char *format, ...) { + va_list args; + va_start(args, format); + upb_string_vprintf(str, format, args); + va_end(args); +} + // Sets the contents of "str" to be the given substring of "target_str", to // which the caller must own a ref. void upb_string_substr(upb_string *str, upb_string *target_str, @@ -144,7 +154,7 @@ void upb_string_substr(upb_string *str, upb_string *target_str, /* upb_string library functions ***********************************************/ // Named like their counterparts, these are all safe against buffer -// overflow. These only use the public upb_string interface. +// overflow. For the most part 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) { @@ -163,6 +173,17 @@ INLINE bool upb_streql(upb_string *s1, upb_string *s2) { // Like strcmp(). int upb_strcmp(upb_string *s1, upb_string *s2); +// Compare a upb_string with memory or a NULL-terminated C string. +INLINE bool upb_streqllen(upb_string *str, const void *buf, upb_strlen_t len) { + return len == upb_string_len(str) && + memcmp(upb_string_getrobuf(str), buf, len) == 0; +} + +INLINE bool upb_streqlc(upb_string *str, const void *buf) { + // Could be made one-pass. + return upb_streqllen(str, buf, strlen((const char*)buf)); +} + // Like upb_strcpy, but copies from a buffer and length. INLINE void upb_strcpylen(upb_string *dest, const void *src, upb_strlen_t len) { memcpy(upb_string_getrwbuf(dest, len), src, len); @@ -175,10 +196,10 @@ INLINE void upb_strcpy(upb_string *dest, upb_string *src) { } // Like upb_strcpy, but copies from a NULL-terminated string. -INLINE void upb_strcpyc(upb_string *dest, const char *src) { +INLINE void upb_strcpyc(upb_string *dest, const void *src) { // This does two passes over src, but that is necessary unless we want to // repeatedly re-allocate dst, which seems worse. - upb_strcpylen(dest, src, strlen(src)); + upb_strcpylen(dest, src, strlen((const char*)src)); } // Returns a new string whose contents are a copy of s. @@ -200,11 +221,18 @@ INLINE upb_string *upb_strdupc(const char *src) { void upb_strcat(upb_string *s, upb_string *append); // Returns a new string that is a substring of the given string. -upb_string *upb_strslice(upb_string *s, int offset, int len); +INLINE upb_string *upb_strslice(upb_string *s, int offset, int len) { + upb_string *str = upb_string_new(); + upb_string_substr(str, s, offset, len); + return str; +} // Reads an entire file into a newly-allocated string. upb_string *upb_strreadfile(const char *filename); +// Returns a new string with the contents of the given printf. +upb_string *upb_string_asprintf(const char *format, ...); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tests/test_string.c b/tests/test_string.c index 4fdab6c..5e6e2a9 100644 --- a/tests/test_string.c +++ b/tests/test_string.c @@ -17,7 +17,7 @@ int main() { assert(upb_string_len(str) == (sizeof(static_str) - 1)); const char *robuf = upb_string_getrobuf(str); assert(robuf != NULL); - assert(memcmp(robuf, static_str, upb_string_len(str)) == 0); + assert(upb_streqlc(str, static_str)); upb_string_endread(str); upb_string *str2 = upb_string_tryrecycle(str); @@ -28,7 +28,7 @@ int main() { upb_strcpyc(str, "XX"); const char *robuf2 = upb_string_getrobuf(str); assert(robuf2 == robuf); - assert(memcmp(robuf2, "XX", 2) == 0); + assert(upb_streqlc(str, "XX")); // Make string alias part of another string. str2 = upb_strdupc("WXYZ"); @@ -51,6 +51,19 @@ int main() { const char *robuf5 = upb_string_getrobuf(str); assert(robuf5 == robuf); + // Resetting str to something very long should require new data to be + // allocated. + str = upb_string_tryrecycle(str); + const char longstring[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + upb_strcpyc(str, longstring); + const char *robuf6 = upb_string_getrobuf(str); + assert(robuf6 != robuf); + assert(upb_streqlc(str, longstring)); + + // Test printf. + str = upb_string_tryrecycle(str); + upb_string_printf(str, "Number: %d, String: %s", 5, "YO!"); + upb_string_unref(str); upb_string_unref(str2); } -- cgit v1.2.3