summaryrefslogtreecommitdiff
path: root/src/upb_serialize.h
blob: 29c411ac1f52f21ad792c21fd4a0c5869dc998ae (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
/*
 * upb - a minimalist implementation of protocol buffers.
 *
 * Implements routines for serializing protobufs.  For serializing an entire
 * upb_msg, see the serialization routines in upb_msg.h (which are layered on
 * top of this).
 *
 * By default this interface does not "check your work."  It doesn't pay
 * attention to whether the lengths you give for submessages are correct, or
 * whether your groups are properly balanced, or whether you give each value a
 * tag of the appropriate type.  In other words, it is quite possible (easy,
 * even) to use this interface to emit invalid protobufs.  We don't want to
 * pay for the runtime checks.
 *
 * The best way to test that you're using the API correctly is to try to parse
 * your output.
 *
 * Copyright (c) 2009 Joshua Haberman.  See LICENSE for details.
 */

#ifndef UPB_SERIALIZE_H_
#define UPB_SERIALIZE_H_

#include "upb.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Functions to write wire values. ********************************************/

/* Puts a varint (wire type: UPB_WIRE_TYPE_VARINT). */
INLINE uint8_t *upb_put_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val,
                                   struct upb_status *status)
{
  do {
    uint8_t byte = val & 0x7f;
    val >>= 7;
    if(val) byte |= 0x80;
    if(buf >= end) {
      status->code = UPB_STATUS_NEED_MORE_DATA;
      return end;
    }
    *buf++ = byte;
  } while(val);
  return buf;
}

/* Puts an unsigned 32-bit varint, verbatim.  Never uses the high 64 bits. */
INLINE uint8_t *upb_put_v_uint32_t(uint8_t *buf, uint8_t *end,
                                   uint32_t val, struct upb_status *status)
{
  return upb_put_v_uint64_t(buf, end, val, status);
}

/* Puts a signed 32-bit varint, first sign-extending to 64-bits.  We do this to
 * maintain wire-compatibility with 64-bit signed integers. */
INLINE uint8_t *upb_put_v_int32_t(uint8_t *buf, uint8_t *end,
                                  int32_t val, struct upb_status *status)
{
  return upb_put_v_uint64_t(buf, end, (int64_t)val, status);
}

INLINE void upb_put32(uint8_t *buf, uint32_t val) {
  buf[0] = val & 0xff;
  buf[1] = (val >> 8) & 0xff;
  buf[2] = (val >> 16) & 0xff;
  buf[3] = (val >> 24);
}

/* Puts a fixed-length 32-bit integer (wire type: UPB_WIRE_TYPE_32BIT). */
INLINE uint8_t *upb_put_f_uint32_t(uint8_t *buf, uint8_t *end,
                                   uint32_t val, struct upb_status *status)
{
  uint8_t *uint32_end = buf + sizeof(uint32_t);
  if(uint32_end > end) {
    status->code = UPB_STATUS_NEED_MORE_DATA;
    return end;
  }
#if UPB_UNALIGNED_READS_OK
  *(uint32_t*)buf = val;
#else
  upb_put32(buf, val);
#endif
  return uint32_end;
}

/* Puts a fixed-length 64-bit integer (wire type: UPB_WIRE_TYPE_64BIT). */
INLINE uint8_t *upb_put_f_uint64_t(uint8_t *buf, uint8_t *end,
                                   uint64_t val, struct upb_status *status)
{
  uint8_t *uint64_end = buf + sizeof(uint64_t);
  if(uint64_end > end) {
    status->code = UPB_STATUS_NEED_MORE_DATA;
    return end;
  }
#if UPB_UNALIGNED_READS_OK
  *(uint64_t*)buf = val;
#else
  upb_put32(buf, (uint32_t)val);
  upb_put32(buf, (uint32_t)(val >> 32));
#endif
  return uint64_end;
}

INLINE size_t upb_v_uint64_t_size(uint64_t val) {
#ifdef __GNUC__
  int high_bit = 63 - __builtin_clzll(val);  /* 0-based, undef if val == 0. */
#else
  int high_bit = 0;
  uint64_t tmp = val;
  while(tmp >>= 1) high_bit++;
#endif
  return val == 0 ? 1 : high_bit / 7 + 1;
}

