summaryrefslogtreecommitdiff
path: root/stream
diff options
context:
space:
mode:
Diffstat (limited to 'stream')
-rw-r--r--stream/upb_decoder.c429
-rw-r--r--stream/upb_decoder.h53
-rw-r--r--stream/upb_encoder.c420
-rw-r--r--stream/upb_encoder.h56
-rw-r--r--stream/upb_stdio.c104
-rw-r--r--stream/upb_stdio.h42
-rw-r--r--stream/upb_strstream.c71
-rw-r--r--stream/upb_strstream.h61
-rw-r--r--stream/upb_textprinter.c143
-rw-r--r--stream/upb_textprinter.h29
10 files changed, 1408 insertions, 0 deletions
diff --git a/stream/upb_decoder.c b/stream/upb_decoder.c
new file mode 100644
index 0000000..3a279a1
--- /dev/null
+++ b/stream/upb_decoder.c
@@ -0,0 +1,429 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2008-2009 Joshua Haberman. See LICENSE for details.
+ */
+
+#include "upb_decoder.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "upb_def.h"
+
+/* Pure Decoding **************************************************************/
+
+// The key fast-path varint-decoding routine. Here we can assume we have at
+// least UPB_MAX_VARINT_ENCODED_SIZE bytes available. There are a lot of
+// possibilities for optimization/experimentation here.
+INLINE bool upb_decode_varint_fast(const char **ptr, uint64_t *val,
+ upb_status *status) {
+ const char *p = *ptr;
+ uint32_t low, high = 0;
+ uint32_t b;
+ b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7f) << 28;
+ high = (b & 0x7f) >> 3; if(!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7f) << 4; if(!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7f) << 11; if(!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7f) << 18; if(!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7f) << 25; if(!(b & 0x80)) goto done;
+
+ upb_seterr(status, UPB_ERROR, "Unterminated varint");
+ return false;
+done:
+ *ptr = p;
+ *val = ((uint64_t)high << 32) | low;
+ return true;
+}
+
+
+/* Decoding/Buffering of individual values ************************************/
+
+// Performs zig-zag decoding, which is used by sint32 and sint64.
+INLINE int32_t upb_zzdec_32(uint32_t n) { return (n >> 1) ^ -(int32_t)(n & 1); }
+INLINE int64_t upb_zzdec_64(uint64_t n) { return (n >> 1) ^ -(int64_t)(n & 1); }
+
+// The decoder keeps a stack with one entry per level of recursion.
+// upb_decoder_frame is one frame of that stack.
+typedef struct {
+ upb_msgdef *msgdef;
+ size_t end_offset; // For groups, 0.
+} upb_decoder_frame;
+
+struct upb_decoder {
+ // Immutable state of the decoder.
+ upb_src src;
+ upb_dispatcher dispatcher;
+ upb_bytesrc *bytesrc;
+ upb_msgdef *toplevel_msgdef;
+ upb_decoder_frame stack[UPB_MAX_NESTING];
+
+ // Mutable state of the decoder.
+
+ // Where we will store any errors that occur.
+ upb_status *status;
+
+ // Stack entries store the offset where the submsg ends (for groups, 0).
+ upb_decoder_frame *top, *limit;
+
+ // Current input buffer.
+ upb_string *buf;
+
+ // The offset within the overall stream represented by the *beginning* of buf.
+ size_t buf_stream_offset;
+};
+
+typedef struct {
+ // Our current position in the data buffer.
+ const char *ptr;
+
+ // End of this submessage, relative to *ptr.
+ const char *submsg_end;
+
+ // Number of bytes available at ptr.
+ size_t len;
+
+ // Msgdef for the current level.
+ upb_msgdef *msgdef;
+} upb_dstate;
+
+// Constant used to signal that the submessage is a group and therefore we
+// don't know its end offset. This cannot be the offset of a real submessage
+// end because it takes at least one byte to begin a submessage.
+#define UPB_GROUP_END_OFFSET 0
+#define UPB_MAX_VARINT_ENCODED_SIZE 10
+
+INLINE void upb_dstate_advance(upb_dstate *s, size_t len) {
+ s->ptr += len;
+ s->len -= len;
+}
+
+INLINE void upb_dstate_setmsgend(upb_decoder *d, upb_dstate *s) {
+ s->submsg_end = (d->top->end_offset == UPB_GROUP_END_OFFSET) ?
+ (void*)UINTPTR_MAX :
+ upb_string_getrobuf(d->buf) + (d->top->end_offset - d->buf_stream_offset);
+}
+
+static upb_flow_t upb_pop(upb_decoder *d, upb_dstate *s);
+
+// Called only from the slow path, this function copies the next "len" bytes
+// from the stream to "data", adjusting the dstate appropriately.
+static bool upb_getbuf(upb_decoder *d, void *data, size_t bytes_wanted,
+ upb_dstate *s) {
+ while (1) {
+ size_t to_copy = UPB_MIN(bytes_wanted, s->len);
+ memcpy(data, s->ptr, to_copy);
+ upb_dstate_advance(s, to_copy);
+ bytes_wanted -= to_copy;
+ if (bytes_wanted == 0) {
+ upb_dstate_setmsgend(d, s);
+ return true;
+ }
+
+ // Get next buffer.
+ if (d->buf) d->buf_stream_offset += upb_string_len(d->buf);
+ upb_string_recycle(&d->buf);
+ if (!upb_bytesrc_getstr(d->bytesrc, d->buf, d->status)) return false;
+ s->ptr = upb_string_getrobuf(d->buf);
+ s->len = upb_string_len(d->buf);
+ }
+}
+
+// We use this path when we don't have UPB_MAX_VARINT_ENCODED_SIZE contiguous
+// bytes available in our current buffer. We don't inline this because we
+// accept that it will be slow and we don't want to pay for two copies of it.
+static bool upb_decode_varint_slow(upb_decoder *d, upb_dstate *s,
+ upb_value *val) {
+ char byte = 0x80;
+ uint64_t val64 = 0;
+ int bitpos;
+ for(bitpos = 0;
+ bitpos < 70 && (byte & 0x80) && upb_getbuf(d, &byte, 1, s);
+ bitpos += 7)
+ val64 |= ((uint64_t)byte & 0x7F) << bitpos;
+
+ if(bitpos == 70) {
+ upb_seterr(d->status, UPB_ERROR,
+ "Varint was unterminated after 10 bytes.\n");
+ return false;
+ } else if (d->status->code == UPB_EOF && bitpos == 0) {
+ // Regular EOF.
+ return false;
+ } else if (d->status->code == UPB_EOF && (byte & 0x80)) {
+ upb_seterr(d->status, UPB_ERROR,
+ "Provided data ended in the middle of a varint.\n");
+ return false;
+ } else {
+ // Success.
+ upb_value_setraw(val, val64);
+ return true;
+ }
+}
+
+typedef struct {
+ upb_wire_type_t wire_type;
+ upb_field_number_t field_number;
+} upb_tag;
+
+INLINE bool upb_decode_tag(upb_decoder *d, upb_dstate *s, upb_tag *tag) {
+ const char *p = s->ptr;
+ uint32_t tag_int;
+ upb_value val;
+ // Nearly all tag varints will be either 1 byte (1-16) or 2 bytes (17-2048).
+ if (s->len < 2) goto slow; // unlikely.
+ tag_int = *p & 0x7f;
+ if ((*(p++) & 0x80) == 0) goto done; // predictable if fields are in order
+ tag_int |= (*p & 0x7f) << 7;
+ if ((*(p++) & 0x80) == 0) goto done; // likely
+slow:
+ // Decode a full varint starting over from ptr.
+ if (!upb_decode_varint_slow(d, s, &val)) return false;
+ tag_int = upb_value_getint64(val);
+ p = s->ptr; // Trick the next line into not overwriting us.
+done:
+ upb_dstate_advance(s, p - s->ptr);
+ tag->wire_type = (upb_wire_type_t)(tag_int & 0x07);
+ tag->field_number = tag_int >> 3;
+ return true;
+}
+
+INLINE bool upb_decode_varint(upb_decoder *d, upb_dstate *s, upb_value *val) {
+ if (s->len >= UPB_MAX_VARINT_ENCODED_SIZE) {
+ // Common (fast) case.
+ uint64_t val64;
+ const char *p = s->ptr;
+ if (!upb_decode_varint_fast(&p, &val64, d->status)) return false;
+ upb_dstate_advance(s, p - s->ptr);
+ upb_value_setraw(val, val64);
+ return true;
+ } else {
+ return upb_decode_varint_slow(d, s, val);
+ }
+}
+
+INLINE bool upb_decode_fixed(upb_decoder *d, upb_wire_type_t wt,
+ upb_dstate *s, upb_value *val) {
+ static const char table[] = {0, 8, 0, 0, 0, 4};
+ size_t bytes = table[wt];
+ if (s->len >= bytes) {
+ // Common (fast) case.
+ memcpy(&val, s->ptr, bytes);
+ upb_dstate_advance(s, bytes);
+ } else {
+ if (!upb_getbuf(d, &val, bytes, s)) return false;
+ }
+ return true;
+}
+
+// "val" initially holds the length of the string, this is replaced by the
+// contents of the string.
+INLINE bool upb_decode_string(upb_decoder *d, upb_value *val, upb_string **str,
+ upb_dstate *s) {
+ upb_string_recycle(str);
+ uint32_t strlen = upb_value_getint32(*val);
+ if (s->len >= strlen) {
+ // Common (fast) case.
+ upb_string_substr(*str, d->buf, s->ptr - upb_string_getrobuf(d->buf), strlen);
+ upb_dstate_advance(s, strlen);
+ } else {
+ if (!upb_getbuf(d, upb_string_getrwbuf(*str, strlen), strlen, s))
+ return false;
+ }
+ upb_value_setstr(val, *str);
+ return true;
+}
+
+
+/* The main decoding loop *****************************************************/
+
+extern upb_wire_type_t upb_expected_wire_types[];
+// Returns true if wt is the correct on-the-wire type for ft.
+INLINE bool upb_check_type(upb_wire_type_t wt, upb_fieldtype_t ft) {
+ // This doesn't currently support packed arrays.
+ return upb_types[ft].native_wire_type == wt;
+}
+
+static upb_flow_t upb_push(upb_decoder *d, upb_dstate *s, upb_fielddef *f,
+ upb_value submsg_len, upb_fieldtype_t type) {
+ d->top++;
+ if(d->top >= d->limit) {
+ upb_seterr(d->status, UPB_ERROR, "Nesting too deep.");
+ return UPB_ERROR;
+ }
+ d->top->end_offset = (type == UPB_TYPE(GROUP)) ?
+ UPB_GROUP_END_OFFSET :
+ d->buf_stream_offset + (s->ptr - upb_string_getrobuf(d->buf)) +
+ upb_value_getint32(submsg_len);
+ d->top->msgdef = upb_downcast_msgdef(f->def);
+ upb_dstate_setmsgend(d, s);
+ return upb_dispatch_startsubmsg(&d->dispatcher, f);
+}
+
+static upb_flow_t upb_pop(upb_decoder *d, upb_dstate *s) {
+ d->top--;
+ upb_dstate_setmsgend(d, s);
+ return upb_dispatch_endsubmsg(&d->dispatcher);
+}
+
+void upb_decoder_run(upb_src *src, upb_status *status) {
+ upb_decoder *d = (upb_decoder*)src;
+ d->status = status;
+ // We put our dstate on the stack so the compiler knows they can't be changed
+ // by external code (like when we dispatch a callback). We must be sure not
+ // to let its address escape this source file.
+ upb_dstate state = {NULL, (void*)0x1, 0, d->top->msgdef};
+ upb_string *str = NULL;
+
+// TODO: handle UPB_SKIPSUBMSG
+#define CHECK_FLOW(expr) if ((expr) != UPB_CONTINUE) goto err
+#define CHECK(expr) if (!expr) goto err;
+
+ CHECK_FLOW(upb_dispatch_startmsg(&d->dispatcher));
+
+ // Main loop: executed once per tag/field pair.
+ while(1) {
+ // Check for end-of-submessage.
+ while (state.ptr >= state.submsg_end) {
+ if (state.ptr > state.submsg_end) {
+ upb_seterr(d->status, UPB_ERROR, "Bad submessage end.");
+ goto err;
+ }
+ CHECK_FLOW(upb_pop(d, &state));
+ }
+
+ // Parse/handle tag.
+ upb_tag tag;
+ if (!upb_decode_tag(d, &state, &tag)) {
+ if (status->code == UPB_EOF && d->top == d->stack) {
+ // Normal end-of-file.
+ upb_clearerr(status);
+ CHECK_FLOW(upb_dispatch_endmsg(&d->dispatcher));
+ upb_string_unref(str);
+ return;
+ } else {
+ if (status->code == UPB_EOF) {
+ upb_seterr(status, UPB_ERROR,
+ "Input ended in the middle of a submessage.");
+ }
+ goto err;
+ }
+ }
+
+ // Decode wire data. Hopefully this branch will predict pretty well
+ // since most types will read a varint here.
+ upb_value val;
+ switch (tag.wire_type) {
+ case UPB_WIRE_TYPE_START_GROUP:
+ break; // Nothing to do now, below we will push appropriately.
+ case UPB_WIRE_TYPE_END_GROUP:
+ if(d->top->end_offset != UPB_GROUP_END_OFFSET) {
+ upb_seterr(status, UPB_ERROR, "Unexpected END_GROUP tag.");
+ goto err;
+ }
+ CHECK_FLOW(upb_pop(d, &state));
+ continue; // We have no value to dispatch.
+ case UPB_WIRE_TYPE_VARINT:
+ case UPB_WIRE_TYPE_DELIMITED:
+ // For the delimited case we are parsing the length.
+ CHECK(upb_decode_varint(d, &state, &val));
+ break;
+ case UPB_WIRE_TYPE_32BIT:
+ case UPB_WIRE_TYPE_64BIT:
+ CHECK(upb_decode_fixed(d, tag.wire_type, &state, &val));
+ break;
+ }
+
+ // Look up field by tag number.
+ upb_fielddef *f = upb_msgdef_itof(d->top->msgdef, tag.field_number);
+
+ if (!f) {
+ if (tag.wire_type == UPB_WIRE_TYPE_DELIMITED)
+ CHECK(upb_decode_string(d, &val, &str, &state));
+ CHECK_FLOW(upb_dispatch_unknownval(&d->dispatcher, tag.field_number, val));
+ } else if (!upb_check_type(tag.wire_type, f->type)) {
+ // TODO: put more details in this error msg.
+ upb_seterr(status, UPB_ERROR, "Field had incorrect type.");
+ goto err;
+ }
+
+ // Perform any further massaging of the data now that we have the fielddef.
+ // Now we can distinguish strings from submessages, and we know about
+ // zig-zag-encoded types.
+ // TODO: handle packed encoding.
+ // TODO: if we were being paranoid, we could check for 32-bit-varint types
+ // that the top 32 bits all match the highest bit of the low 32 bits.
+ // If this is not true we are losing data. But the main protobuf library
+ // doesn't check this, and it would slow us down, so pass for now.
+ switch (f->type) {
+ case UPB_TYPE(MESSAGE):
+ case UPB_TYPE(GROUP):
+ CHECK_FLOW(upb_push(d, &state, f, val, f->type));
+ continue; // We have no value to dispatch.
+ case UPB_TYPE(STRING):
+ case UPB_TYPE(BYTES):
+ CHECK(upb_decode_string(d, &val, &str, &state));
+ break;
+ case UPB_TYPE(SINT32):
+ upb_value_setint32(&val, upb_zzdec_32(upb_value_getint32(val)));
+ break;
+ case UPB_TYPE(SINT64):
+ upb_value_setint64(&val, upb_zzdec_64(upb_value_getint64(val)));
+ break;
+ default:
+ break; // Other types need no further processing at this point.
+ }
+ CHECK_FLOW(upb_dispatch_value(&d->dispatcher, f, val));
+ }
+
+err:
+ upb_string_unref(str);
+ if (upb_ok(status)) {
+ upb_seterr(status, UPB_ERROR, "Callback returned UPB_BREAK");
+ }
+}
+
+void upb_decoder_sethandlers(upb_src *src, upb_handlers *handlers) {
+ upb_decoder *d = (upb_decoder*)src;
+ upb_dispatcher_reset(&d->dispatcher, handlers);
+ d->top = d->stack;
+ d->buf_stream_offset = 0;
+ d->top->msgdef = d->toplevel_msgdef;
+ // The top-level message is not delimited (we can keep receiving data for it
+ // indefinitely), so we treat it like a group.
+ d->top->end_offset = 0;
+}
+
+upb_decoder *upb_decoder_new(upb_msgdef *msgdef) {
+ static upb_src_vtbl vtbl = {
+ &upb_decoder_sethandlers,
+ &upb_decoder_run,
+ };
+ upb_decoder *d = malloc(sizeof(*d));
+ upb_src_init(&d->src, &vtbl);
+ upb_dispatcher_init(&d->dispatcher);
+ d->toplevel_msgdef = msgdef;
+ d->limit = &d->stack[UPB_MAX_NESTING];
+ d->buf = NULL;
+ return d;
+}
+
+void upb_decoder_reset(upb_decoder *d, upb_bytesrc *bytesrc) {
+ d->bytesrc = bytesrc;
+ d->top = &d->stack[0];
+ d->top->msgdef = d->toplevel_msgdef;
+ // Never want to end top-level message, so treat it like a group.
+ d->top->end_offset = UPB_GROUP_END_OFFSET;
+ upb_string_unref(d->buf);
+ d->buf = NULL;
+}
+
+void upb_decoder_free(upb_decoder *d) {
+ upb_string_unref(d->buf);
+ free(d);
+}
+
+upb_src *upb_decoder_src(upb_decoder *d) { return &d->src; }
diff --git a/stream/upb_decoder.h b/stream/upb_decoder.h
new file mode 100644
index 0000000..6ba4d77
--- /dev/null
+++ b/stream/upb_decoder.h
@@ -0,0 +1,53 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * upb_decoder implements a high performance, streaming decoder for protobuf
+ * data that works by implementing upb_src and getting its data from a
+ * upb_bytesrc.
+ *
+ * The decoder does not currently support non-blocking I/O, in the sense that
+ * if the bytesrc returns UPB_STATUS_TRYAGAIN it is not possible to resume the
+ * decoder when data becomes available again. Support for this could be added,
+ * but it would add complexity and perhaps cost efficiency also.
+ *
+ * Copyright (c) 2009-2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#ifndef UPB_DECODER_H_
+#define UPB_DECODER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "upb_def.h"
+#include "upb_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* upb_decoder *****************************************************************/
+
+// A upb_decoder decodes the binary protocol buffer format, writing the data it
+// decodes to a upb_sink.
+struct upb_decoder;
+typedef struct upb_decoder upb_decoder;
+
+// Allocates and frees a upb_decoder, respectively.
+upb_decoder *upb_decoder_new(upb_msgdef *md);
+void upb_decoder_free(upb_decoder *d);
+
+// Resets the internal state of an already-allocated decoder. This puts it in a
+// state where it has not seen any data, and expects the next data to be from
+// the beginning of a new protobuf. Parsers must be reset before they can be
+// used. A decoder can be reset multiple times.
+void upb_decoder_reset(upb_decoder *d, upb_bytesrc *bytesrc);
+
+// Returns a upb_src pointer by which the decoder can be used. The returned
+// upb_src is invalidated by upb_decoder_reset() or upb_decoder_free().
+upb_src *upb_decoder_src(upb_decoder *d);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_DECODER_H_ */
diff --git a/stream/upb_encoder.c b/stream/upb_encoder.c
new file mode 100644
index 0000000..304a423
--- /dev/null
+++ b/stream/upb_encoder.c
@@ -0,0 +1,420 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2009 Joshua Haberman. See LICENSE for details.
+ */
+
+#include "upb_encoder.h"
+
+#include <stdlib.h>
+#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
+};
+
diff --git a/stream/upb_encoder.h b/stream/upb_encoder.h
new file mode 100644
index 0000000..e879b0b
--- /dev/null
+++ b/stream/upb_encoder.h
@@ -0,0 +1,56 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Implements a upb_sink that writes protobuf data to the binary wire format.
+ *
+ * For messages that have any submessages, the encoder 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-2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#ifndef UPB_ENCODER_H_
+#define UPB_ENCODER_H_
+
+#include "upb.h"
+#include "upb_srcsink.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* upb_encoder ****************************************************************/
+
+// A upb_encoder is a upb_sink that emits data to a upb_bytesink in the protocol
+// buffer binary wire format.
+struct upb_encoder;
+typedef struct upb_encoder upb_encoder;
+
+upb_encoder *upb_encoder_new(upb_msgdef *md);
+void upb_encoder_free(upb_encoder *e);
+
+// Resets the given upb_encoder such that is is ready to begin encoding,
+// outputting data to "bytesink" (which must live until the encoder is
+// reset or destroyed).
+void upb_encoder_reset(upb_encoder *e, upb_bytesink *bytesink);
+
+// Returns the upb_sink to which data can be written. The sink is invalidated
+// when the encoder is reset or destroyed. Note that if the client wants to
+// encode any length-delimited submessages it must first call
+// upb_encoder_buildsizes() below.
+upb_sink *upb_encoder_sink(upb_encoder *e);
+
+// Call prior to pushing any data with embedded submessages. "src" must yield
+// exactly the same data as what will next be encoded, but in reverse order.
+// The encoder iterates over this data in order to determine the sizes of the
+// submessages. If any errors are returned by the upb_src, the status will
+// be saved in *status. If the client is sure that the upb_src will not throw
+// any errors, "status" may be NULL.
+void upb_encoder_buildsizes(upb_encoder *e, upb_src *src, upb_status *status);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_ENCODER_H_ */
diff --git a/stream/upb_stdio.c b/stream/upb_stdio.c
new file mode 100644
index 0000000..8857677
--- /dev/null
+++ b/stream/upb_stdio.c
@@ -0,0 +1,104 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#include "upb_stdio.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include "upb_string.h"
+
+// We can make this configurable if necessary.
+#define BLOCK_SIZE 4096
+
+struct upb_stdio {
+ upb_bytesrc bytesrc;
+ upb_bytesink bytesink;
+ FILE *file;
+};
+
+void upb_stdio_reset(upb_stdio *stdio, FILE* file) {
+ stdio->file = file;
+}
+
+
+/* upb_bytesrc methods ********************************************************/
+
+static upb_strlen_t upb_stdio_read(upb_bytesrc *src, void *buf,
+ upb_strlen_t count, upb_status *status) {
+ upb_stdio *stdio = (upb_stdio*)src;
+ assert(count > 0);
+ size_t read = fread(buf, 1, count, stdio->file);
+ if(read < (size_t)count) {
+ // Error or EOF.
+ if(feof(stdio->file)) {
+ upb_seterr(status, UPB_EOF, "");
+ return read;
+ } else if(ferror(stdio->file)) {
+ upb_seterr(status, UPB_ERROR, "Error reading from stdio stream.");
+ return -1;
+ }
+ }
+ return read;
+}
+
+static bool upb_stdio_getstr(upb_bytesrc *src, upb_string *str,
+ upb_status *status) {
+ upb_strlen_t read = upb_stdio_read(
+ src, upb_string_getrwbuf(str, BLOCK_SIZE), BLOCK_SIZE, status);
+ if (read <= 0) return false;
+ upb_string_getrwbuf(str, read);
+ return true;
+}
+
+
+/* upb_bytesink methods *******************************************************/
+
+upb_strlen_t upb_stdio_putstr(upb_bytesink *sink, upb_string *str, upb_status *status) {
+ upb_stdio *stdio = (upb_stdio*)((char*)sink - offsetof(upb_stdio, bytesink));
+ upb_strlen_t len = upb_string_len(str);
+ upb_strlen_t written = fwrite(upb_string_getrobuf(str), 1, len, stdio->file);
+ if(written < len) {
+ upb_seterr(status, UPB_ERROR, "Error writing to stdio stream.");
+ return -1;
+ }
+ return written;
+}
+
+upb_strlen_t upb_stdio_vprintf(upb_bytesink *sink, upb_status *status,
+ const char *fmt, va_list args) {
+ upb_stdio *stdio = (upb_stdio*)((char*)sink - offsetof(upb_stdio, bytesink));
+ upb_strlen_t written = vfprintf(stdio->file, fmt, args);
+ if (written < 0) {
+ upb_seterr(status, UPB_ERROR, "Error writing to stdio stream.");
+ return -1;
+ }
+ return written;
+}
+
+upb_stdio *upb_stdio_new() {
+ static upb_bytesrc_vtbl bytesrc_vtbl = {
+ upb_stdio_read,
+ upb_stdio_getstr,
+ };
+
+ static upb_bytesink_vtbl bytesink_vtbl = {
+ NULL,
+ upb_stdio_putstr,
+ upb_stdio_vprintf
+ };
+
+ upb_stdio *stdio = malloc(sizeof(*stdio));
+ upb_bytesrc_init(&stdio->bytesrc, &bytesrc_vtbl);
+ upb_bytesink_init(&stdio->bytesink, &bytesink_vtbl);
+ return stdio;
+}
+
+void upb_stdio_free(upb_stdio *stdio) {
+ free(stdio);
+}
+
+upb_bytesrc* upb_stdio_bytesrc(upb_stdio *stdio) { return &stdio->bytesrc; }
+upb_bytesink* upb_stdio_bytesink(upb_stdio *stdio) { return &stdio->bytesink; }
diff --git a/stream/upb_stdio.h b/stream/upb_stdio.h
new file mode 100644
index 0000000..fd71fdd
--- /dev/null
+++ b/stream/upb_stdio.h
@@ -0,0 +1,42 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * This file provides upb_bytesrc and upb_bytesink implementations for
+ * ANSI C stdio.
+ *
+ * Copyright (c) 2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#include <stdio.h>
+#include "upb_stream.h"
+
+#ifndef UPB_STDIO_H_
+#define UPB_STDIO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct upb_stdio;
+typedef struct upb_stdio upb_stdio;
+
+// Creation/deletion.
+upb_stdio *upb_stdio_new();
+void upb_stdio_free(upb_stdio *stdio);
+
+// Reset/initialize the object for use. The src or sink will call
+// fread()/fwrite()/etc. on the given FILE*.
+void upb_stdio_reset(upb_stdio *stdio, FILE* file);
+
+// Gets a bytesrc or bytesink for the given stdio. The returned pointer is
+// invalidated by upb_stdio_reset above. It is perfectly valid to get both
+// a bytesrc and a bytesink for the same stdio if the FILE* is open for reading
+// and writing.
+upb_bytesrc* upb_stdio_bytesrc(upb_stdio *stdio);
+upb_bytesink* upb_stdio_bytesink(upb_stdio *stdio);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/stream/upb_strstream.c b/stream/upb_strstream.c
new file mode 100644
index 0000000..d3fd4e0
--- /dev/null
+++ b/stream/upb_strstream.c
@@ -0,0 +1,71 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#include "upb_strstream.h"
+
+#include <stdlib.h>
+#include "upb_string.h"
+
+struct upb_stringsrc {
+ upb_bytesrc bytesrc;
+ upb_string *str;
+ upb_strlen_t offset;
+};
+
+void upb_stringsrc_reset(upb_stringsrc *s, upb_string *str) {
+ if (str != s->str) {
+ if (s->str) upb_string_unref(s->str);
+ s->str = upb_string_getref(str);
+ }
+ s->bytesrc.eof = false;
+}
+
+void upb_stringsrc_free(upb_stringsrc *s) {
+ if (s->str) upb_string_unref(s->str);
+ free(s);
+}
+
+static upb_strlen_t upb_stringsrc_read(upb_bytesrc *_src, void *buf,
+ upb_strlen_t count, upb_status *status) {
+ upb_stringsrc *src = (upb_stringsrc*)_src;
+ if (src->offset == upb_string_len(src->str)) {
+ upb_seterr(status, UPB_EOF, "");
+ return -1;
+ } else {
+ upb_strlen_t to_read = UPB_MIN(count, upb_string_len(src->str) - src->offset);
+ memcpy(buf, upb_string_getrobuf(src->str) + src->offset, to_read);
+ src->offset += to_read;
+ return to_read;
+ }
+}
+
+static bool upb_stringsrc_getstr(upb_bytesrc *_src, upb_string *str,
+ upb_status *status) {
+ upb_stringsrc *src = (upb_stringsrc*)_src;
+ if (src->offset == upb_string_len(str)) {
+ upb_seterr(status, UPB_EOF, "");
+ return false;
+ } else {
+ upb_string_substr(str, src->str, 0, upb_string_len(src->str));
+ return true;
+ }
+}
+
+upb_stringsrc *upb_stringsrc_new() {
+ static upb_bytesrc_vtbl bytesrc_vtbl = {
+ upb_stringsrc_read,
+ upb_stringsrc_getstr,
+ };
+
+ upb_stringsrc *s = malloc(sizeof(*s));
+ s->str = NULL;
+ upb_bytesrc_init(&s->bytesrc, &bytesrc_vtbl);
+ return s;
+}
+
+upb_bytesrc *upb_stringsrc_bytesrc(upb_stringsrc *s) {
+ return &s->bytesrc;
+}
diff --git a/stream/upb_strstream.h b/stream/upb_strstream.h
new file mode 100644
index 0000000..d01d21f
--- /dev/null
+++ b/stream/upb_strstream.h
@@ -0,0 +1,61 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * This file contains upb_bytesrc and upb_bytesink implementations for
+ * upb_string.
+ *
+ * Copyright (c) 2009-2010 Joshua Haberman. See LICENSE for details.
+ */
+
+#ifndef UPB_STRSTREAM_H
+#define UPB_STRSTREAM_H
+
+#include "upb_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* upb_stringsrc **************************************************************/
+
+struct upb_stringsrc;
+typedef struct upb_stringsrc upb_stringsrc;
+
+// Create/free a stringsrc.
+upb_stringsrc *upb_stringsrc_new();
+void upb_stringsrc_free(upb_stringsrc *s);
+
+// Resets the stringsrc to a state where it will vend the given string. The
+// stringsrc will take a reference on the string, so the caller need not ensure
+// that it outlives the stringsrc. A stringsrc can be reset multiple times.
+void upb_stringsrc_reset(upb_stringsrc *s, upb_string *str);
+
+// Returns the upb_bytesrc* for this stringsrc. Invalidated by reset above.
+upb_bytesrc *upb_stringsrc_bytesrc(upb_stringsrc *s);
+
+
+/* upb_stringsink *************************************************************/
+
+struct upb_stringsink;
+typedef struct upb_stringsink upb_stringsink;
+
+// Create/free a stringsrc.
+upb_stringsink *upb_stringsink_new();
+void upb_stringsink_free(upb_stringsink *s);
+
+// Gets a string containing the data that has been written to this stringsink.
+// The caller does *not* own any references to this string.
+upb_string *upb_stringsink_getstring(upb_stringsink *s);
+
+// Clears the internal string of accumulated data, resetting it to empty.
+void upb_stringsink_reset(upb_stringsink *s);
+
+// Returns the upb_bytesrc* for this stringsrc. Invalidated by reset above.
+upb_bytesink *upb_stringsrc_bytesink();
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/stream/upb_textprinter.c b/stream/upb_textprinter.c
new file mode 100644
index 0000000..894a1ea
--- /dev/null
+++ b/stream/upb_textprinter.c
@@ -0,0 +1,143 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2009 Joshua Haberman. See LICENSE for details.
+ */
+
+#include "upb_textprinter.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include "upb_def.h"
+#include "upb_string.h"
+
+struct _upb_textprinter {
+ upb_bytesink *bytesink;
+ int indent_depth;
+ bool single_line;
+ upb_status status;
+};
+
+#define CHECK(x) if ((x) < 0) goto err;
+
+static int upb_textprinter_indent(upb_textprinter *p)
+{
+ if(!p->single_line)
+ for(int i = 0; i < p->indent_depth; i++)
+ CHECK(upb_bytesink_putstr(p->bytesink, UPB_STRLIT(" "), &p->status));
+ return 0;
+err:
+ return -1;
+}
+
+static int upb_textprinter_endfield(upb_textprinter *p) {
+ if(p->single_line) {
+ CHECK(upb_bytesink_putstr(p->bytesink, UPB_STRLIT(" "), &p->status));
+ } else {
+ CHECK(upb_bytesink_putstr(p->bytesink, UPB_STRLIT("\n"), &p->status));
+ }
+ return 0;
+err:
+ return -1;
+}
+
+static upb_flow_t upb_textprinter_value(void *_p, upb_fielddef *f,
+ upb_value val) {
+ upb_textprinter *p = _p;
+ upb_textprinter_indent(p);
+ CHECK(upb_bytesink_printf(p->bytesink, &p->status, UPB_STRFMT ": ", UPB_STRARG(f->name)));
+#define CASE(fmtstr, member) \
+ CHECK(upb_bytesink_printf(p->bytesink, &p->status, fmtstr, upb_value_get ## member(val))); break;
+ switch(f->type) {
+ case UPB_TYPE(DOUBLE):
+ CASE("%0.f", double);
+ case UPB_TYPE(FLOAT):
+ CASE("%0.f", float)
+ case UPB_TYPE(INT64):
+ case UPB_TYPE(SFIXED64):
+ case UPB_TYPE(SINT64):
+ CASE("%" PRId64, int64)
+ case UPB_TYPE(UINT64):
+ case UPB_TYPE(FIXED64):
+ CASE("%" PRIu64, uint64)
+ case UPB_TYPE(UINT32):
+ case UPB_TYPE(FIXED32):
+ CASE("%" PRIu32, uint32);
+ case UPB_TYPE(ENUM): {
+ upb_enumdef *enum_def = upb_downcast_enumdef(f->def);
+ upb_string *enum_label =
+ upb_enumdef_iton(enum_def, upb_value_getint32(val));
+ if (enum_label) {
+ // We found a corresponding string for this enum. Otherwise we fall
+ // through to the int32 code path.
+ CHECK(upb_bytesink_putstr(p->bytesink, enum_label, &p->status));
+ break;
+ }
+ }
+ case UPB_TYPE(INT32):
+ case UPB_TYPE(SFIXED32):
+ case UPB_TYPE(SINT32):
+ CASE("%" PRId32, int32)
+ case UPB_TYPE(BOOL):
+ CASE("%hhu", bool);
+ case UPB_TYPE(STRING):
+ case UPB_TYPE(BYTES):
+ // TODO: escaping.
+ CHECK(upb_bytesink_putstr(p->bytesink, UPB_STRLIT("\""), &p->status));
+ CHECK(upb_bytesink_putstr(p->bytesink, upb_value_getstr(val), &p->status))
+ CHECK(upb_bytesink_putstr(p->bytesink, UPB_STRLIT("\""), &p->status));
+ break;
+ }
+ upb_textprinter_endfield(p);
+ return UPB_CONTINUE;
+err:
+ return UPB_BREAK;
+}
+
+static upb_flow_t upb_textprinter_startsubmsg(void *_p, upb_fielddef *f,
+ upb_handlers *delegate_to) {
+ (void)delegate_to;
+ upb_textprinter *p = _p;
+ upb_textprinter_indent(p);
+ CHECK(upb_bytesink_printf(p->bytesink, &p->status, UPB_STRFMT " {", UPB_STRARG(f->name)));
+ if(!p->single_line) upb_bytesink_putstr(p->bytesink, UPB_STRLIT("\n"), &p->status);
+ p->indent_depth++;
+ return UPB_CONTINUE;
+err:
+ return UPB_BREAK;
+}
+
+static upb_flow_t upb_textprinter_endsubmsg(void *_p)
+{
+ upb_textprinter *p = _p;
+ p->indent_depth--;
+ upb_textprinter_indent(p);
+ upb_bytesink_putstr(p->bytesink, UPB_STRLIT("}"), &p->status);
+ upb_textprinter_endfield(p);
+ return UPB_CONTINUE;
+}
+
+upb_textprinter *upb_textprinter_new() {
+ upb_textprinter *p = malloc(sizeof(*p));
+ return p;
+}
+
+void upb_textprinter_free(upb_textprinter *p) {
+ free(p);
+}
+
+void upb_textprinter_reset(upb_textprinter *p, upb_handlers *handlers,
+ upb_bytesink *sink, bool single_line) {
+ static upb_handlerset handlerset = {
+ NULL, // startmsg
+ NULL, // endmsg
+ upb_textprinter_value,
+ upb_textprinter_startsubmsg,
+ upb_textprinter_endsubmsg,
+ };
+ p->bytesink = sink;
+ p->single_line = single_line;
+ p->indent_depth = 0;
+ upb_register_handlerset(handlers, &handlerset);
+ upb_set_handler_closure(handlers, p, &p->status);
+}
diff --git a/stream/upb_textprinter.h b/stream/upb_textprinter.h
new file mode 100644
index 0000000..a880626
--- /dev/null
+++ b/stream/upb_textprinter.h
@@ -0,0 +1,29 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2009 Joshua Haberman. See LICENSE for details.
+ */
+
+#ifndef UPB_TEXT_H_
+#define UPB_TEXT_H_
+
+#include "upb_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _upb_textprinter;
+typedef struct _upb_textprinter upb_textprinter;
+
+upb_textprinter *upb_textprinter_new();
+void upb_textprinter_free(upb_textprinter *p);
+void upb_textprinter_reset(upb_textprinter *p, upb_handlers *handlers,
+ upb_bytesink *sink, bool single_line);
+void upb_textprinter_sethandlers(upb_textprinter *p, upb_handlers *h);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_TEXT_H_ */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback