/*- * 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 #include #else #include #include #include #include #include #include #endif #include "pb.h" #define PB_ABI_VERSION 0x00000000 #define PB_ABI_MAJOR(v) (((v) >> 16) & 0xffff) #define PB_ABI_MINOR(v) ((v) & 0xffff) #if defined(__NetBSD__) && defined(_KERNEL) static void kmem_malloc(size_t); static void * kmem_realloc(void *, size_t, size_t); #else static void free_sized(void *, size_t); #endif static void pb_allocator_destroy(const struct pb_allocator *, struct pb_msg); static void pb_allocator_init_repeated(const struct pb_allocator *, const struct pb_field *, struct pb_repeated *, void **); static void pb_allocator_destroy_repeated(const struct pb_allocator *, const struct pb_field *, struct pb_repeated *, void **); static void pb_allocator_init_field(const struct pb_allocator *, const struct pb_field *, unsigned char *); static void pb_allocator_destroy_field(const struct pb_allocator *, const struct pb_field *, unsigned char *); static void pb_allocator_bytes_init(const struct pb_allocator *, struct pb_bytes *); static void pb_bytes_reset(struct pb_bytes *); static void pb_allocator_bytes_destroy(const struct pb_allocator *, struct pb_bytes *); static void pb_allocator_string_init(const struct pb_allocator *, struct pb_string *); static void pb_string_reset(struct pb_string *); static void pb_allocator_string_destroy(const struct pb_allocator *, struct pb_string *); struct pb_repeated_enclosure { struct pb_repeated pbre_repeated; void *pbre_item; }; #if defined(__NetBSD__) && defined(_KERNEL) static void kmem_malloc(size_t size) { return kmem_zalloc(size, KM_SLEEP); } static void * kmem_realloc(void *oldptr, size_t oldsize, size_t newsize) { void *const newptr = kmem_zalloc(newsize, KM_SLEEP); if (newptr == NULL) return NULL; if (oldptr != NULL) { (void)memcpy(newptr, oldptr, oldsize); kmem_free(oldptr, oldsize); } return newptr; } const struct pb_allocator pb_default_allocator = { .pba_alloc = &kmem_malloc, .pba_realloc = &kmem_realloc, .pba_free = &kmem_free, }; #else /* !(__NetBSD__ && _KERNEL) */ static void * realloc_sized(void *oldptr, size_t oldsize pb_attr_unused, size_t newsize) { return realloc(oldptr, newsize); } static void free_sized(void *p, size_t n pb_attr_unused) { free(p); } const struct pb_allocator pb_default_allocator = { .pba_alloc = &malloc, .pba_realloc = &realloc_sized, .pba_free = &free_sized, }; #endif /* _KERNEL */ bool pb_abi_compatible(uint32_t stored, uint32_t implemented) { if (PB_ABI_MAJOR(stored) != PB_ABI_MAJOR(implemented)) return false; if (PB_ABI_MINOR(stored) > PB_ABI_MINOR(implemented)) return false; return true; } void pb_init(struct pb_msg pbm) { pb_allocator_init(&pb_default_allocator, pbm); } void pb_allocator_init(const struct pb_allocator *allocator, struct pb_msg pbm) { const struct pb_msgdesc *const msgdesc = pbm.pbm_msgdesc; struct pb_msg_hdr *const msg_hdr = pbm.pbm_ptr; unsigned char *const addr = pbm.pbm_ptr; size_t i; pb_assert(pb_abi_compatible(msgdesc->pbmd_abi_version, PB_ABI_VERSION)); msg_hdr->pbmh_msgdesc = msgdesc; msg_hdr->pbmh_allocator = allocator; msg_hdr->pbmh_cached_size = ~(size_t)0; /* XXX Default values? */ for (i = 0; i < msgdesc->pbmd_nfields; i++) { const struct pb_field *const field = &msgdesc->pbmd_fields[i]; switch (field->pbf_quant) { case PBQ_REQUIRED: pb_allocator_init_field(allocator, field, (addr + field->pbf_qu.required.offset)); break; case PBQ_OPTIONAL: *(bool *)(addr + field->pbf_qu.optional.present_offset) = false; pb_allocator_init_field(allocator, field, (addr + field->pbf_qu.optional.value_offset)); break; case PBQ_REPEATED: { struct pb_repeated *const repeated = (struct pb_repeated *)(addr + field->pbf_qu.repeated.hdr_offset); pb_allocator_init_repeated(allocator, field, repeated, /* XXX Sketchy pointer cast. */ (void **)(addr + field->pbf_qu.repeated.ptr_offset)); break; } default: pb_bug("field %s: invalid quantifier: %d", field->pbf_name, (int)field->pbf_quant); } } } void pb_destroy(struct pb_msg pbm) { struct pb_msg_hdr *const msg_hdr = pbm.pbm_ptr; const struct pb_allocator *const allocator = msg_hdr->pbmh_allocator; pb_allocator_destroy(allocator, pbm); } static void pb_allocator_destroy(const struct pb_allocator *allocator, struct pb_msg pbm) { const struct pb_msgdesc *const msgdesc = pbm.pbm_msgdesc; struct pb_msg_hdr *const msg_hdr = pbm.pbm_ptr; unsigned char *const addr = pbm.pbm_ptr; size_t i; pb_assert(pb_abi_compatible(msgdesc->pbmd_abi_version, PB_ABI_VERSION)); pb_assert(msg_hdr->pbmh_msgdesc == msgdesc); pb_assert(msg_hdr->pbmh_allocator == allocator); for (i = 0; i < msgdesc->pbmd_nfields; i++) { const struct pb_field *const field = &msgdesc->pbmd_fields[i]; switch (field->pbf_quant) { case PBQ_REQUIRED: pb_allocator_destroy_field(allocator, field, (addr + field->pbf_qu.required.offset)); break; case PBQ_OPTIONAL: pb_allocator_destroy_field(allocator, field, (addr + field->pbf_qu.optional.value_offset)); break; case PBQ_REPEATED: { struct pb_repeated *const repeated = (struct pb_repeated *)(addr + field->pbf_qu.repeated.hdr_offset); pb_allocator_destroy_repeated(allocator, field, repeated, /* XXX Sketchy pointer cast. */ (void **)(addr + field->pbf_qu.repeated.ptr_offset)); break; } default: pb_bug("field %s: invalid quantifier: %d", field->pbf_name, (int)field->pbf_quant); } } msg_hdr->pbmh_msgdesc = NULL; msg_hdr->pbmh_allocator = NULL; } static void pb_allocator_init_repeated(const struct pb_allocator *allocator, const struct pb_field *field, struct pb_repeated *repeated, void **ptrp pb_attr_diagused) { pb_assert(ptrp == &((struct pb_repeated_enclosure *)repeated) ->pbre_item); *ptrp = NULL; repeated->pbr_allocator = allocator; repeated->pbr_field = field; repeated->pbr_nused = 0; repeated->pbr_nalloc = 0; } static void pb_allocator_destroy_repeated(const struct pb_allocator *allocator, const struct pb_field *field, struct pb_repeated *repeated, void **ptrp) { unsigned char *const ptr = *ptrp; const size_t elemsize = pb_type_size(&field->pbf_type); size_t i; pb_assert(ptrp == &((struct pb_repeated_enclosure *)(void *)repeated) ->pbre_item); pb_assert(repeated->pbr_allocator == allocator); pb_assert(repeated->pbr_nused <= repeated->pbr_nalloc); pb_assert(repeated->pbr_nalloc <= (SIZE_MAX / elemsize)); for (i = 0; i < pb_repeated_count(repeated); i++) pb_allocator_destroy_field(allocator, field, (ptr + (i * elemsize))); if (0 < repeated->pbr_nalloc) (*allocator->pba_free)(ptr, (elemsize * repeated->pbr_nalloc)); *ptrp = NULL; repeated->pbr_allocator = NULL; repeated->pbr_nused = 0; repeated->pbr_nalloc = 0; } static void pb_allocator_init_field(const struct pb_allocator *allocator, const struct pb_field *field, unsigned char *value) { /* XXX Default values...? */ switch (field->pbf_type.pbt_type) { case PB_TYPE_BOOL: *(bool *)value = false; break; case PB_TYPE_UINT32: *(uint32_t *)value = 0; break; case PB_TYPE_UINT64: *(uint64_t *)value = 0; break; case PB_TYPE_FIXED32: *(uint32_t *)value = 0; break; case PB_TYPE_FIXED64: *(uint64_t *)value = 0; break; case PB_TYPE_INT32: *(int32_t *)value = 0; break; case PB_TYPE_INT64: *(int64_t *)value = 0; break; case PB_TYPE_SINT32: *(int32_t *)value = 0; break; case PB_TYPE_SINT64: *(int64_t *)value = 0; break; case PB_TYPE_SFIXED32: *(int32_t *)value = 0; break; case PB_TYPE_SFIXED64: *(int64_t *)value = 0; break; case PB_TYPE_ENUM: *(int32_t *)value = 0; break; case PB_TYPE_FLOAT: *(float *)value = 0; break; case PB_TYPE_DOUBLE: *(double *)value = 0; break; case PB_TYPE_BYTES: pb_allocator_bytes_init(allocator, (struct pb_bytes *)value); break; case PB_TYPE_STRING: pb_allocator_string_init(allocator, (struct pb_string *)value); break; case PB_TYPE_MSG: pb_allocator_init(allocator, (struct pb_msg) { .pbm_msgdesc = field->pbf_type.pbt_u.msg.msgdesc, .pbm_ptr = value, }); break; default: pb_bug("field %s: invalid type: %d", field->pbf_name, (int)field->pbf_type.pbt_type); } } static void pb_allocator_destroy_field(const struct pb_allocator *allocator, const struct pb_field *field, unsigned char *value) { switch (field->pbf_type.pbt_type) { case PB_TYPE_BOOL: *(bool *)value = false; break; case PB_TYPE_UINT32: *(uint32_t *)value = 0; break; case PB_TYPE_UINT64: *(uint64_t *)value = 0; break; case PB_TYPE_FIXED32: *(uint32_t *)value = 0; break; case PB_TYPE_FIXED64: *(uint64_t *)value = 0; break; case PB_TYPE_INT32: *(int32_t *)value = 0; break; case PB_TYPE_INT64: *(int64_t *)value = 0; break; case PB_TYPE_SINT32: *(int32_t *)value = 0; break; case PB_TYPE_SINT64: *(int64_t *)value = 0; break; case PB_TYPE_SFIXED32: *(int32_t *)value = 0; break; case PB_TYPE_SFIXED64: *(int64_t *)value = 0; break; case PB_TYPE_ENUM: *(int32_t *)value = 0; break; case PB_TYPE_FLOAT: *(float *)value = 0; break; case PB_TYPE_DOUBLE: *(double *)value = 0; break; case PB_TYPE_BYTES: pb_allocator_bytes_destroy(allocator, (struct pb_bytes *)value); break; case PB_TYPE_STRING: pb_allocator_string_destroy(allocator, (struct pb_string *)value); break; case PB_TYPE_MSG: pb_allocator_destroy(allocator, (struct pb_msg) { .pbm_msgdesc = field->pbf_type.pbt_u.msg.msgdesc, .pbm_ptr = value }); break; default: pb_bug("field %s: invalid type: %d", field->pbf_name, (int)field->pbf_type.pbt_type); } } int pb_alloc(struct pb_msg_ptr pbmp) { return pb_allocator_alloc(&pb_default_allocator, pbmp); } int pb_allocator_alloc(const struct pb_allocator *allocator, struct pb_msg_ptr pbmp) { const struct pb_msgdesc *const msgdesc = pbmp.pbmp_msgdesc; void *ptr; pb_assert(0 < msgdesc->pbmd_size); ptr = (*allocator->pba_alloc)(msgdesc->pbmd_size); if (ptr == NULL) return ENOMEM; pb_allocator_init(allocator, (struct pb_msg) { .pbm_msgdesc = msgdesc, .pbm_ptr = ptr }); /* XXX Sketchy pointer cast. */ *(void **)pbmp.pbmp_ptrp = ptr; return 0; } void pb_free(struct pb_msg_ptr pbmp) { const struct pb_msgdesc *const msgdesc = pbmp.pbmp_msgdesc; /* XXX Sketchy pointer cast. */ void *ptr = *(void **)pbmp.pbmp_ptrp; struct pb_msg_hdr *const msg_hdr = ptr; const struct pb_allocator *const allocator = msg_hdr->pbmh_allocator; pb_destroy((struct pb_msg){ .pbm_msgdesc = msgdesc, .pbm_ptr = ptr }); pb_assert(0 < msgdesc->pbmd_size); (*allocator->pba_free)(ptr, msgdesc->pbmd_size); /* XXX Sketchy pointer cast. */ *(void **)pbmp.pbmp_ptrp = NULL; } static void pb_allocator_bytes_init(const struct pb_allocator *allocator, struct pb_bytes *bytes) { bytes->pbb_allocator = allocator; bytes->pbb_allocation = PB_ALLOC_STATIC; bytes->pbb_u.static_alloc.ptr = NULL; bytes->pbb_u.static_alloc.size = 0; } static void pb_bytes_reset(struct pb_bytes *bytes) { const struct pb_allocator *const allocator = bytes->pbb_allocator; switch (bytes->pbb_allocation) { case PB_ALLOC_STATIC: break; case PB_ALLOC_DYNAMIC: pb_assert(0 < bytes->pbb_u.dynamic_alloc.size); (*allocator->pba_free)(bytes->pbb_u.dynamic_alloc.ptr, bytes->pbb_u.dynamic_alloc.size); break; default: pb_bug("invalid bytes allocation: %d", (int)bytes->pbb_allocation); } bytes->pbb_allocation = PB_ALLOC_STATIC; bytes->pbb_u.static_alloc.ptr = NULL; bytes->pbb_u.static_alloc.size = 0; } static void pb_allocator_bytes_destroy(const struct pb_allocator *allocator, struct pb_bytes *bytes) { pb_assert(bytes->pbb_allocator == allocator); pb_bytes_reset(bytes); bytes->pbb_allocator = NULL; } size_t pb_bytes_size(const struct pb_bytes *bytes) { switch (bytes->pbb_allocation) { case PB_ALLOC_STATIC: return bytes->pbb_u.static_alloc.size; case PB_ALLOC_DYNAMIC: return bytes->pbb_u.dynamic_alloc.size; default: pb_bug("invalid bytes allocation: %d", (int)bytes->pbb_allocation); } } const uint8_t * pb_bytes_ptr(const struct pb_bytes *bytes, size_t *sizep) { switch (bytes->pbb_allocation) { case PB_ALLOC_STATIC: *sizep = bytes->pbb_u.static_alloc.size; return bytes->pbb_u.static_alloc.ptr; case PB_ALLOC_DYNAMIC: *sizep = bytes->pbb_u.dynamic_alloc.size; return bytes->pbb_u.dynamic_alloc.ptr; default: pb_bug("invalid bytes allocation: %d", (int)bytes->pbb_allocation); } } uint8_t * pb_bytes_ptr_mutable(struct pb_bytes *bytes, size_t *sizep) { pb_assert(bytes->pbb_allocation == PB_ALLOC_DYNAMIC); *sizep = bytes->pbb_u.dynamic_alloc.size; return bytes->pbb_u.dynamic_alloc.ptr; } int pb_bytes_alloc(struct pb_bytes *bytes, size_t size) { if (size == 0) { pb_bytes_reset(bytes); } else { uint8_t *const ptr = (*bytes->pbb_allocator->pba_alloc)(size); if (ptr == NULL) return ENOMEM; bytes->pbb_allocation = PB_ALLOC_DYNAMIC; bytes->pbb_u.dynamic_alloc.ptr = ptr; bytes->pbb_u.dynamic_alloc.size = size; } return 0; } int pb_bytes_set_copy(struct pb_bytes *bytes, const uint8_t *from, size_t size) { uint8_t *ptr; size_t tsize pb_attr_diagused; int error; error = pb_bytes_alloc(bytes, size); if (error) return error; if (size == 0) return 0; ptr = pb_bytes_ptr_mutable(bytes, &tsize); pb_assert(tsize == size); (void)memcpy(ptr, from, size); return 0; } void pb_bytes_set_ptr(struct pb_bytes *bytes, const uint8_t *ptr, size_t size) { pb_bytes_reset(bytes); bytes->pbb_allocation = PB_ALLOC_STATIC; bytes->pbb_u.static_alloc.ptr = ptr; bytes->pbb_u.static_alloc.size = size; } static void pb_allocator_string_init(const struct pb_allocator *allocator, struct pb_string *string) { string->pbs_allocator = allocator; string->pbs_allocation = PB_ALLOC_STATIC; string->pbs_u.static_alloc.ptr = ""; string->pbs_u.static_alloc.len = 0; } static void pb_string_reset(struct pb_string *string) { const struct pb_allocator *const allocator = string->pbs_allocator; switch (string->pbs_allocation) { case PB_ALLOC_STATIC: break; case PB_ALLOC_DYNAMIC: (*allocator->pba_free)(string->pbs_u.dynamic_alloc.ptr, (string->pbs_u.dynamic_alloc.len + 1)); break; default: pb_bug("invalid string allocation: %d", (int)string->pbs_allocation); } string->pbs_allocation = PB_ALLOC_STATIC; string->pbs_u.static_alloc.ptr = ""; string->pbs_u.static_alloc.len = 0; } static void pb_allocator_string_destroy(const struct pb_allocator *allocator, struct pb_string *string) { pb_assert(string->pbs_allocator == allocator); pb_string_reset(string); pb_assert(string->pbs_allocation == PB_ALLOC_STATIC); string->pbs_u.static_alloc.ptr = ""; pb_assert(string->pbs_u.static_alloc.len == 0); string->pbs_allocator = NULL; } size_t pb_string_len(const struct pb_string *string) { switch (string->pbs_allocation) { case PB_ALLOC_STATIC: return string->pbs_u.static_alloc.len; case PB_ALLOC_DYNAMIC: return string->pbs_u.dynamic_alloc.len; default: pb_bug("invalid string allocation: %d", (int)string->pbs_allocation); } } const char * pb_string_ptr(const struct pb_string *string) { switch (string->pbs_allocation) { case PB_ALLOC_STATIC: pb_assert(string->pbs_u.static_alloc.ptr != NULL); return string->pbs_u.static_alloc.ptr; case PB_ALLOC_DYNAMIC: pb_assert(string->pbs_u.dynamic_alloc.ptr != NULL); return string->pbs_u.dynamic_alloc.ptr; default: pb_bug("invalid string allocation: %d", (int)string->pbs_allocation); } } char * pb_string_ptr_mutable(struct pb_string *string) { pb_assert(string->pbs_allocation == PB_ALLOC_DYNAMIC); pb_assert(string->pbs_u.dynamic_alloc.ptr != NULL); return string->pbs_u.dynamic_alloc.ptr; } int pb_string_alloc(struct pb_string *string, size_t len) { if (len == SIZE_MAX) return ENOMEM; /* XXX Realloc for dynamic strings. */ char *const ptr = (*string->pbs_allocator->pba_alloc)(len + 1); if (ptr == NULL) return ENOMEM; pb_string_reset(string); string->pbs_allocation = PB_ALLOC_DYNAMIC; string->pbs_u.dynamic_alloc.ptr = ptr; string->pbs_u.dynamic_alloc.len = len; ptr[len] = '\0'; return 0; } int pb_string_set_copy(struct pb_string *string, const char *from, size_t len) { int error; error = pb_string_alloc(string, len); if (error) return error; pb_assert(string->pbs_allocation == PB_ALLOC_DYNAMIC); pb_assert(string->pbs_u.dynamic_alloc.len == len); pb_assert(string->pbs_u.dynamic_alloc.ptr[len] == '\0'); (void)memcpy(string->pbs_u.dynamic_alloc.ptr, from, len); return 0; } void pb_string_set_ptr(struct pb_string *string, const char *ptr, size_t len) { pb_assert(ptr != NULL); pb_assert(len < SIZE_MAX); pb_assert(ptr[len] == '\0'); pb_string_reset(string); string->pbs_allocation = PB_ALLOC_STATIC; string->pbs_u.static_alloc.ptr = ptr; string->pbs_u.static_alloc.len = len; } int pb_string_set_copy_asciz(struct pb_string *string, const char *ptr) { return pb_string_set_copy(string, ptr, strlen(ptr)); } void pb_string_set_ptr_asciz(struct pb_string *string, const char *ptr) { pb_string_set_ptr(string, ptr, strlen(ptr)); } /* * UTF-8 validation, inspired by Bjoern Hoermann's UTF-8 decoder at * , but reimplemented * from scratch. */ #define UTF8_ACCEPT 0 #define UTF8_REJECT 96 typedef unsigned long utf8_class_t; typedef unsigned long utf8_state_t; typedef uint32_t unicodept_t; static uint8_t utf8_classtab[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 11,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 7,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, }; static uint8_t utf8_statetab[] = { 0,96,12,36,48,84,72,60,96,96,96,24, 96, 0,96,96,96,96,96,96, 0, 0,96,96, 96,12,96,96,96,96,96,96,96,96,96,96, 96,12,96,96,96,96,96,96,12,12,96,96, 96,96,96,96,96,96,96,96,12,12,96,96, 96,36,96,96,96,96,96,96,96,36,96,96, 96,36,96,96,96,96,96,96,36,36,96,96, 96,96,96,96,96,96,96,96,36,96,96,96, 96,96,96,96,96,96,96,96,96,96,96,96, }; static utf8_state_t utf8_decode_step(utf8_state_t state, uint8_t octet, unicodept_t *cpp) { const utf8_class_t class = utf8_classtab[octet]; *cpp = (state == UTF8_ACCEPT ? (octet & (0xff >> class)) : ((octet & 0x3f) | (*cpp << 6))); return utf8_statetab[state + class]; } int pb_utf8_validate(const char *string, size_t len) { utf8_state_t state = UTF8_ACCEPT; unicodept_t cp pb_attr_unused; while (len--) state = utf8_decode_step(state, *string++, &cp); if (state != UTF8_ACCEPT) return EILSEQ; return 0; } /* * Repeated fields */ int pb_repeated_add(struct pb_repeated *repeated, size_t *ip) { int error; if (repeated->pbr_nused == repeated->pbr_nalloc) { size_t i = repeated->pbr_nused; if (i == SIZE_MAX) return ENOMEM; error = pb_repeated_alloc(repeated, i + 1); if (error) return error; *ip = i; return 0; } pb_assert(repeated->pbr_nused < repeated->pbr_nalloc); *ip = repeated->pbr_nused++; return 0; } #define MIN(A, B) ((A) < (B)? (A) : (B)) int pb_repeated_alloc(struct pb_repeated *repeated, size_t n) { struct pb_repeated_enclosure *enclosure = (struct pb_repeated_enclosure *)repeated; void **ptrp = &enclosure->pbre_item; const size_t elemsize = pb_type_size(&repeated->pbr_field->pbf_type); unsigned char *const oldptr = *ptrp, *newptr; size_t i, nalloc, oldsize, newsize; if (n == repeated->pbr_nused) return 0; if (n > SIZE_MAX / elemsize) return ENOMEM; if (repeated->pbr_field->pbf_qu.repeated.maximum && repeated->pbr_field->pbf_qu.repeated.maximum < n) return ENOMEM; /* * Round up to a power of two, up to 4096, or to a multiple of * 4096. This makes sure that for small messages, adding an * element at a time doesn't give quadratic-time behaviour. * For large messages, you're probably doing something wrong in * your protocol and application anyway. */ nalloc = n; if (nalloc < 4096) { nalloc |= nalloc >> 1; nalloc |= nalloc >> 2; nalloc |= nalloc >> 4; nalloc |= nalloc >> 8; nalloc++; pb_assert(nalloc <= 4096); } else if (nalloc > SIZE_MAX - 4095) { nalloc = SIZE_MAX; } else { nalloc = 4096 * ((nalloc + 4095) / 4096); } /* Bound by the maximum number of elements and by machine limits. */ if (repeated->pbr_field->pbf_qu.repeated.maximum && repeated->pbr_field->pbf_qu.repeated.maximum < nalloc) nalloc = repeated->pbr_field->pbf_qu.repeated.maximum; if (nalloc > SIZE_MAX / elemsize) nalloc = SIZE_MAX / elemsize; pb_assert(repeated->pbr_nalloc <= (SIZE_MAX / elemsize)); pb_assert(nalloc <= (SIZE_MAX / elemsize)); oldsize = (repeated->pbr_nalloc * elemsize); newsize = (nalloc * elemsize); /* * Alloc, rather than realloc, so we can destroy the old * elements on success if we're shrinking. (XXX Kinda silly -- * realloc shouldn't fail if we're shrinking -- but the kmem * allocator currently may require this.) */ if (newsize == 0) { newptr = NULL; } else { newptr = (*repeated->pbr_allocator->pba_alloc)(newsize); if (newptr == NULL) return ENOMEM; } /* Copy old used (but not merely allocated) elements. */ /* * XXX We assume elements have no pointers into themselves, so * that they are relocatable in memory. An early development * version of this library failed that. Beware! */ pb_assert(nalloc <= (SIZE_MAX / elemsize)); pb_assert(repeated->pbr_nused <= (SIZE_MAX / elemsize)); (void)memcpy(newptr, oldptr, elemsize * MIN(nalloc, repeated->pbr_nused)); /* Destroy old used or allocated elements if we're shrinking. */ pb_assert(repeated->pbr_nalloc <= (SIZE_MAX / elemsize)); for (i = nalloc; i < repeated->pbr_nalloc; i++) pb_allocator_destroy_field(repeated->pbr_allocator, repeated->pbr_field, (oldptr + (i * elemsize))); /* Done with the old array. Free it. */ if (0 < oldsize) (*repeated->pbr_allocator->pba_free)(oldptr, oldsize); else pb_assert(oldptr == NULL); /* Initialize new previously unused elements if we're growing. */ pb_assert(nalloc <= (SIZE_MAX / elemsize)); for (i = repeated->pbr_nused; i < nalloc; i++) pb_allocator_init_field(repeated->pbr_allocator, repeated->pbr_field, (newptr + (i * elemsize))); /* Done. Commit the changes. */ *ptrp = newptr; repeated->pbr_nused = n; repeated->pbr_nalloc = nalloc; return 0; } /* * Format cookies */ const void * pb_get_format(const struct pb_msgdesc *msgdesc, const struct pb_format *format) { size_t i; pb_assert(pb_abi_compatible(msgdesc->pbmd_abi_version, PB_ABI_VERSION)); /* * XXX Consider a binary search on format names, if the number * of formats is large. Not worth doing now because the * expected number of formats is somewhere between 0 and 1. */ for (i = 0; i < msgdesc->pbmd_nformat_cookies; i++) { if (msgdesc->pbmd_format_cookies[i].pbfc_format == format) return msgdesc->pbmd_format_cookies[i].pbfc_cookie; } return NULL; }