summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Haberman <joshua@reverberate.org>2009-12-30 18:44:09 -0800
committerJoshua Haberman <joshua@reverberate.org>2009-12-30 18:44:09 -0800
commitf6cc21bc2daf657621fbc82e8ec3475eaee82d1f (patch)
treef26fc7777a6ea1f003f97f8d9938b0a5e515e211
parentab0f2b8bba2fa477d6a96e9e3c19b2d943b9f384 (diff)
Doesn't compile, but all the code to be adapted is there.
-rw-r--r--src/upb_data.c136
-rw-r--r--src/upb_data.h12
2 files changed, 147 insertions, 1 deletions
diff --git a/src/upb_data.c b/src/upb_data.c
index e77f194..595015f 100644
--- a/src/upb_data.c
+++ b/src/upb_data.c
@@ -185,6 +185,7 @@ upb_msg *upb_msg_new(struct upb_msgdef *md) {
upb_msg *msg = malloc(md->size);
memset(msg, 0, md->size);
data_init(&msg->base, UPB_DATA_HEAPALLOCATED | UPB_DATA_REFCOUNTED);
+ upb_def_ref(UPB_UPCAST(md));
return msg;
}
@@ -201,3 +202,138 @@ void _upb_msg_free(upb_msg *msg, struct upb_msgdef *md)
free(msg);
}
+
+/* Parsing. ******************************************************************/
+
+struct upb_msgparser_frame {
+ upb_msg *msg;
+ struct upb_msgdef *md;
+};
+
+struct upb_msgparser {
+ struct upb_cbparser *s;
+ bool merge;
+ struct upb_msgparser_frame stack[UPB_MAX_NESTING], *top;
+};
+
+/* Helper function that returns a pointer to where the next value for field "f"
+ * should be stored, taking into account whether f is an array that may need to
+ * be allocated or resized. */
+static union upb_value_ptr get_value_ptr(struct upb_msg *msg,
+ struct upb_fielddef *f)
+{
+ union upb_value_ptr p = upb_msg_getptr(msg, f);
+ if(upb_isarray(f)) {
+ if(!upb_msg_isset(msg, f)) {
+ if(!*p.arr || !upb_mmhead_only(&((*p.arr)->mmhead))) {
+ if(*p.arr)
+ upb_array_unref(*p.arr);
+ *p.arr = upb_array_new(f);
+ }
+ upb_array_truncate(*p.arr);
+ upb_msg_set(msg, f);
+ }
+ p = upb_array_append(*p.arr);
+ }
+ return p;
+}
+
+/* Callbacks for the stream parser. */
+
+static bool value_cb(void *udata, struct upb_msgdef *msgdef,
+ struct upb_fielddef *f, union upb_value val)
+{
+ (void)msgdef;
+ struct upb_msgparser *mp = udata;
+ struct upb_msg *msg = mp->top->msg;
+ union upb_value_ptr p = get_value_ptr(msg, f);
+ upb_msg_set(msg, f);
+ upb_value_write(p, val, f->type);
+ return true;
+}
+
+static bool str_cb(void *udata, struct upb_msgdef *msgdef,
+ struct upb_fielddef *f, uint8_t *str, size_t avail_len,
+ size_t total_len)
+{
+ (void)msgdef;
+ struct upb_msgparser *mp = udata;
+ struct upb_msg *msg = mp->top->msg;
+ union upb_value_ptr p = get_value_ptr(msg, f);
+ upb_msg_set(msg, f);
+ if(avail_len != total_len) abort(); /* TODO: support streaming. */
+ if(!*p.str || !upb_mmhead_only(&((*p.str)->mmhead))) {
+ if(*p.str)
+ upb_string_unref(*p.str);
+ *p.str = upb_string_new();
+ }
+ upb_string_resize(*p.str, total_len);
+ memcpy((*p.str)->ptr, str, avail_len);
+ (*p.str)->byte_len = avail_len;
+ return true;
+}
+
+static void start_cb(void *udata, struct upb_fielddef *f)
+{
+ struct upb_msgparser *mp = udata;
+ struct upb_msg *oldmsg = mp->top->msg;
+ union upb_value_ptr p = get_value_ptr(oldmsg, f);
+
+ if(upb_isarray(f) || !upb_msg_isset(oldmsg, f)) {
+ if(!*p.msg || !upb_mmhead_only(&((*p.msg)->mmhead))) {
+ if(*p.msg)
+ upb_msg_unref(*p.msg);
+ *p.msg = upb_msg_new(upb_downcast_msgdef(f->def));
+ }
+ upb_msg_clear(*p.msg);
+ upb_msg_set(oldmsg, f);
+ }
+
+ mp->top++;
+ mp->top->msg = *p.msg;
+}
+
+static void end_cb(void *udata)
+{
+ struct upb_msgparser *mp = udata;
+ mp->top--;
+}
+
+/* Externally-visible functions for the msg parser. */
+
+struct upb_msgparser *upb_msgparser_new(struct upb_msgdef *def)
+{
+ struct upb_msgparser *mp = malloc(sizeof(struct upb_msgparser));
+ mp->s = upb_cbparser_new(def, value_cb, str_cb, start_cb, end_cb);
+ return mp;
+}
+
+void upb_msgparser_reset(struct upb_msgparser *s, struct upb_msg *msg, bool byref)
+{
+ upb_cbparser_reset(s->s, s);
+ s->byref = byref;
+ s->top = s->stack;
+ s->top->msg = msg;
+}
+
+void upb_msgparser_free(struct upb_msgparser *s)
+{
+ upb_cbparser_free(s->s);
+ free(s);
+}
+
+void upb_msg_parsestr(struct upb_msg *msg, void *buf, size_t len,
+ struct upb_status *status)
+{
+ struct upb_msgparser *mp = upb_msgparser_new(msg->def);
+ upb_msgparser_reset(mp, msg, false);
+ upb_msg_clear(msg);
+ upb_msgparser_parse(mp, buf, len, status);
+ upb_msgparser_free(mp);
+}
+
+size_t upb_msgparser_parse(struct upb_msgparser *s, upb_string *str,
+ struct upb_status *status)
+{
+ return upb_cbparser_parse(s->s, str, status);
+}
diff --git a/src/upb_data.h b/src/upb_data.h
index ef80a40..b3bbb3c 100644
--- a/src/upb_data.h
+++ b/src/upb_data.h
@@ -446,7 +446,17 @@ void upb_msg_set(upb_msg *msg, struct upb_fielddef *f, union upb_value val);
void upb_msg_clear(upb_msg *msg, struct upb_msgdef *md);
-void upb_msg_parsestr(upb_msg *msg, struct upb_msgdef *md, upb_string *data,
+/* Parsing ********************************************************************/
+
+void upb_msg_parsestr(upb_msg *msg, struct upb_msgdef *md, upb_string *str,
struct upb_status *status);
+struct upb_msgparser *upb_msgparser_new(struct upb_msgdef *def);
+void upb_msgparser_free(struct upb_msgparser *mp);
+
+void upb_msgparser_reset(struct upb_msgparser *mp, upb_msg *m);
+
+size_t upb_msgparser_parse(struct upb_msgparser *mp, upb_string *str,
+ struct upb_status *status);
+
#endif
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback