|
|
|
/*
|
|
|
|
** 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 *******************************************************************/
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
|
|
|
|
/* 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 <class T> Sink(const upb_handlers* handlers, T* closure) {
|
|
|
|
Reset(handlers, closure);
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_sink* ptr() { return &sink_; }
|
|
|
|
|
|
|
|
/* Resets the value of the sink. */
|
|
|
|
template <class T> 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 <class T> T* GetObject() const {
|
|
|
|
return static_cast<T*>(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(upb_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 <class T> BytesSink(const upb_byteshandler* handler, T* closure) {
|
|
|
|
upb_bytessink_reset(sink_, handler, closure);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resets the value of the sink. */
|
|
|
|
template <class T> 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 *****************************************************************/
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink sink);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
|
|
|
|
namespace upb {
|
|
|
|
template <class T> bool PutBuffer(const T& str, BytesSink sink) {
|
|
|
|
return upb_bufsrc_putbuf(str.c_str(), str.size(), sink.sink());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
|
|
#endif
|