Sketches of serialization.
diff --git a/Makefile b/Makefile
index 2ee0ff6..0b0d9da 100644
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,9 @@
 CXX=g++
 CFLAGS=-std=c99
 INCLUDE=-Idescriptor -Isrc -Itests -I.
-CPPFLAGS=-O3 -fomit-frame-pointer -Wall -Wextra -g -DUPB_UNALIGNED_READS_OK -DNDEBUG $(INCLUDE)
+CPPFLAGS=-O3 -fomit-frame-pointer -Wall -Wextra -g -DNDEBUG $(INCLUDE)
 OBJ=src/upb_parse.o src/upb_table.o src/upb_msg.o src/upb_enum.o src/upb_context.o \
-    src/upb_string.o src/upb_text.o descriptor/descriptor.o
+    src/upb_string.o src/upb_text.o src/upb_serialize.o descriptor/descriptor.o
 SRC=src/*.c src/*.h descriptor/*.c descriptor/*.h tests/*.c tests/*.h tools/*.c
 ALL=$(OBJ) src/libupb.a tests/test_table tests/tests tools/upbc benchmark/benchmark
 all: $(ALL)
diff --git a/src/upb_msg.c b/src/upb_msg.c
index 513c0c5..f74e96d 100644
--- a/src/upb_msg.c
+++ b/src/upb_msg.c
@@ -503,6 +503,74 @@
   return sizes->sizes[sizes->len-1];
 }
 
+struct upb_msg_serialize_state {
+  struct {
+    int field_iter;
+    int elem_iter;
+    struct upb_msg *m;
+    void *msg;
+  } stack[UPB_MAX_NESTING], *top, *limit;
+};
+
+void upb_msg_serialize_alloc(struct upb_msg_serialize_state *s)
+{
+  (void)s;
+}
+
+void upb_msg_serialize_free(struct upb_msg_serialize_state *s)
+{
+  (void)s;
+}
+
+void upb_msg_serialize_init(struct upb_msg_serialize_state *s, void *data,
+                            struct upb_msg *m, struct upb_msgsizes *sizes)
+{
+  (void)s;
+  (void)data;
+  (void)m;
+  (void)sizes;
+}
+
+static upb_status_t serialize_tag(uint8_t *buf, uint8_t *end,
+                                  struct upb_msg_field *f, uint8_t **outptr)
+{
+  /* TODO: need to have the field number also. */
+  UPB_CHECK(upb_put_UINT32(buf, end, f->type, outptr));
+  return UPB_STATUS_OK;
+}
+
+/* Serializes the next set of bytes into buf (which has size len).  Returns
+ * UPB_STATUS_OK if serialization is complete, or UPB_STATUS_NEED_MORE_DATA
+ * if there is more data from the message left to be serialized.
+ *
+ * The number of bytes written to buf is returned in *read.  This will be
+ * equal to len unless we finished serializing. */
+upb_status_t upb_msg_serialize(struct upb_msg_serialize_state *s,
+                               void *_buf, size_t len, size_t *written)
+{
+  uint8_t *buf = _buf;
+  uint8_t *end = buf + len;
+  uint8_t *const start = buf;
+  int i = s->top->field_iter;
+  int j = s->top->elem_iter;
+  void *msg = s->top->msg;
+  struct upb_msg *m = s->top->m;
+
+  while(buf < end) {
+    struct upb_msg_field *f = &m->fields[i];
+    union upb_value_ptr p = upb_msg_getptr(msg, f);
+    serialize_tag(buf, end, f, &buf);
+    if(f->type == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE) {
+    } else if(f->type == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP) {
+    } else if(upb_isstring(f)) {
+    } else {
+      upb_serialize_value(buf, end, f->type, p, &buf);
+    }
+  }
+  *written = buf - start;
+  return UPB_STATUS_OK;
+}
+
 /* Comparison.  ***************************************************************/
 
 bool upb_value_eql(union upb_value_ptr p1, union upb_value_ptr p2,
diff --git a/src/upb_msg.h b/src/upb_msg.h
index 8699fbf..043af23 100644
--- a/src/upb_msg.h
+++ b/src/upb_msg.h
@@ -398,10 +398,10 @@
  * UPB_STATUS_OK if serialization is complete, or UPB_STATUS_NEED_MORE_DATA
  * if there is more data from the message left to be serialized.
  *
- * The number of bytes written to buf is returned in *read.  This will be
+ * The number of bytes written to buf is returned in *written.  This will be
  * equal to len unless we finished serializing. */
 upb_status_t upb_msg_serialize(struct upb_msg_serialize_state *s,
-                               void *buf, size_t len, size_t *read);
+                               void *buf, size_t len, size_t *written);
 
 /* Text dump  *****************************************************************/
 
diff --git a/src/upb_parse.h b/src/upb_parse.h
index 193c307..e727c11 100644
--- a/src/upb_parse.h
+++ b/src/upb_parse.h
@@ -99,7 +99,9 @@
   upb_submsg_end_cb   submsg_end_cb;
 };
 
-/* Parses up to len bytes of protobuf data out of buf, calling cb as needed.
+/* Parses up to len bytes of protobuf data out of buf, calling the appropriate
+ * callbacks as values are parsed.
+ *
  * The function returns a status indicating the success of the operation.  Data
  * is parsed until no more data can be read from buf, or the callback returns an
  * error like UPB_STATUS_USER_CANCELLED, or an error occurs.
@@ -109,7 +111,10 @@
  * of the currently provided data.
  *
  * The next call to upb_parse must be the first byte after buf + *read, even in
- * the case that *read > len. */
+ * the case that *read > len.
+ *
+ * TODO: see if we can provide the following guarantee efficiently:
+ *   *read will always be >= len. */
 upb_status_t upb_parse(struct upb_parse_state *s, void *buf, size_t len,
                        size_t *read);
 
diff --git a/src/upb_serialize.h b/src/upb_serialize.h
index ec735c2..e1468de 100644
--- a/src/upb_serialize.h
+++ b/src/upb_serialize.h
@@ -170,7 +170,7 @@
 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_zzdec_64(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;     }
@@ -191,7 +191,7 @@
 #undef PUT
 #undef T
 
-size_t upb_get_tag_size(uint32_t fieldnum) {
+INLINE size_t upb_get_tag_size(uint32_t fieldnum) {
   return upb_v_uint64_t_size((uint64_t)fieldnum << 3);
 }