/* ** upb::Sink (upb_sink) ** upb::BytesSink (upb_bytessink) ** ** A upb_sink is an object that binds a upb_handlers object to some runtime ** state. It is the object that can actually receive data via the upb_handlers ** interface. ** ** Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or ** thread-safe. You can create as many of them as you want, but each one may ** only be used in a single thread at a time. ** ** If we compare with class-based OOP, a you can think of a upb_def as an ** abstract base class, a upb_handlers as a concrete derived class, and a ** upb_sink as an object (class instance). */ #ifndef UPB_SINK_H #define UPB_SINK_H #include "upb/handlers.h" #ifdef __cplusplus namespace upb { class BytesSink; class Sink; } #endif /* upb_sink *******************************************************************/ UPB_BEGIN_EXTERN_C typedef struct { const upb_handlers *handlers; void *closure; } upb_sink; #define PUTVAL(type, ctype) \ UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ ctype val) { \ typedef upb_##type##_handlerfunc functype; \ functype *func; \ const void *hd; \ if (!s->handlers) return true; \ func = (functype *)upb_handlers_gethandler(s->handlers, sel, &hd); \ if (!func) return true; \ return func(s->closure, hd, val); \ } PUTVAL(int32, int32_t) PUTVAL(int64, int64_t) PUTVAL(uint32, uint32_t) PUTVAL(uint64, uint64_t) PUTVAL(float, float) PUTVAL(double, double) PUTVAL(bool, bool) #undef PUTVAL UPB_INLINE void upb_sink_reset(upb_sink *s, const upb_handlers *h, void *c) { s->handlers = h; s->closure = c; } UPB_INLINE size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel, const char *buf, size_t n, const upb_bufhandle *handle) { typedef upb_string_handlerfunc func; func *handler; const void *hd; if (!s->handlers) return n; handler = (func *)upb_handlers_gethandler(s->handlers, sel, &hd); if (!handler) return n; return handler(s->closure, hd, buf, n, handle); } UPB_INLINE bool upb_sink_putunknown(upb_sink *s, const char *buf, size_t n) { typedef upb_unknown_handlerfunc func; func *handler; const void *hd; if (!s->handlers) return true; handler = (func *)upb_handlers_gethandler(s->handlers, UPB_UNKNOWN_SELECTOR, &hd); if (!handler) return n; return handler(s->closure, hd, buf, n); } UPB_INLINE bool upb_sink_startmsg(upb_sink *s) { typedef upb_startmsg_handlerfunc func; func *startmsg; const void *hd; if (!s->handlers) return true; startmsg = (func *)upb_handlers_gethandler(s->handlers, UPB_STARTMSG_SELECTOR, &hd); if (!startmsg) return true; return startmsg(s->closure, hd); } UPB_INLINE bool upb_sink_endmsg(upb_sink *s, upb_status *status) { typedef upb_endmsg_handlerfunc func; func *endmsg; const void *hd; if (!s->handlers) return true; endmsg = (func *)upb_handlers_gethandler(s->handlers, UPB_ENDMSG_SELECTOR, &hd); if (!endmsg) return true; return endmsg(s->closure, hd, status); } UPB_INLINE bool upb_sink_startseq(upb_sink *s, upb_selector_t sel, upb_sink *sub) { typedef upb_startfield_handlerfunc func; func *startseq; const void *hd; sub->closure = s->closure; sub->handlers = s->handlers; if (!s->handlers) return true; startseq = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!startseq) return true; sub->closure = startseq(s->closure, hd); return sub->closure ? true : false; } UPB_INLINE bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) { typedef upb_endfield_handlerfunc func; func *endseq; const void *hd; if (!s->handlers) return true; endseq = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!endseq) return true; return endseq(s->closure, hd); } UPB_INLINE bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, size_t size_hint, upb_sink *sub) { typedef upb_startstr_handlerfunc func; func *startstr; const void *hd; sub->closure = s->closure; sub->handlers = s->handlers; if (!s->handlers) return true; startstr = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!startstr) return true; sub->closure = startstr(s->closure, hd, size_hint); return sub->closure ? true : false; } UPB_INLINE bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) { typedef upb_endfield_handlerfunc func; func *endstr; const void *hd; if (!s->handlers) return true; endstr = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!endstr) return true; return endstr(s->closure, hd); } UPB_INLINE bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel, upb_sink *sub) { typedef upb_startfield_handlerfunc func; func *startsubmsg; const void *hd; sub->closure = s->closure; if (!s->handlers) { sub->handlers = NULL; return true; } sub->handlers = upb_handlers_getsubhandlers_sel(s->handlers, sel); startsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!startsubmsg) return true; sub->closure = startsubmsg(s->closure, hd); return sub->closure ? true : false; } UPB_INLINE bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) { typedef upb_endfield_handlerfunc func; func *endsubmsg; const void *hd; if (!s->handlers) return true; endsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel, &hd); if (!endsubmsg) return s->closure; return endsubmsg(s->closure, hd); } UPB_END_EXTERN_C #ifdef __cplusplus /* A upb::Sink is an object that binds a upb::Handlers object to some runtime * state. It represents an endpoint to which data can be sent. * * TODO(haberman): right now all of these functions take selectors. Should they * take selectorbase instead? * * ie. instead of calling: * sink->StartString(FOO_FIELD_START_STRING, ...) * a selector base would let you say: * sink->StartString(FOO_FIELD, ...) * * This would make call sites a little nicer and require emitting fewer selector * definitions in .h files. * * But the current scheme has the benefit that you can retrieve a function * pointer for any handler with handlers->GetHandler(selector), without having * to have a separate GetHandler() function for each handler type. The JIT * compiler uses this. To accommodate we'd have to expose a separate * GetHandler() for every handler type. * * Also to ponder: selectors right now are independent of a specific Handlers * instance. In other words, they allocate a number to every possible handler * that *could* be registered, without knowing anything about what handlers * *are* registered. That means that using selectors as table offsets prohibits * us from compacting the handler table at Freeze() time. If the table is very * sparse, this could be wasteful. * * Having another selector-like thing that is specific to a Handlers instance * would allow this compacting, but then it would be impossible to write code * ahead-of-time that can be bound to any Handlers instance at runtime. For * example, a .proto file parser written as straight C will not know what * Handlers it will be bound to, so when it calls sink->StartString() what * selector will it pass? It needs a selector like we have today, that is * independent of any particular upb::Handlers. * * Is there a way then to allow Handlers table compaction? */ class upb::Sink { public: /* Constructor with no initialization; must be Reset() before use. */ Sink() {} Sink(const Sink&) = default; Sink& operator=(const Sink&) = default; Sink(const upb_sink& sink) : sink_(sink) {} Sink &operator=(const upb_sink &sink) { sink_ = sink; return *this; } upb_sink sink() { return sink_; } /* Constructs a new sink for the given frozen handlers and closure. * * TODO: once the Handlers know the expected closure type, verify that T * matches it. */ template Sink(const upb_handlers* handlers, T* closure) { Reset(handlers, closure); } upb_sink* ptr() { return &sink_; } /* Resets the value of the sink. */ template void Reset(const upb_handlers* handlers, T* closure) { upb_sink_reset(&sink_, handlers, closure); } /* Returns the top-level object that is bound to this sink. * * TODO: once the Handlers know the expected closure type, verify that T * matches it. */ template T* GetObject() const { return static_cast(sink_.closure); } /* Functions for pushing data into the sink. * * These return false if processing should stop (either due to error or just * to suspend). * * These may not be called from within one of the same sink's handlers (in * other words, handlers are not re-entrant). */ /* Should be called at the start and end of every message; both the top-level * message and submessages. This means that submessages should use the * following sequence: * sink->StartSubMessage(startsubmsg_selector); * sink->StartMessage(); * // ... * sink->EndMessage(&status); * sink->EndSubMessage(endsubmsg_selector); */ bool StartMessage() { return upb_sink_startmsg(&sink_); } bool EndMessage(Status* status) { return upb_sink_endmsg(&sink_, status); } /* Putting of individual values. These work for both repeated and * non-repeated fields, but for repeated fields you must wrap them in * calls to StartSequence()/EndSequence(). */ bool PutInt32(HandlersPtr::Selector s, int32_t val) { return upb_sink_putint32(&sink_, s, val); } bool PutInt64(HandlersPtr::Selector s, int64_t val) { return upb_sink_putint64(&sink_, s, val); } bool PutUInt32(HandlersPtr::Selector s, uint32_t val) { return upb_sink_putuint32(&sink_, s, val); } bool PutUInt64(HandlersPtr::Selector s, uint64_t val) { return upb_sink_putuint64(&sink_, s, val); } bool PutFloat(HandlersPtr::Selector s, float val) { return upb_sink_putfloat(&sink_, s, val); } bool PutDouble(HandlersPtr::Selector s, double val) { return upb_sink_putdouble(&sink_, s, val); } bool PutBool(HandlersPtr::Selector s, bool val) { return upb_sink_putbool(&sink_, s, val); } /* Putting of string/bytes values. Each string can consist of zero or more * non-contiguous buffers of data. * * For StartString(), the function will write a sink for the string to "sub." * The sub-sink must be used for any/all PutStringBuffer() calls. */ bool StartString(HandlersPtr::Selector s, size_t size_hint, Sink* sub) { upb_sink sub_c; bool ret = upb_sink_startstr(&sink_, s, size_hint, &sub_c); *sub = sub_c; return ret; } size_t PutStringBuffer(HandlersPtr::Selector s, const char *buf, size_t len, const upb_bufhandle *handle) { return upb_sink_putstring(&sink_, s, buf, len, handle); } bool EndString(HandlersPtr::Selector s) { return upb_sink_endstr(&sink_, s); } /* For submessage fields. * * For StartSubMessage(), the function will write a sink for the string to * "sub." The sub-sink must be used for any/all handlers called within the * submessage. */ bool StartSubMessage(HandlersPtr::Selector s, Sink* sub) { upb_sink sub_c; bool ret = upb_sink_startsubmsg(&sink_, s, &sub_c); *sub = sub_c; return ret; } bool EndSubMessage(HandlersPtr::Selector s) { return upb_sink_endsubmsg(&sink_, s); } /* For repeated fields of any type, the sequence of values must be wrapped in * these calls. * * For StartSequence(), the function will write a sink for the string to * "sub." The sub-sink must be used for any/all handlers called within the * sequence. */ bool StartSequence(HandlersPtr::Selector s, Sink* sub) { upb_sink sub_c; bool ret = upb_sink_startseq(&sink_, s, &sub_c); *sub = sub_c; return ret; } bool EndSequence(HandlersPtr::Selector s) { return upb_sink_endseq(&sink_, s); } /* Copy and assign specifically allowed. * We don't even bother making these members private because so many * functions need them and this is mainly just a dumb data container anyway. */ private: upb_sink sink_; }; #endif /* __cplusplus */ /* upb_bytessink **************************************************************/ typedef struct { const upb_byteshandler *handler; void *closure; } upb_bytessink ; UPB_INLINE void upb_bytessink_reset(upb_bytessink *s, const upb_byteshandler *h, void *closure) { s->handler = h; s->closure = closure; } UPB_INLINE bool upb_bytessink_start(upb_bytessink *s, size_t size_hint, void **subc) { typedef upb_startstr_handlerfunc func; func *start; *subc = s->closure; if (!s->handler) return true; start = (func *)s->handler->table[UPB_STARTSTR_SELECTOR].func; if (!start) return true; *subc = start(s->closure, s->handler->table[UPB_STARTSTR_SELECTOR].attr.handler_data, size_hint); return *subc != NULL; } UPB_INLINE size_t upb_bytessink_putbuf(upb_bytessink *s, void *subc, const char *buf, size_t size, const upb_bufhandle* handle) { typedef upb_string_handlerfunc func; func *putbuf; if (!s->handler) return true; putbuf = (func *)s->handler->table[UPB_STRING_SELECTOR].func; if (!putbuf) return true; return putbuf(subc, s->handler->table[UPB_STRING_SELECTOR].attr.handler_data, buf, size, handle); } UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) { typedef upb_endfield_handlerfunc func; func *end; if (!s->handler) return true; end = (func *)s->handler->table[UPB_ENDSTR_SELECTOR].func; if (!end) return true; return end(s->closure, s->handler->table[UPB_ENDSTR_SELECTOR].attr.handler_data); } #ifdef __cplusplus class upb::BytesSink { public: BytesSink() {} BytesSink(const BytesSink&) = default; BytesSink& operator=(const BytesSink&) = default; BytesSink(const upb_bytessink& sink) : sink_(sink) {} BytesSink &operator=(const upb_bytessink &sink) { sink_ = sink; return *this; } upb_bytessink sink() { return sink_; } /* Constructs a new sink for the given frozen handlers and closure. * * TODO(haberman): once the Handlers know the expected closure type, verify * that T matches it. */ template BytesSink(const upb_byteshandler* handler, T* closure) { upb_bytessink_reset(&sink_, handler, closure); } /* Resets the value of the sink. */ template void Reset(const upb_byteshandler* handler, T* closure) { upb_bytessink_reset(&sink_, handler, closure); } bool Start(size_t size_hint, void **subc) { return upb_bytessink_start(&sink_, size_hint, subc); } size_t PutBuffer(void *subc, const char *buf, size_t len, const upb_bufhandle *handle) { return upb_bytessink_putbuf(&sink_, subc, buf, len, handle); } bool End() { return upb_bytessink_end(&sink_); } private: upb_bytessink sink_; }; #endif /* __cplusplus */ /* upb_bufsrc *****************************************************************/ UPB_BEGIN_EXTERN_C bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink sink); UPB_END_EXTERN_C #ifdef __cplusplus namespace upb { template bool PutBuffer(const T& str, BytesSink sink) { return upb_bufsrc_putbuf(str.c_str(), str.size(), sink.sink()); } } #endif /* __cplusplus */ /* upb_bufsink ****************************************************************/ /* A class for accumulating output string data in a flat buffer. */ struct upb_bufsink; typedef struct upb_bufsink upb_bufsink; UPB_BEGIN_EXTERN_C upb_bufsink *upb_bufsink_init(upb_env *env); void upb_bufsink_free(upb_bufsink *sink); upb_bytessink *upb_bufsink_sink(upb_bufsink *sink); const char *upb_bufsink_getdata(const upb_bufsink *sink, size_t *len); UPB_END_EXTERN_C #endif