/*- * Copyright (c) 2015--2018 Taylor R. Campbell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(__NetBSD__) && defined(_KERNEL) #include #include #include #else #include #include #include #include #include #endif #include "pb.h" #include "pb_encode.h" #define PB_ABI_VERSION 0x00000000 /* * Forward declarations */ struct encode { pb_encoder_callback_t *callback; void *arg; }; static int pb_encode_buf(struct encode *, const void *, size_t); static int pb_encode_by_hdr(struct encode *, const struct pb_msg_hdr *); static int pb_encode_field(struct encode *, const struct pb_field *, const unsigned char *); static int pb_encode_field_value(struct encode *, const struct pb_field *, const unsigned char *); static uint64_t pb_compose_tag(uint32_t, enum pb_wiretype); static size_t pb_format_varint(uint64_t, uint8_t *); static size_t pb_format_tag(uint32_t, enum pb_wiretype, uint8_t *); static int pb_encode_tagged_varint_u(struct encode *, uint32_t, uint64_t); static int pb_encode_tagged_fixed32(struct encode *, uint32_t, uint32_t); static int pb_encode_tagged_fixed64(struct encode *, uint32_t, uint64_t); static int pb_encode_tagged_varint_s(struct encode *, uint32_t, int64_t); static int pb_encode_tagged_zigzag(struct encode *, uint32_t, int64_t); static int pb_encode_tagged_sfixed32(struct encode *, uint32_t, int32_t); static int pb_encode_tagged_sfixed64(struct encode *, uint32_t, int64_t); static int pb_encode_tagged_ieee32(struct encode *, uint32_t, float); static int pb_encode_tagged_ieee64(struct encode *, uint32_t, double); static int pb_encode_tagged_length(struct encode *, uint32_t, size_t); static int pb_encode_submsg(struct encode *, const struct pb_msg_hdr *, size_t); static int pb_size_by_hdr(size_t *, struct pb_msg_hdr *); static int pb_size_field(size_t *, const struct pb_field *, unsigned char *); static int pb_size_field_value(size_t *, const struct pb_field *, unsigned char *); static int pb_size_tagged_varint_u(size_t *, uint32_t, uint64_t); static int pb_size_tagged_fixed32(size_t *, uint32_t, uint32_t); static int pb_size_tagged_fixed64(size_t *, uint32_t, uint64_t); static int pb_size_tagged_varint_s(size_t *, uint32_t, int64_t); static int pb_size_tagged_zigzag(size_t *, uint32_t, int64_t); static int pb_size_tagged_sfixed32(size_t *, uint32_t, int32_t); static int pb_size_tagged_sfixed64(size_t *, uint32_t, int64_t); static int pb_size_tagged_ieee32(size_t *, uint32_t, float); static int pb_size_tagged_ieee64(size_t *, uint32_t, double); static int pb_size_tagged_length(size_t *, uint32_t, size_t); struct encode_memory { uint8_t *ptr; size_t nleft; }; static pb_encoder_callback_t encode_memory_callback; static int encode_memory_callback(void *cookie, const void *buf, size_t len) { struct encode_memory *const M = cookie; if (len > M->nleft) return EINVAL; (void)memcpy(M->ptr, buf, len); M->ptr += len; M->nleft -= len; return 0; } int pb_encode_to_memory(struct pb_msg msg, void *buf, size_t len) { struct encode_memory M = { .ptr = buf, .nleft = len }; return pb_encode(msg, &encode_memory_callback, &M); } int pb_encode(struct pb_msg msg, pb_encoder_callback_t *callback, void *arg) { const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)msg.pbm_ptr; struct encode E = { .callback = callback, .arg = arg, }; if (!pb_abi_compatible(msg.pbm_msgdesc->pbmd_abi_version, PB_ABI_VERSION)) /* XXX pb_bug */ return EINVAL; if (msg_hdr->pbmh_msgdesc != msg.pbm_msgdesc) /* XXX pb_bug */ return EINVAL; return pb_encode_by_hdr(&E, msg_hdr); } static int pb_attr_noinline pb_encode_buf(struct encode *E, const void *buf, size_t size) { return (*E->callback)(E->arg, buf, size); } static int pb_encode_by_hdr(struct encode *E, const struct pb_msg_hdr *msg_hdr) { const unsigned char *const addr = (const void *)msg_hdr; const struct pb_msgdesc *const msgdesc = msg_hdr->pbmh_msgdesc; size_t i; int error; for (i = 0; i < msgdesc->pbmd_nfields; i++) { error = pb_encode_field(E, &msgdesc->pbmd_fields[i], addr); if (error) return error; } return 0; } static int pb_encode_field(struct encode *E, const struct pb_field *field, const unsigned char *addr) { int error; switch (field->pbf_quant) { case PBQ_REQUIRED: return pb_encode_field_value(E, field, (addr + field->pbf_qu.required.offset)); case PBQ_OPTIONAL: if (*(const bool *)(addr + field->pbf_qu.optional.present_offset)) return pb_encode_field_value(E, field, (addr + field->pbf_qu.optional.value_offset)); return 0; case PBQ_REPEATED: { const struct pb_repeated *const repeated = (const struct pb_repeated *)(addr + field->pbf_qu.repeated.hdr_offset); const unsigned char *const ptr = /* XXX Sketchy pointer cast. */ *(const void *const *)(addr + field->pbf_qu.repeated.ptr_offset); const size_t elemsize = pb_type_size(&field->pbf_type); size_t i; /* XXX Handle packed repeated fields. */ for (i = 0; i < pb_repeated_count(repeated); i++) { error = pb_encode_field_value(E, field, (ptr + (i * elemsize))); if (error) return error; } return 0; } default: pb_bug("field %s: invalid quantifier: %d", field->pbf_name, (int)field->pbf_quant); } } static int pb_encode_field_value(struct encode *E, const struct pb_field *field, const unsigned char *v) { const uint32_t t = field->pbf_tag; int error; switch (field->pbf_type.pbt_type) { case PB_TYPE_BOOL: return pb_encode_tagged_varint_u(E, t, *(const bool *)v); case PB_TYPE_UINT32: return pb_encode_tagged_varint_u(E, t, *(const uint32_t *)v); case PB_TYPE_UINT64: return pb_encode_tagged_varint_u(E, t, *(const uint64_t *)v); case PB_TYPE_FIXED32: return pb_encode_tagged_fixed32(E, t, *(const uint32_t *)v); case PB_TYPE_FIXED64: return pb_encode_tagged_fixed64(E, t, *(const uint64_t *)v); case PB_TYPE_INT32: return pb_encode_tagged_varint_s(E, t, *(const int32_t *)v); case PB_TYPE_INT64: return pb_encode_tagged_varint_s(E, t, *(const int64_t *)v); case PB_TYPE_SINT32: return pb_encode_tagged_zigzag(E, t, *(const int32_t *)v); case PB_TYPE_SINT64: return pb_encode_tagged_zigzag(E, t, *(const int64_t *)v); case PB_TYPE_SFIXED32: return pb_encode_tagged_sfixed32(E, t, *(const int32_t *)v); case PB_TYPE_SFIXED64: return pb_encode_tagged_sfixed64(E, t, *(const int64_t *)v); case PB_TYPE_ENUM: return pb_encode_tagged_varint_s(E, t, *(const int32_t *)v); case PB_TYPE_FLOAT: return pb_encode_tagged_ieee32(E, t, *(const float *)v); case PB_TYPE_DOUBLE: return pb_encode_tagged_ieee64(E, t, *(const double *)v); case PB_TYPE_BYTES: { const struct pb_bytes *const bytes = (const struct pb_bytes *)v; size_t size; const uint8_t *const ptr = pb_bytes_ptr(bytes, &size); error = pb_encode_tagged_length(E, t, size); if (error) return error; pb_assert(size == 0 || ptr != NULL); return (size? pb_encode_buf(E, ptr, size) : 0); } case PB_TYPE_STRING: { const struct pb_string *const string = (const struct pb_string *)v; const char *const ptr = pb_string_ptr(string); const size_t len = pb_string_len(string); error = pb_encode_tagged_length(E, t, len); if (error) return error; return pb_encode_buf(E, ptr, len); } case PB_TYPE_MSG: { const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)v; size_t size; size = msg_hdr->pbmh_cached_size; if (size == ~(size_t)0) /* Caller failed to precompute encoding size. */ return EINVAL; error = pb_encode_tagged_length(E, t, size); if (error) return error; return pb_encode_submsg(E, msg_hdr, size); } default: pb_bug("field %s: invalid type: %d", field->pbf_name, field->pbf_type.pbt_type); } } /* * Tags and varints */ #define PB_VARINT_BUFSIZE 10 PB_CTASSERT(PB_VARINT_BUFSIZE == ((sizeof(uint64_t) * 8) + 6)/7); static uint64_t pb_compose_tag(uint32_t tag, enum pb_wiretype wiretype) { return (((uint64_t)tag << 3) | (uint64_t)wiretype); } static size_t pb_format_varint(uint64_t u, uint8_t buf[PB_VARINT_BUFSIZE]) { uint8_t *p = buf; do { pb_assert(p < &buf[PB_VARINT_BUFSIZE]); *p++ = ((u < 128? 0 : 0x80) | (u & 0x7f)); u >>= 7; } while (0 < u); pb_assert(buf < p); return (p - buf); } #define PB_TAG_BUFSIZE PB_VARINT_BUFSIZE static size_t pb_format_tag(uint32_t tag, enum pb_wiretype wiretype, uint8_t *buf) { return pb_format_varint(pb_compose_tag(tag, wiretype), buf); } /* * Unsigned integer formats */ static int pb_encode_tagged_varint_u(struct encode *E, uint32_t tag, uint64_t u) { uint8_t buf[PB_TAG_BUFSIZE + PB_VARINT_BUFSIZE]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_VARINT, &buf[n]); n += pb_format_varint(u, &buf[n]); return pb_encode_buf(E, buf, n); } static int pb_encode_tagged_fixed32(struct encode *E, uint32_t tag, uint32_t u) { uint8_t buf[PB_TAG_BUFSIZE + 4]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_32BIT, &buf[n]); buf[n++] = u; buf[n++] = u >> 8; buf[n++] = u >> 16; buf[n++] = u >> 24; return pb_encode_buf(E, buf, n); } static int pb_encode_tagged_fixed64(struct encode *E, uint32_t tag, uint64_t u) { uint8_t buf[PB_TAG_BUFSIZE + 8]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_64BIT, &buf[n]); buf[n++] = u; buf[n++] = u >> 8; buf[n++] = u >> 16; buf[n++] = u >> 24; buf[n++] = u >> 32; buf[n++] = u >> 40; buf[n++] = u >> 48; buf[n++] = u >> 56; return pb_encode_buf(E, buf, n); } /* * Signed integer formats * * XXX These assume two's-complement arithmetic. */ static int pb_encode_tagged_varint_s(struct encode *E, uint32_t tag, int64_t s) { return pb_encode_tagged_varint_u(E, tag, (uint64_t)s); } static int pb_encode_tagged_zigzag(struct encode *E, uint32_t tag, int64_t s) { return pb_encode_tagged_varint_u(E, tag, (uint64_t)((s << 1) ^ (s >> 63))); } static int pb_encode_tagged_sfixed32(struct encode *E, uint32_t tag, int32_t s) { return pb_encode_tagged_fixed32(E, tag, (uint64_t)s); } static int pb_encode_tagged_sfixed64(struct encode *E, uint32_t tag, int64_t s) { return pb_encode_tagged_fixed64(E, tag, (uint64_t)s); } /* * Floating-point formats * * XXX These assume IEEE 754 floating-point representation with the * same byte order as integers. */ static int pb_encode_tagged_ieee32(struct encode *E, uint32_t tag, float f) { const union { float f; uint32_t i; } u = { .f = f }; return pb_encode_tagged_fixed32(E, tag, u.i); } static int pb_encode_tagged_ieee64(struct encode *E, uint32_t tag, double d) { const union { double d; uint32_t i; } u = { .d = d }; return pb_encode_tagged_fixed64(E, tag, u.i); } /* * Length-delimited fields */ static int pb_encode_tagged_length(struct encode *E, uint32_t tag, size_t size) { PB_CTASSERT(sizeof(size) <= sizeof(uint64_t)); uint8_t buf[PB_TAG_BUFSIZE + PB_VARINT_BUFSIZE]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_LENGTH_DELIMITED, &buf[n]); n += pb_format_varint(size, &buf[n]); return pb_encode_buf(E, buf, n); } /* * Submessages */ struct submsg { size_t sm_size; struct encode *sm_E; }; static pb_encoder_callback_t encode_submsg_callback; static int pb_encode_submsg(struct encode *E, const struct pb_msg_hdr *msg_hdr, size_t size) { struct submsg submsg = { .sm_size = size, .sm_E = E, }; struct encode Esub = { .callback = &encode_submsg_callback, .arg = &submsg, }; int error; error = pb_encode_by_hdr(&Esub, msg_hdr); if (error) return error; if (submsg.sm_size) /* Means caller used stale precomputed encoding size. */ return EINVAL; return 0; } static int encode_submsg_callback(void *arg, const void *buf, size_t size) { struct submsg *const submsg = arg; struct encode *const E = submsg->sm_E; if (size > submsg->sm_size) /* Means caller used stale precomputed encoding size. */ return EINVAL; submsg->sm_size -= size; return pb_encode_buf(E, buf, size); } /* * Sizing */ int pb_encoding_size(struct pb_msg msg, size_t *sizep) { struct pb_msg_hdr *const msg_hdr = (struct pb_msg_hdr *)msg.pbm_ptr; size_t size; int error; if (!pb_abi_compatible(msg.pbm_msgdesc->pbmd_abi_version, PB_ABI_VERSION)) /* XXX pb_bug */ return EINVAL; if (msg_hdr->pbmh_msgdesc != msg.pbm_msgdesc) /* XXX pb_bug */ return EINVAL; error = pb_size_by_hdr(&size, msg_hdr); if (error) return error; *sizep = size; return 0; } static int pb_size_by_hdr(size_t *sizep, struct pb_msg_hdr *msg_hdr) { unsigned char *const addr = (void *)msg_hdr; const struct pb_msgdesc *const msgdesc = msg_hdr->pbmh_msgdesc; size_t total = 0; size_t i; int error; for (i = 0; i < msgdesc->pbmd_nfields; i++) { size_t size; error = pb_size_field(&size, &msgdesc->pbmd_fields[i], addr); if (error) return error; if (size > SIZE_MAX - total) return EFBIG; total += size; } msg_hdr->pbmh_cached_size = total; *sizep = total; return 0; } static int pb_size_field(size_t *sizep, const struct pb_field *field, unsigned char *addr) { int error; switch (field->pbf_quant) { case PBQ_REQUIRED: return pb_size_field_value(sizep, field, (addr + field->pbf_qu.required.offset)); case PBQ_OPTIONAL: if (*(const bool *)(addr + field->pbf_qu.optional.present_offset)) { return pb_size_field_value(sizep, field, (addr + field->pbf_qu.optional.value_offset)); } *sizep = 0; return 0; case PBQ_REPEATED: { const struct pb_repeated *const repeated = (const struct pb_repeated *)(addr + field->pbf_qu.repeated.hdr_offset); unsigned char *const ptr = /* XXX Sketchy pointer cast. */ *(void *const *)(addr + field->pbf_qu.repeated.ptr_offset); const size_t elemsize = pb_type_size(&field->pbf_type); size_t total = 0; size_t i; /* XXX Handle packed repeated fields. */ for (i = 0; i < pb_repeated_count(repeated); i++) { size_t size; error = pb_size_field_value(&size, field, (ptr + (i * elemsize))); if (error) return error; if (size > SIZE_MAX - total) return EFBIG; total += size; } *sizep = total; return 0; } default: pb_bug("field %s: invalid quantifier: %d", field->pbf_name, (int)field->pbf_quant); } } static int pb_size_field_value(size_t *sizep, const struct pb_field *field, unsigned char *v) { const uint32_t t = field->pbf_tag; int error; switch (field->pbf_type.pbt_type) { case PB_TYPE_BOOL: return pb_size_tagged_varint_u(sizep, t, *(const bool *)v); case PB_TYPE_UINT32: return pb_size_tagged_varint_u(sizep, t, *(const uint32_t *)v); case PB_TYPE_UINT64: return pb_size_tagged_varint_u(sizep, t, *(const uint64_t *)v); case PB_TYPE_FIXED32: return pb_size_tagged_fixed32(sizep, t, *(const uint32_t *)v); case PB_TYPE_FIXED64: return pb_size_tagged_fixed64(sizep, t, *(const uint64_t *)v); case PB_TYPE_INT32: return pb_size_tagged_varint_s(sizep, t, *(const int32_t *)v); case PB_TYPE_INT64: return pb_size_tagged_varint_s(sizep, t, *(const int64_t *)v); case PB_TYPE_SINT32: return pb_size_tagged_zigzag(sizep, t, *(const int32_t *)v); case PB_TYPE_SINT64: return pb_size_tagged_zigzag(sizep, t, *(const int64_t *)v); case PB_TYPE_SFIXED32: return pb_size_tagged_sfixed32(sizep, t, *(const int32_t *)v); case PB_TYPE_SFIXED64: return pb_size_tagged_sfixed64(sizep, t, *(const int64_t *)v); case PB_TYPE_ENUM: return pb_size_tagged_varint_s(sizep, t, *(const int32_t *)v); case PB_TYPE_FLOAT: return pb_size_tagged_ieee32(sizep, t, *(const float *)v); case PB_TYPE_DOUBLE: return pb_size_tagged_ieee64(sizep, t, *(const double *)v); case PB_TYPE_BYTES: { const struct pb_bytes *const bytes = (const struct pb_bytes *)v; size_t size = pb_bytes_size(bytes); size_t lensize; error = pb_size_tagged_length(&lensize, t, size); if (error) return error; if (lensize > SIZE_MAX - size) return EFBIG; *sizep = lensize + size; return 0; } case PB_TYPE_STRING: { const struct pb_string *const string = (const struct pb_string *)v; const size_t len = pb_string_len(string); size_t lensize; error = pb_size_tagged_length(&lensize, t, len); if (error) return error; if (lensize > SIZE_MAX - len) return EFBIG; *sizep = lensize + len; return 0; } case PB_TYPE_MSG: { struct pb_msg_hdr *const msg_hdr = (struct pb_msg_hdr *)v; size_t size, lensize; error = pb_size_by_hdr(&size, msg_hdr); if (error) return error; error = pb_size_tagged_length(&lensize, t, size); if (error) return error; if (lensize > SIZE_MAX - size) return EFBIG; *sizep = lensize + size; return 0; } default: pb_bug("field %s: invalid type: %d", field->pbf_name, field->pbf_type.pbt_type); } } static int pb_size_tagged_varint_u(size_t *sizep, uint32_t tag, uint64_t u) { uint8_t buf[PB_TAG_BUFSIZE + PB_VARINT_BUFSIZE]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_VARINT, &buf[n]); n += pb_format_varint(u, &buf[n]); *sizep = n; return 0; } static int pb_size_tagged_fixed32(size_t *sizep, uint32_t tag, uint32_t u pb_attr_unused) { uint8_t buf[PB_TAG_BUFSIZE + 4]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_32BIT, &buf[n]); n += 4; *sizep = n; return 0; } static int pb_size_tagged_fixed64(size_t *sizep, uint32_t tag, uint64_t u pb_attr_unused) { uint8_t buf[PB_TAG_BUFSIZE + 8]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_64BIT, &buf[n]); n += 8; *sizep = n; return 0; } static int pb_size_tagged_varint_s(size_t *sizep, uint32_t tag, int64_t s) { return pb_size_tagged_varint_u(sizep, tag, (uint64_t)s); } static int pb_size_tagged_zigzag(size_t *sizep, uint32_t tag, int64_t s) { return pb_size_tagged_varint_u(sizep, tag, (uint64_t)((s << 1) ^ (s >> 63))); } static int pb_size_tagged_sfixed32(size_t *sizep, uint32_t tag, int32_t s pb_attr_unused) { return pb_size_tagged_fixed32(sizep, tag, 0); } static int pb_size_tagged_sfixed64(size_t *sizep, uint32_t tag, int64_t s pb_attr_unused) { return pb_size_tagged_fixed64(sizep, tag, 0); } static int pb_size_tagged_ieee32(size_t *sizep, uint32_t tag, float f pb_attr_unused) { return pb_size_tagged_fixed32(sizep, tag, 0); } static int pb_size_tagged_ieee64(size_t *sizep, uint32_t tag, double d pb_attr_unused) { return pb_size_tagged_fixed64(sizep, tag, 0); } static int pb_size_tagged_length(size_t *sizep, uint32_t tag, size_t size) { PB_CTASSERT(sizeof(size) <= sizeof(uint64_t)); uint8_t buf[PB_TAG_BUFSIZE + PB_VARINT_BUFSIZE]; size_t n = 0; n += pb_format_tag(tag, PB_WIRETYPE_LENGTH_DELIMITED, &buf[n]); n += pb_format_varint(size, &buf[n]); *sizep = n; return 0; }