INLINE size_t upb_v_int32_t_size(int32_t val) {
  /* v_uint32's are sign-extended to maintain wire compatibility with int64s. */
  return upb_v_uint64_t_size((int64_t)val);
}
INLINE size_t upb_v_uint32_t_size(uint32_t val) {
  return upb_v_uint64_t_size(val);
}
INLINE size_t upb_f_uint64_t_size(uint64_t val) {
  (void)val;  /* Length is independent of value. */
  return sizeof(uint64_t);
}
INLINE size_t upb_f_uint32_t_size(uint32_t val) {
  (void)val;  /* Length is independent of value. */
  return sizeof(uint32_t);
}

/* Functions to write .proto values. ******************************************/

/* Performs zig-zag encoding, which is used by sint32 and sint64. */
INLINE uint32_t upb_zzenc_32(int32_t n) { return (n << 1) ^ (n >> 31); }
INLINE uint64_t upb_zzenc_64(int64_t n) { return (n << 1) ^ (n >> 63); }

/* Use macros to define a set of two functions for each .proto type:
 *
 *  // Converts and writes a .proto value into buf.  "end" indicates the end
 *  // of the current available buffer (if the buffer does not contain enough
 *  // space UPB_STATUS_NEED_MORE_DATA is returned).  On success, *outbuf will
 *  // point one past the data that was written.
 *  uint8_t *upb_put_INT32(uint8_t *buf, uint8_t *end, int32_t val,
 *                         struct upb_status *status);
 *
 *  // Returns the number of bytes required to serialize val.
 *  size_t upb_get_INT32_size(int32_t val);
 *
 *  // Given a .proto value s (source) convert it to a wire value.
 *  uint32_t upb_vtowv_INT32(int32_t s);
 */

#define VTOWV(type, wire_t, val_t) \
  INLINE wire_t upb_vtowv_ ## type(val_t s)

#define PUT(type, v_or_f, wire_t, val_t, member_name) \
  INLINE uint8_t *upb_put_ ## type(uint8_t *buf, uint8_t *end, val_t val, \
                                   struct upb_status *status) { \
    wire_t tmp = upb_vtowv_ ## type(val); \
    return upb_put_ ## v_or_f ## _ ## wire_t(buf, end, tmp, status); \
  }

#define T(type, v_or_f, wire_t, val_t, member_name) \
  INLINE size_t upb_get_ ## type ## _size(val_t val) { \
    return upb_ ## v_or_f ## _ ## wire_t ## _size(val); \
  } \
  VTOWV(type, wire_t, val_t);  /* prototype for PUT below */ \
  PUT(type, v_or_f, wire_t, val_t, member_name) \
  VTOWV(type, wire_t, val_t)

T(INT32,    v, uint32_t, int32_t,  int32)   { return (uint32_t)s;     }
T(INT64,    v, uint64_t, int64_t,  int64)   { return (uint64_t)s;     }
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_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;     }
T(SFIXED64, f, uint64_t, int64_t,  int64)   { return (uint64_t)s;     }
T(BOOL,     v, uint32_t, bool,     _bool)   { return (uint32_t)s;     }
T(ENUM,     v, uint32_t, int32_t,  int32)   { return (uint32_t)s;     }
T(DOUBLE,   f, uint64_t, double,   _double) {
  union upb_value v;
  v._double = s;
  return v.uint64;
}
T(FLOAT,    f, uint32_t, float,    _float)  {
  union upb_value v;
  v._float = s;
  return v.uint32;
}
#undef VTOWV
#undef PUT
#undef T

INLINE size_t upb_get_tag_size(uint32_t fieldnum) {
  return upb_v_uint64_t_size((uint64_t)fieldnum << 3);
}

uint8_t *upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
                             union upb_value_ptr v, struct upb_status *status);

#ifdef __cplusplus
}  /* extern "C" */
#endif

#endif  /* UPB_SERIALIZE_H_ */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback