/* * upb - a minimalist implementation of protocol buffers. * * Copyright (c) 2009 Google Inc. See LICENSE for details. * Author: Josh Haberman */ #include "upb_encoder.h" #include #include "descriptor.h" /* Functions for calculating sizes of wire values. ****************************/ static 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; } static 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); } static size_t upb_v_uint32_t_size(uint32_t val) { return upb_v_uint64_t_size(val); } static size_t upb_f_uint64_t_size(uint64_t val) { (void)val; // Length is independent of value. return sizeof(uint64_t); } static 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. ********************************************/ // Since we know in advance the longest that the value could be, we always make // sure that our buffer is long enough. This saves us from having to perform // bounds checks. // Puts a varint (wire type: UPB_WIRE_TYPE_VARINT). static uint8_t *upb_put_v_uint64_t(uint8_t *buf, uint64_t val) { do { uint8_t byte = val & 0x7f; val >>= 7; if(val) byte |= 0x80; *buf++ = byte; } while(val); return buf; } // Puts an unsigned 32-bit varint, verbatim. Never uses the high 64 bits. static uint8_t *upb_put_v_uint32_t(uint8_t *buf, uint32_t val) { return upb_put_v_uint64_t(buf, val); } // Puts a signed 32-bit varint, first sign-extending to 64-bits. We do this to // maintain wire-compatibility with 64-bit signed integers. static uint8_t *upb_put_v_int32_t(uint8_t *buf, int32_t val) { return upb_put_v_uint64_t(buf, (int64_t)val); } static 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). static uint8_t *upb_put_f_uint32_t(uint8_t *buf, uint32_t val) { uint8_t *uint32_end = buf + sizeof(uint32_t); #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). static uint8_t *upb_put_f_uint64_t(uint8_t *buf, uint64_t val) { uint8_t *uint64_end = buf + sizeof(uint64_t); #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 and calculate sizes for .proto values. ******************/ // Performs zig-zag encoding, which is used by sint32 and sint64. static uint32_t upb_zzenc_32(int32_t n) { return (n << 1) ^ (n >> 31); } static 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, int32_t val); * * // Returns the number of bytes required to encode 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) \ static wire_t upb_vtowv_ ## type(val_t s) #define PUT(type, v_or_f, wire_t, val_t, member_name) \ static uint8_t *upb_put_ ## type(uint8_t *buf, val_t val) { \ wire_t tmp = upb_vtowv_ ## type(val); \ return upb_put_ ## v_or_f ## _ ## wire_t(buf, tmp); \ } #define T(type, v_or_f, wire_t, val_t, member_name) \ static 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, int32_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) { upb_value v; v._double = s; return v.uint64; } T(FLOAT, f, uint32_t, float, _float) { upb_value v; v._float = s; return v.uint32; } #undef VTOWV #undef PUT #undef T static uint8_t *upb_encode_value(uint8_t *buf, upb_field_type_t ft, upb_value v) { #define CASE(t, member_name) \ case UPB_TYPE(t): return upb_put_ ## t(buf, v.member_name); switch(ft) { CASE(DOUBLE, _double) CASE(FLOAT, _float) CASE(INT32, int32) CASE(INT64, int64) CASE(UINT32, uint32) CASE(UINT64, uint64) CASE(SINT32, int32) CASE(SINT64, int64) CASE(FIXED32, uint32) CASE(FIXED64, uint64) CASE(SFIXED32, int32) CASE(SFIXED64, int64) CASE(BOOL, _bool) CASE(ENUM, int32) default: assert(false); return buf; } #undef CASE } static uint32_t _upb_get_value_size(upb_field_type_t ft, upb_value v) { #define CASE(t, member_name) \ case UPB_TYPE(t): return upb_get_ ## t ## _size(v.member_name); switch(ft) { CASE(DOUBLE, _double) CASE(FLOAT, _float) CASE(INT32, int32) CASE(INT64, int64) CASE(UINT32, uint32) CASE(UINT64, uint64) CASE(SINT32, int32) CASE(SINT64, int64) CASE(FIXED32, uint32) CASE(FIXED64, uint64) CASE(SFIXED32, int32) CASE(SFIXED64, int64) CASE(BOOL, _bool) CASE(ENUM, int32) default: assert(false); return 0; } #undef CASE } static uint8_t *_upb_put_tag(uint8_t *buf, upb_field_number_t num, upb_wire_type_t wt) { return upb_put_UINT32(buf, wt | (num << 3)); } static uint32_t _upb_get_tag_size(upb_field_number_t num) { return upb_get_UINT32_size(num << 3); } /* upb_sizebuilder ************************************************************/ struct upb_sizebuilder { // Accumulating size for the current level. uint32_t size; // Stack of sizes for our current nesting. uint32_t stack[UPB_MAX_NESTING], *top; // Vector of sizes. uint32_t *sizes; int sizes_len; int sizes_size; upb_status status; }; // upb_sink callbacks. static upb_sink_status _upb_sizebuilder_valuecb(upb_sink *sink, upb_fielddef *f, upb_value val, upb_status *status) { (void)status; upb_sizebuilder *sb = (upb_sizebuilder*)sink; uint32_t size = 0; size += _upb_get_tag_size(f->number); size += _upb_get_value_size(f->type, val); sb->size += size; return UPB_SINK_CONTINUE; } static upb_sink_status _upb_sizebuilder_strcb(upb_sink *sink, upb_fielddef *f, upb_strptr str, int32_t start, uint32_t end, upb_status *status) { (void)status; (void)str; // String data itself is not used. upb_sizebuilder *sb = (upb_sizebuilder*)sink; if(start >= 0) { uint32_t size = 0; size += _upb_get_tag_size(f->number); size += upb_get_UINT32_size(end - start); sb->size += size; } return UPB_SINK_CONTINUE; } static upb_sink_status _upb_sizebuilder_startcb(upb_sink *sink, upb_fielddef *f, upb_status *status) { (void)status; (void)f; // Unused (we calculate tag size and delimiter in endcb). upb_sizebuilder *sb = (upb_sizebuilder*)sink; if(f->type == UPB_TYPE(MESSAGE)) { *sb->top = sb->size; sb->top++; sb->size = 0; } else { assert(f->type == UPB_TYPE(GROUP)); sb->size += _upb_get_tag_size(f->number); } return UPB_SINK_CONTINUE; } static upb_sink_status _upb_sizebuilder_endcb(upb_sink *sink, upb_fielddef *f, upb_status *status) { (void)status; upb_sizebuilder *sb = (upb_sizebuilder*)sink; if(f->type == UPB_TYPE(MESSAGE)) { sb->top--; if(sb->sizes_len == sb->sizes_size) { sb->sizes_size *= 2; sb->sizes = realloc(sb->sizes, sb->sizes_size * sizeof(*sb->sizes)); } uint32_t child_size = sb->size; uint32_t parent_size = *sb->top; sb->sizes[sb->sizes_len++] = child_size; // The size according to the parent includes the tag size and delimiter of // the submessage. parent_size += upb_get_UINT32_size(child_size); parent_size += _upb_get_tag_size(f->number); // Include size accumulated in parent before child began. sb->size = child_size + parent_size; } else { assert(f->type == UPB_TYPE(GROUP)); // As an optimization, we could just add this number twice in startcb, to // avoid having to recalculate it. sb->size += _upb_get_tag_size(f->number); } return UPB_SINK_CONTINUE; } upb_sink_callbacks _upb_sizebuilder_sink_vtbl = { _upb_sizebuilder_valuecb, _upb_sizebuilder_strcb, _upb_sizebuilder_startcb, _upb_sizebuilder_endcb }; /* upb_sink callbacks *********************************************************/ struct upb_encoder { upb_sink base; //upb_bytesink *bytesink; uint32_t *sizes; int size_offset; }; // Within one callback we may need to encode up to two separate values. #define UPB_ENCODER_BUFSIZE (UPB_MAX_ENCODED_SIZE * 2) static upb_sink_status _upb_encoder_push_buf(upb_encoder *s, const uint8_t *buf, size_t len, upb_status *status) { // TODO: conjure a upb_strptr that points to buf. //upb_strptr ptr; (void)s; (void)buf; (void)status; size_t written = 5;// = upb_bytesink_onbytes(s->bytesink, ptr); if(written < len) { // TODO: mark to skip "written" bytes next time. return UPB_SINK_STOP; } else { return UPB_SINK_CONTINUE; } } static upb_sink_status _upb_encoder_valuecb(upb_sink *sink, upb_fielddef *f, upb_value val, upb_status *status) { upb_encoder *s = (upb_encoder*)sink; uint8_t buf[UPB_ENCODER_BUFSIZE], *ptr = buf; upb_wire_type_t wt = upb_types[f->type].expected_wire_type; // TODO: handle packed encoding. ptr = _upb_put_tag(ptr, f->number, wt); ptr = upb_encode_value(ptr, f->type, val); return _upb_encoder_push_buf(s, buf, ptr - buf, status); } static upb_sink_status _upb_encoder_strcb(upb_sink *sink, upb_fielddef *f, upb_strptr str, int32_t start, uint32_t end, upb_status *status) { upb_encoder *s = (upb_encoder*)sink; uint8_t buf[UPB_ENCODER_BUFSIZE], *ptr = buf; if(start >= 0) { ptr = _upb_put_tag(ptr, f->number, UPB_WIRE_TYPE_DELIMITED); ptr = upb_put_UINT32(ptr, end - start); } // TODO: properly handle partially consumed strings and partially supplied // strings. _upb_encoder_push_buf(s, buf, ptr - buf, status); return _upb_encoder_push_buf(s, (uint8_t*)upb_string_getrobuf(str), end - start, status); } static upb_sink_status _upb_encoder_startcb(upb_sink *sink, upb_fielddef *f, upb_status *status) { upb_encoder *s = (upb_encoder*)sink; uint8_t buf[UPB_ENCODER_BUFSIZE], *ptr = buf; if(f->type == UPB_TYPE(GROUP)) { ptr = _upb_put_tag(ptr, f->number, UPB_WIRE_TYPE_START_GROUP); } else { ptr = _upb_put_tag(ptr, f->number, UPB_WIRE_TYPE_DELIMITED); ptr = upb_put_UINT32(ptr, s->sizes[--s->size_offset]); } return _upb_encoder_push_buf(s, buf, ptr - buf, status); } static upb_sink_status _upb_encoder_endcb(upb_sink *sink, upb_fielddef *f, upb_status *status) { upb_encoder *s = (upb_encoder*)sink; uint8_t buf[UPB_ENCODER_BUFSIZE], *ptr = buf; if(f->type != UPB_TYPE(GROUP)) return UPB_SINK_CONTINUE; ptr = _upb_put_tag(ptr, f->number, UPB_WIRE_TYPE_END_GROUP); return _upb_encoder_push_buf(s, buf, ptr - buf, status); } upb_sink_callbacks _upb_encoder_sink_vtbl = { _upb_encoder_valuecb, _upb_encoder_strcb, _upb_encoder_startcb, _upb_encoder_endcb };