summaryrefslogtreecommitdiff
path: root/upb/sink.c
blob: f3af23d8f2f8784ba8eb476f681b034d03434833 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Copyright (c) 2011-2012 Google Inc.  See LICENSE for details.
 * Author: Josh Haberman <jhaberman@gmail.com>
 */

#include "upb/sink.h"

#include <stdlib.h>
#include <string.h>

static void upb_sink_init(upb_sink *s, const upb_handlers *h, upb_pipeline *p);
static void upb_sink_resetobj(void *obj);
static const upb_frametype upb_sink_frametype;

static bool chkstack(upb_sink *s) {
  if (s->top_ + 1 >= s->limit) {
    upb_status_seterrliteral(&s->pipeline_->status_, "Nesting too deep.");
    return false;
  } else {
    return true;
  }
}

#define alignof(type) offsetof (struct { char c; type member; }, member)

typedef union { double u; void *p; long l; } maxalign_t;
static const size_t maxalign = alignof(maxalign_t);

static void *align_up(void *p) {
  if (!p) return NULL;
  uintptr_t val = (uintptr_t)p;
  uintptr_t aligned =
      val % maxalign == 0 ? val : val + maxalign - (val % maxalign);
  return (void*)aligned;
}

void *upb_realloc(void *ud, void *ptr, size_t size) {
  UPB_UNUSED(ud);
  return realloc(ptr, size);
}


/* upb_pipeline ***************************************************************/

// For the moment we get fixed-size blocks of this size, but we could change
// this strategy if necessary.
#define BLOCK_SIZE 8192

struct region {
  struct region *prev;
  maxalign_t data[1];  // Region data follows.
};

size_t regionsize(size_t usable_size) {
  return sizeof(struct region) - sizeof(maxalign_t) + usable_size;
}

struct obj {
  struct obj *prev;
  const upb_frametype *ft;
  maxalign_t data;  // Region data follows.
};

size_t objsize(size_t memsize) {
  return sizeof(struct obj) - sizeof(maxalign_t) + memsize;
}

void upb_pipeline_init(upb_pipeline *p, void *initial_mem, size_t initial_size,
                       void *(*realloc)(void *ud, void *ptr, size_t bytes),
                       void *ud) {
  p->realloc = realloc;
  p->ud = ud;
  p->bump_top = initial_mem;
  p->bump_limit = initial_mem ? initial_mem + initial_size : NULL;
  p->region_head = NULL;
  p->obj_head = NULL;
  p->last_alloc = NULL;
  upb_status_init(&p->status_);
}

void upb_pipeline_uninit(upb_pipeline *p) {
  for (struct obj *o = p->obj_head; o; o = o->prev) {
    if (o->ft->uninit)
      o->ft->uninit(&o->data);
  }

  for (struct region *r = p->region_head; r; ) {
    struct region *prev = r->prev;
    p->realloc(p->ud, r, 0);
    r = prev;
  }
  upb_status_uninit(&p->status_);
}

void *upb_pipeline_alloc(upb_pipeline *p, size_t bytes) {
  void *mem = align_up(p->bump_top);
  if (!mem || mem > p->bump_limit || p->bump_limit - mem < bytes) {
    size_t size = regionsize(UPB_MAX(BLOCK_SIZE, bytes));
    struct region *r;
    if (!p->realloc || !(r = p->realloc(p->ud, NULL, size))) {
      return NULL;
    }
    r->prev = p->region_head;
    p->region_head = r;
    p->bump_limit = (char*)r + size;
    mem = &r->data[0];
    assert(p->bump_limit > mem);
    assert(p->bump_limit - mem >= bytes);
  }
  p->bump_top = mem + bytes;
  p->last_alloc = mem;
  return mem;
}

void *upb_pipeline_realloc(upb_pipeline *p, void *ptr,
                           size_t oldsize, size_t bytes) {
  if (ptr && ptr == p->last_alloc &&
      p->bump_limit - ptr >= bytes) {
    p->bump_top = ptr + bytes;
    return ptr;
  } else {
    void *mem = upb_pipeline_alloc(p, bytes);
    memcpy(mem, ptr, oldsize);
    return mem;
  }
}

