From d7d1b2a14120e0194aadcfcb327a542f81213058 Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Tue, 12 Jan 2010 11:29:32 -0800 Subject: Move many serializing functions to .cc file, since they do not need to be exposed. --- src/upb_serialize.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++-- src/upb_serialize.h | 196 ++++------------------------------------------------ src/upb_sink.h | 2 + 3 files changed, 194 insertions(+), 188 deletions(-) (limited to 'src') diff --git a/src/upb_serialize.c b/src/upb_serialize.c index a1d4718..8527f1e 100644 --- a/src/upb_serialize.c +++ b/src/upb_serialize.c @@ -7,12 +7,188 @@ #include "upb_serialize.h" #include "descriptor.h" -upb_status_t upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft, - union upb_value_ptr v, uint8_t **outbuf) +/* Functions for calculating sizes. *******************************************/ + +INLINE size_t upb_v_uint64_t_size(uint64_t val) { +#ifdef __GNUC__ + int high_bit = 63 - __builtin_clzll(val); /* 0-based, undef if val == 0. */ +#else + int high_bit = 0; + uint64_t tmp = val; + while(tmp >>= 1) high_bit++; +#endif + return val == 0 ? 1 : high_bit / 7 + 1; +} + +INLINE size_t upb_v_int32_t_size(int32_t val) { + /* v_uint32's are sign-extended to maintain wire compatibility with int64s. */ + return upb_v_uint64_t_size((int64_t)val); +} +INLINE size_t upb_v_uint32_t_size(uint32_t val) { + return upb_v_uint64_t_size(val); +} +INLINE size_t upb_f_uint64_t_size(uint64_t val) { + (void)val; /* Length is independent of value. */ + return sizeof(uint64_t); +} +INLINE size_t upb_f_uint32_t_size(uint32_t val) { + (void)val; /* Length is independent of value. */ + return sizeof(uint32_t); +} + + +/* Functions to write wire values. ********************************************/ + +/* Puts a varint (wire type: UPB_WIRE_TYPE_VARINT). */ +uint8_t *upb_put_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val, + struct upb_status *status) +{ + do { + uint8_t byte = val & 0x7f; + val >>= 7; + if(val) byte |= 0x80; + if(buf >= end) { + status->code = UPB_STATUS_NEED_MORE_DATA; + return end; + } + *buf++ = byte; + } while(val); + return buf; +} + +/* Puts an unsigned 32-bit varint, verbatim. Never uses the high 64 bits. */ +uint8_t *upb_put_v_uint32_t(uint8_t *buf, uint8_t *end, uint32_t val, + struct upb_status *status) +{ + return upb_put_v_uint64_t(buf, end, val, status); +} + +/* Puts a signed 32-bit varint, first sign-extending to 64-bits. We do this to + * maintain wire-compatibility with 64-bit signed integers. */ +uint8_t *upb_put_v_int32_t(uint8_t *buf, uint8_t *end, int32_t val, + struct upb_status *status) +{ + return upb_put_v_uint64_t(buf, end, (int64_t)val, status); +} + +void upb_put32(uint8_t *buf, uint32_t val) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24); +} + +/* Puts a fixed-length 32-bit integer (wire type: UPB_WIRE_TYPE_32BIT). */ +uint8_t *upb_put_f_uint32_t(uint8_t *buf, uint8_t *end, uint32_t val, + struct upb_status *status) +{ + uint8_t *uint32_end = buf + sizeof(uint32_t); + if(uint32_end > end) { + status->code = UPB_STATUS_NEED_MORE_DATA; + return end; + } +#if UPB_UNALIGNED_READS_OK + *(uint32_t*)buf = val; +#else + upb_put32(buf, val); +#endif + return uint32_end; +} + +/* Puts a fixed-length 64-bit integer (wire type: UPB_WIRE_TYPE_64BIT). */ +uint8_t *upb_put_f_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val, + struct upb_status *status) +{ + uint8_t *uint64_end = buf + sizeof(uint64_t); + if(uint64_end > end) { + status->code = UPB_STATUS_NEED_MORE_DATA; + return end; + } +#if UPB_UNALIGNED_READS_OK + *(uint64_t*)buf = val; +#else + upb_put32(buf, (uint32_t)val); + upb_put32(buf, (uint32_t)(val >> 32)); +#endif + return uint64_end; +} + +/* Functions to write .proto values. ******************************************/ + +/* Performs zig-zag encoding, which is used by sint32 and sint64. */ +INLINE uint32_t upb_zzenc_32(int32_t n) { return (n << 1) ^ (n >> 31); } +INLINE uint64_t upb_zzenc_64(int64_t n) { return (n << 1) ^ (n >> 63); } + +/* Use macros to define a set of two functions for each .proto type: + * + * // Converts and writes a .proto value into buf. "end" indicates the end + * // of the current available buffer (if the buffer does not contain enough + * // space UPB_STATUS_NEED_MORE_DATA is returned). On success, *outbuf will + * // point one past the data that was written. + * uint8_t *upb_put_INT32(uint8_t *buf, uint8_t *end, int32_t val, + * struct upb_status *status); + * + * // Returns the number of bytes required to serialize val. + * size_t upb_get_INT32_size(int32_t val); + * + * // Given a .proto value s (source) convert it to a wire value. + * uint32_t upb_vtowv_INT32(int32_t s); + */ + +#define VTOWV(type, wire_t, val_t) \ + INLINE wire_t upb_vtowv_ ## type(val_t s) + +#define PUT(type, v_or_f, wire_t, val_t, member_name) \ + INLINE uint8_t *upb_put_ ## type(uint8_t *buf, uint8_t *end, val_t val, \ + struct upb_status *status) { \ + wire_t tmp = upb_vtowv_ ## type(val); \ + return upb_put_ ## v_or_f ## _ ## wire_t(buf, end, tmp, status); \ + } + +#define T(type, v_or_f, wire_t, val_t, member_name) \ + INLINE size_t upb_get_ ## type ## _size(val_t val) { \ + return upb_ ## v_or_f ## _ ## wire_t ## _size(val); \ + } \ + VTOWV(type, wire_t, val_t); /* prototype for PUT below */ \ + PUT(type, v_or_f, wire_t, val_t, member_name) \ + VTOWV(type, wire_t, val_t) + +T(INT32, v, uint32_t, int32_t, int32) { return (uint32_t)s; } +T(INT64, v, uint64_t, int64_t, int64) { return (uint64_t)s; } +T(UINT32, v, uint32_t, uint32_t, uint32) { return s; } +T(UINT64, v, uint64_t, uint64_t, uint64) { return s; } +T(SINT32, v, uint32_t, int32_t, int32) { return upb_zzenc_32(s); } +T(SINT64, v, uint64_t, int64_t, int64) { return upb_zzenc_64(s); } +T(FIXED32, f, uint32_t, uint32_t, uint32) { return s; } +T(FIXED64, f, uint64_t, uint64_t, uint64) { return s; } +T(SFIXED32, f, uint32_t, int32_t, int32) { return (uint32_t)s; } +T(SFIXED64, f, uint64_t, int64_t, int64) { return (uint64_t)s; } +T(BOOL, v, uint32_t, bool, _bool) { return (uint32_t)s; } +T(ENUM, v, uint32_t, int32_t, int32) { return (uint32_t)s; } +T(DOUBLE, f, uint64_t, double, _double) { + union upb_value v; + v._double = s; + return v.uint64; +} +T(FLOAT, f, uint32_t, float, _float) { + union upb_value v; + v._float = s; + return v.uint32; +} +#undef VTOWV +#undef PUT +#undef T + +INLINE size_t upb_get_tag_size(uint32_t fieldnum) { + return upb_v_uint64_t_size((uint64_t)fieldnum << 3); +} + +uint8_t *upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft, + union upb_value_ptr v, struct upb_status *status) { #define CASE(t, member_name) \ case GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ ## t: \ - return upb_put_ ## t(buf, end, *v.member_name, outbuf); + return upb_put_ ## t(buf, end, *v.member_name, status); switch(ft) { CASE(DOUBLE, _double) CASE(FLOAT, _float) @@ -28,7 +204,7 @@ upb_status_t upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft CASE(SFIXED64, int64) CASE(BOOL, _bool) CASE(ENUM, int32) - default: return UPB_ERROR_ILLEGAL; + default: return end; } #undef CASE } diff --git a/src/upb_serialize.h b/src/upb_serialize.h index 29c411a..6ce9c81 100644 --- a/src/upb_serialize.h +++ b/src/upb_serialize.h @@ -1,19 +1,11 @@ /* * upb - a minimalist implementation of protocol buffers. * - * Implements routines for serializing protobufs. For serializing an entire - * upb_msg, see the serialization routines in upb_msg.h (which are layered on - * top of this). + * Implements a upb_sink that writes protobuf data to the binary wire format. * - * By default this interface does not "check your work." It doesn't pay - * attention to whether the lengths you give for submessages are correct, or - * whether your groups are properly balanced, or whether you give each value a - * tag of the appropriate type. In other words, it is quite possible (easy, - * even) to use this interface to emit invalid protobufs. We don't want to - * pay for the runtime checks. - * - * The best way to test that you're using the API correctly is to try to parse - * your output. + * For messages that have any submessages, the serializer needs a buffer + * containing the submessage sizes, so they can be properly written at the + * front of each message. Note that groups do *not* have this requirement. * * Copyright (c) 2009 Joshua Haberman. See LICENSE for details. */ @@ -22,186 +14,22 @@ #define UPB_SERIALIZE_H_ #include "upb.h" +#include "upb_sink.h" #ifdef __cplusplus extern "C" { #endif -/* Functions to write wire values. ********************************************/ - -/* Puts a varint (wire type: UPB_WIRE_TYPE_VARINT). */ -INLINE uint8_t *upb_put_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val, - struct upb_status *status) -{ - do { - uint8_t byte = val & 0x7f; - val >>= 7; - if(val) byte |= 0x80; - if(buf >= end) { - status->code = UPB_STATUS_NEED_MORE_DATA; - return end; - } - *buf++ = byte; - } while(val); - return buf; -} - -/* Puts an unsigned 32-bit varint, verbatim. Never uses the high 64 bits. */ -INLINE uint8_t *upb_put_v_uint32_t(uint8_t *buf, uint8_t *end, - uint32_t val, struct upb_status *status) -{ - return upb_put_v_uint64_t(buf, end, val, status); -} - -/* Puts a signed 32-bit varint, first sign-extending to 64-bits. We do this to - * maintain wire-compatibility with 64-bit signed integers. */ -INLINE uint8_t *upb_put_v_int32_t(uint8_t *buf, uint8_t *end, - int32_t val, struct upb_status *status) -{ - return upb_put_v_uint64_t(buf, end, (int64_t)val, status); -} - -INLINE void upb_put32(uint8_t *buf, uint32_t val) { - buf[0] = val & 0xff; - buf[1] = (val >> 8) & 0xff; - buf[2] = (val >> 16) & 0xff; - buf[3] = (val >> 24); -} - -/* Puts a fixed-length 32-bit integer (wire type: UPB_WIRE_TYPE_32BIT). */ -INLINE uint8_t *upb_put_f_uint32_t(uint8_t *buf, uint8_t *end, - uint32_t val, struct upb_status *status) -{ - uint8_t *uint32_end = buf + sizeof(uint32_t); - if(uint32_end > end) { - status->code = UPB_STATUS_NEED_MORE_DATA; - return end; - } -#if UPB_UNALIGNED_READS_OK - *(uint32_t*)buf = val; -#else - upb_put32(buf, val); -#endif - return uint32_end; -} - -/* Puts a fixed-length 64-bit integer (wire type: UPB_WIRE_TYPE_64BIT). */ -INLINE uint8_t *upb_put_f_uint64_t(uint8_t *buf, uint8_t *end, - uint64_t val, struct upb_status *status) -{ - uint8_t *uint64_end = buf + sizeof(uint64_t); - if(uint64_end > end) { - status->code = UPB_STATUS_NEED_MORE_DATA; - return end; - } -#if UPB_UNALIGNED_READS_OK - *(uint64_t*)buf = val; -#else - upb_put32(buf, (uint32_t)val); - upb_put32(buf, (uint32_t)(val >> 32)); -#endif - return uint64_end; -} - -INLINE size_t upb_v_uint64_t_size(uint64_t val) { -#ifdef __GNUC__ - int high_bit = 63 - __builtin_clzll(val); /* 0-based, undef if val == 0. */ -#else - int high_bit = 0; - uint64_t tmp = val; - while(tmp >>= 1) high_bit++; -#endif - return val == 0 ? 1 : high_bit / 7 + 1; -} - -INLINE size_t upb_v_int32_t_size(int32_t val) { - /* v_uint32's are sign-extended to maintain wire compatibility with int64s. */ - return upb_v_uint64_t_size((int64_t)val); -} -INLINE size_t upb_v_uint32_t_size(uint32_t val) { - return upb_v_uint64_t_size(val); -} -INLINE size_t upb_f_uint64_t_size(uint64_t val) { - (void)val; /* Length is independent of value. */ - return sizeof(uint64_t); -} -INLINE size_t upb_f_uint32_t_size(uint32_t val) { - (void)val; /* Length is independent of value. */ - return sizeof(uint32_t); -} - -/* Functions to write .proto values. ******************************************/ - -/* Performs zig-zag encoding, which is used by sint32 and sint64. */ -INLINE uint32_t upb_zzenc_32(int32_t n) { return (n << 1) ^ (n >> 31); } -INLINE uint64_t upb_zzenc_64(int64_t n) { return (n << 1) ^ (n >> 63); } - -/* Use macros to define a set of two functions for each .proto type: - * - * // Converts and writes a .proto value into buf. "end" indicates the end - * // of the current available buffer (if the buffer does not contain enough - * // space UPB_STATUS_NEED_MORE_DATA is returned). On success, *outbuf will - * // point one past the data that was written. - * uint8_t *upb_put_INT32(uint8_t *buf, uint8_t *end, int32_t val, - * struct upb_status *status); - * - * // Returns the number of bytes required to serialize val. - * size_t upb_get_INT32_size(int32_t val); - * - * // Given a .proto value s (source) convert it to a wire value. - * uint32_t upb_vtowv_INT32(int32_t s); - */ - -#define VTOWV(type, wire_t, val_t) \ - INLINE wire_t upb_vtowv_ ## type(val_t s) - -#define PUT(type, v_or_f, wire_t, val_t, member_name) \ - INLINE uint8_t *upb_put_ ## type(uint8_t *buf, uint8_t *end, val_t val, \ - struct upb_status *status) { \ - wire_t tmp = upb_vtowv_ ## type(val); \ - return upb_put_ ## v_or_f ## _ ## wire_t(buf, end, tmp, status); \ - } - -#define T(type, v_or_f, wire_t, val_t, member_name) \ - INLINE size_t upb_get_ ## type ## _size(val_t val) { \ - return upb_ ## v_or_f ## _ ## wire_t ## _size(val); \ - } \ - VTOWV(type, wire_t, val_t); /* prototype for PUT below */ \ - PUT(type, v_or_f, wire_t, val_t, member_name) \ - VTOWV(type, wire_t, val_t) +size_t upb_get_value_size(union upb_value v, struct upb_fielddef *f); -T(INT32, v, uint32_t, int32_t, int32) { return (uint32_t)s; } -T(INT64, v, uint64_t, int64_t, int64) { return (uint64_t)s; } -T(UINT32, v, uint32_t, uint32_t, uint32) { return s; } -T(UINT64, v, uint64_t, uint64_t, uint64) { return s; } -T(SINT32, v, uint32_t, int32_t, int32) { return upb_zzenc_32(s); } -T(SINT64, v, uint64_t, int64_t, int64) { return upb_zzenc_64(s); } -T(FIXED32, f, uint32_t, uint32_t, uint32) { return s; } -T(FIXED64, f, uint64_t, uint64_t, uint64) { return s; } -T(SFIXED32, f, uint32_t, int32_t, int32) { return (uint32_t)s; } -T(SFIXED64, f, uint64_t, int64_t, int64) { return (uint64_t)s; } -T(BOOL, v, uint32_t, bool, _bool) { return (uint32_t)s; } -T(ENUM, v, uint32_t, int32_t, int32) { return (uint32_t)s; } -T(DOUBLE, f, uint64_t, double, _double) { - union upb_value v; - v._double = s; - return v.uint64; -} -T(FLOAT, f, uint32_t, float, _float) { - union upb_value v; - v._float = s; - return v.uint32; -} -#undef VTOWV -#undef PUT -#undef T +struct upb_serializer; +typedef struct upb_serializer upb_serializer; -INLINE size_t upb_get_tag_size(uint32_t fieldnum) { - return upb_v_uint64_t_size((uint64_t)fieldnum << 3); -} +upb_serializer *upb_serializer_new(); +void upb_serializer_free(upb_serializer *s); -uint8_t *upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft, - union upb_value_ptr v, struct upb_status *status); +void upb_serializer_reset(upb_serializer *s, uint32_t *sizes); +upb_sink *upb_serializer_sink(upb_serializer *s); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/upb_sink.h b/src/upb_sink.h index 3400092..2055688 100644 --- a/src/upb_sink.h +++ b/src/upb_sink.h @@ -38,6 +38,8 @@ extern "C" { #endif +struct upb_fielddef; + // Each of the upb_sink callbacks returns a status of this type. typedef enum { // The normal case, where the consumer wants to continue consuming. -- cgit v1.2.3