void *upb_pipeline_allocobj(upb_pipeline *p, const upb_frametype *ft) {
  struct obj *obj = upb_pipeline_alloc(p, objsize(ft->size));
  if (!obj) return NULL;

  obj->prev = p->obj_head;
  obj->ft = ft;
  p->obj_head = obj;
  if (ft->init) ft->init(&obj->data);
  return &obj->data;
}

void upb_pipeline_reset(upb_pipeline *p) {
  upb_status_clear(&p->status_);
  for (struct obj *o = p->obj_head; o; o = o->prev) {
    if (o->ft->reset)
      o->ft->reset(&o->data);
  }
}

upb_sink *upb_pipeline_newsink(upb_pipeline *p, const upb_handlers *handlers) {
  upb_sink *s = upb_pipeline_allocobj(p, &upb_sink_frametype);
  upb_sink_init(s, handlers, p);
  return s;
}

const upb_status *upb_pipeline_status(const upb_pipeline *p) {
  return &p->status_;
}

typedef struct {
  const upb_handlers *h;
} handlersref_t;

static void freehandlersref(void *r) {
  handlersref_t *ref = r;
  upb_handlers_unref(ref->h, &ref->h);
}

static const upb_frametype handlersref_frametype = {
  sizeof(handlersref_t),
  NULL,
  freehandlersref,
  NULL,
};

void upb_pipeline_donateref(
    upb_pipeline *p, const upb_handlers *h, const void *owner) {
  handlersref_t *ref = upb_pipeline_allocobj(p, &handlersref_frametype);
  upb_handlers_donateref(h, owner, &ref->h);
  ref->h = h;
}


/* upb_sinkframe **************************************************************/

int upb_sinkframe_depth(const upb_sinkframe* frame) {
  return frame - frame->sink_->stack;
}

const upb_handlers* upb_sinkframe_handlers(const upb_sinkframe* frame) {
  return frame->h;
}

upb_pipeline *upb_sinkframe_pipeline(const upb_sinkframe* frame) {
  return frame->sink_->pipeline_;
}


/* upb_sink *******************************************************************/

static const upb_frametype upb_sink_frametype = {
  sizeof(upb_sink),
  NULL,
  NULL,
  upb_sink_resetobj,
};

void upb_sink_reset(upb_sink *s, void *closure) {
  s->top_ = s->stack;
  s->top_->closure = closure;
}

static void upb_sink_resetobj(void *obj) {
  upb_sink *s = obj;
  s->top_ = s->stack;
}

static void upb_sink_init(upb_sink *s, const upb_handlers *h, upb_pipeline *p) {
  s->pipeline_ = p;
  s->limit = &s->stack[UPB_MAX_NESTING];
  s->stack[0].h = h;
  s->top_ = s->stack;
  if (h->ft) {
    s->stack[0].closure = upb_pipeline_allocobj(p, h->ft);
  }
}

const upb_sinkframe *upb_sink_top(const upb_sink *s) {
  return s->top_;
}

const upb_sinkframe *upb_sink_base(const upb_sink *s) {
  return s->stack;
}

upb_pipeline *upb_sink_pipeline(const upb_sink *s) {
  return s->pipeline_;
}

bool upb_sink_startmsg(upb_sink *s) {
  const upb_handlers *h = s->top_->h;
  upb_startmsg_handler *startmsg = upb_handlers_getstartmsg(h);
  return startmsg ? startmsg(s->top_) : true;
}

void upb_sink_endmsg(upb_sink *s) {
  assert(s->top_ == s->stack);
  upb_endmsg_handler *endmsg = upb_handlers_getendmsg(s->top_->h);
  if (endmsg) {
    endmsg(s->top_, &s->pipeline_->status_);
  }
}

#define PUTVAL(type, ctype, htype) \
  bool upb_sink_put ## type(upb_sink *s, upb_selector_t sel, ctype val) { \
    const upb_handlers *h = s->top_->h; \
    upb_ ## type ## _handler *handler = (upb_ ## type ## _handler*) \
        upb_handlers_gethandler(h, sel); \
    if (handler) { \
      s->top_->u.handler_data = upb_handlers_gethandlerdata(h, sel); \
      bool ok = handler(s->top_, val); \
      if (!ok) return false; \
    } \
    return true; \
  }

PUTVAL(int32,  int32_t,         INT32);
PUTVAL(int64,  int64_t,         INT64);
PUTVAL(uint32, uint32_t,        UINT32);
PUTVAL(uint64, uint64_t,        UINT64);
PUTVAL(float,  float,           FLOAT);
PUTVAL(double, double,          DOUBLE);
PUTVAL(bool,   bool,            BOOL);
#undef PUTVAL

size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel,
                          const char *buf, size_t n) {
  const upb_handlers *h = s->top_->h;
  upb_string_handler *handler =
      (upb_string_handler*)upb_handlers_gethandler(h, sel);

  if (handler) {
    s->top_->u.handler_data = upb_handlers_gethandlerdata(h, sel);;
    n = handler(s->top_, buf, n);
  }

  return n;
}

bool upb_sink_startseq(upb_sink *s, upb_selector_t sel) {
  if (!chkstack(s)) return false;

  void *subc = s->top_->closure;
  const upb_handlers *h = s->top_->h;
  upb_startfield_handler *startseq =
      (upb_startfield_handler*)upb_handlers_gethandler(h, sel);

  if (startseq) {
    s->top_->u.handler_data = upb_handlers_gethandlerdata(h, sel);
    subc = startseq(s->top_);
    if (subc == UPB_BREAK) {
      return false;
    }
  }

  s->top_->u.selector = upb_getendselector(sel);
  ++s->top_;
  s->top_->h = h;
  s->top_->closure = subc;
  s->top_->sink_ = s;
  return true;
}

bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) {
  --s->top_;
  assert(sel == s->top_->u.selector);

  const upb_handlers *h = s->top_->h;
  upb_endfield_handler *endseq =
      (upb_endfield_handler*)upb_handlers_gethandler(h, sel);

  if (endseq) {
    bool ok = endseq(s->top_);
    if (!ok) {
      ++s->top_;
      return false;
    }
  }

  return true;
}

bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, size_t size_hint) {
  if (!chkstack(s)) return false;

  void *subc = s->top_->closure;
  const upb_handlers *h = s->top_->h;
  upb_startstr_handler *startstr =
      (upb_startstr_handler*)upb_handlers_gethandler(h, sel);

  if (startstr) {
    s->top_->u.handler_data = upb_handlers_gethandlerdata(h, sel);
    subc = startstr(s->top_, size_hint);
    if (subc == UPB_BREAK) {
      return false;
    }
  }

  s->top_->u.selector = upb_getendselector(sel);
  ++s->top_;
  s->top_->h = h;
  s->top_->closure = subc;
  s->top_->sink_ = s;
  return true;
}

bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) {
  --s->top_;
  assert(sel == s->top_->u.selector);
  const upb_handlers *h = s->top_->h;
  upb_endfield_handler *endstr =
      (upb_endfield_handler*)upb_handlers_gethandler(h, sel);

  if (endstr) {
    bool ok = endstr(s->top_);
    if (!ok) {
      ++s->top_;
      return false;
    }
  }

  return true;
}

bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel) {
  if (!chkstack(s)) return false;

  void *subc = s->top_->closure;
  const upb_handlers *h = s->top_->h;
  upb_startfield_handler *startsubmsg =
      (upb_startfield_handler*)upb_handlers_gethandler(h, sel);

  if (startsubmsg) {
    s->top_->u.handler_data = upb_handlers_gethandlerdata(h, sel);
    subc = startsubmsg(s->top_);
    if (subc == UPB_BREAK) {
      return false;
    }
  }

  s->top_->u.selector= upb_getendselector(sel);
  ++s->top_;
  s->top_->h = upb_handlers_getsubhandlers_sel(h, sel);
  // TODO: should add support for submessages without any handlers
  assert(s->top_->h);
  s->top_->closure = subc;
  s->top_->sink_ = s;
  upb_sink_startmsg(s);
  return true;
}

bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) {
  upb_endmsg_handler *endmsg = upb_handlers_getendmsg(s->top_->h);
  if (endmsg) endmsg(s->top_, &s->pipeline_->status_);
  --s->top_;

  assert(sel == s->top_->u.selector);
  const upb_handlers *h = s->top_->h;
  upb_endfield_handler *endsubmsg =
      (upb_endfield_handler*)upb_handlers_gethandler(h, sel);

  if (endsubmsg) {
    bool ok = endsubmsg(s->top_);
    if (!ok) {
      ++s->top_;
      return false;
    }
  }

  return true;
}

const upb_handlers *upb_sink_tophandlers(upb_sink *s) {
  return s->top_->h;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback