/*- * 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 #endif #include #include #include #include "pb_prop_encode.h" #define PB_ABI_VERSION 0x00000000 static int pb_prop_encode_by_hdr(const struct pb_msg_hdr *, const struct pb_prop_msgdesc *, prop_object_t *); static int pb_prop_encode_at(const unsigned char *, const struct pb_prop_msgdesc *, prop_object_t *); static int pb_prop_encode_array(const unsigned char *, const struct pb_prop_array *, prop_object_t *); static int pb_prop_encode_record(const unsigned char *, const struct pb_prop_record *, prop_object_t *); static int pb_prop_encode_record_field(const unsigned char *, const struct pb_prop_record_field *, prop_dictionary_t); static int pb_prop_encode_repeated(const unsigned char *, const struct pb_prop_field *, prop_object_t *); static int pb_prop_encode_field_value(const unsigned char *, const struct pb_type *, const struct pb_prop_field *, prop_object_t *); static int pb_prop_encode_enum(const unsigned char *, const struct pb_prop_enumeration *, prop_object_t *); static const char * pb_prop_enumerand_by_number(const struct pb_prop_enumeration *, int32_t); int pb_prop_encode(struct pb_msg msg, prop_object_t *objp) { const struct pb_prop_msgdesc *prop; const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)msg.pbm_ptr; pb_assert(msg.pbm_msgdesc == msg_hdr->pbmh_msgdesc); if (!pb_abi_compatible(msg.pbm_msgdesc->pbmd_abi_version, PB_ABI_VERSION)) /* XXX pb_bug */ return EINVAL; prop = pb_get_format(msg.pbm_msgdesc, &pb_prop_format); if (prop == NULL) return EINVAL; return pb_prop_encode_by_hdr(msg_hdr, prop, objp); } static int pb_prop_encode_by_hdr(const struct pb_msg_hdr *msg_hdr, const struct pb_prop_msgdesc *prop, prop_object_t *objp) { pb_assert(msg_hdr->pbmh_msgdesc == prop->pbpm_msgdesc); return pb_prop_encode_at((const unsigned char *)msg_hdr, prop, objp); } static int pb_prop_encode_at(const unsigned char *ptr, const struct pb_prop_msgdesc *prop, prop_object_t *objp) { switch (prop->pbpm_t) { case PB_PROP_MSG_ARRAY: return pb_prop_encode_array(ptr, &prop->pbpm_u.array, objp); case PB_PROP_MSG_RECORD: return pb_prop_encode_record(ptr, &prop->pbpm_u.record, objp); case PB_PROP_MSG_SPLICE: { const struct pb_field *field; const struct pb_prop_msgdesc *const prop0 = prop->pbpm_u.splice.pbps_msgdesc; pb_assert(prop->pbpm_msgdesc->pbmd_nfields == 1); field = &prop->pbpm_msgdesc->pbmd_fields[0]; pb_assert(field->pbf_quant == PBQ_REQUIRED); pb_assert(field->pbf_type.pbt_type == PB_TYPE_MSG); pb_assert(field->pbf_type.pbt_u.msg.msgdesc == prop0->pbpm_msgdesc); return pb_prop_encode_at(ptr + field->pbf_qu.required.offset, prop0, objp); } default: return EIO; } } static int pb_prop_encode_array(const unsigned char *ptr, const struct pb_prop_array *arraydesc, prop_object_t *objp) { const struct pb_prop_field *field; pb_assert(arraydesc->pbpa_msgdesc->pbmd_nfields == 1); pb_assert(arraydesc->pbpa_msgdesc->pbmd_fields[0].pbf_quant == PBQ_REPEATED); field = arraydesc->pbpa_field; pb_assert(field->pbpf_msgdesc == arraydesc->pbpa_msgdesc); pb_assert(field->pbpf_fieldno == 0); return pb_prop_encode_repeated(ptr, field, objp); } static int pb_prop_encode_record(const unsigned char *ptr, const struct pb_prop_record *record, prop_object_t *objp) { prop_dictionary_t dict; size_t i; int error; dict = prop_dictionary_create(); if (dict == NULL) { error = ENOMEM; goto fail0; } for (i = 0; i < record->pbpr_nfields; i++) { error = pb_prop_encode_record_field(ptr, &record->pbpr_fields[i], dict); if (error) goto fail1; } /* Success! */ *objp = dict; return 0; fail1: prop_object_release(dict); fail0: return error; } static int pb_prop_encode_record_field(const unsigned char *ptr, const struct pb_prop_record_field *rfield, prop_dictionary_t dict) { const struct pb_prop_field *pfield = &rfield->pbprf_field; const struct pb_field *field; const struct pb_type *type; prop_object_t value; int error; pb_assert(pfield->pbpf_fieldno < pfield->pbpf_msgdesc->pbmd_nfields); field = &pfield->pbpf_msgdesc->pbmd_fields[pfield->pbpf_fieldno]; type = &field->pbf_type; switch (field->pbf_quant) { case PBQ_REQUIRED: { const size_t offset = field->pbf_qu.required.offset; error = pb_prop_encode_field_value(ptr + offset, type, pfield, &value); if (error) goto fail0; break; } case PBQ_OPTIONAL: { const size_t present_offset = field->pbf_qu.optional.present_offset; const size_t value_offset = field->pbf_qu.optional.value_offset; if (!*(const bool *)(ptr + present_offset)) return 0; error = pb_prop_encode_field_value(ptr + value_offset, type, pfield, &value); if (error) goto fail0; break; } case PBQ_REPEATED: error = pb_prop_encode_repeated(ptr, pfield, &value); if (error) goto fail0; break; default: error = EIO; /* XXX */ goto fail0; } if (!prop_dictionary_set(dict, rfield->pbprf_key, value)) { error = EIO; /* XXX */ goto fail1; } /* Success! */ return 0; fail1: prop_object_release(value); fail0: return error; } static int pb_prop_encode_repeated(const unsigned char *ptr, const struct pb_prop_field *pfield, prop_object_t *objp) { const struct pb_field *field; prop_array_t array; const struct pb_repeated *repeated; size_t nelem; const unsigned char *elemptr; size_t i, elemsize; int error; pb_assert(pfield->pbpf_fieldno < pfield->pbpf_msgdesc->pbmd_nfields); field = &pfield->pbpf_msgdesc->pbmd_fields[pfield->pbpf_fieldno]; repeated = (const struct pb_repeated *)(ptr + field->pbf_qu.repeated.hdr_offset); elemptr = *(const void *const *)(ptr + field->pbf_qu.repeated.ptr_offset); elemsize = pb_type_size(&field->pbf_type); nelem = pb_repeated_count(repeated); if (nelem > UINT_MAX) { error = EINVAL; /* XXX */ goto fail0; } array = prop_array_create_with_capacity(nelem); if (array == NULL) { error = ENOMEM; /* XXX */ goto fail1; } for (i = 0; i < nelem; i++) { prop_object_t element; error = pb_prop_encode_field_value(elemptr + i*elemsize, &field->pbf_type, pfield, &element); if (error) goto fail1; if (!prop_array_set(array, i, element)) { prop_object_release(element); error = EIO; /* XXX */ goto fail1; } } /* Success! */ *objp = array; return 0; fail1: prop_object_release(array); fail0: pb_assert(error); return error; } static int pb_prop_encode_field_value(const unsigned char *ptr, const struct pb_type *type, const struct pb_prop_field *field, prop_object_t *valuep) { prop_object_t value; int error; switch (type->pbt_type) { checkalloc: if (value == NULL) { error = ENOMEM; goto fail; } break; case PB_TYPE_BOOL: value = prop_bool_create(*(const bool *)ptr); goto checkalloc; case PB_TYPE_UINT32: case PB_TYPE_FIXED32: { const uint32_t *const u32p = (const uint32_t *)ptr; value = prop_number_create_unsigned_integer(*u32p); goto checkalloc; } case PB_TYPE_UINT64: case PB_TYPE_FIXED64: { const uint64_t *const u64p = (const uint64_t *)ptr; value = prop_number_create_unsigned_integer(*u64p); goto checkalloc; } case PB_TYPE_INT32: case PB_TYPE_SINT32: case PB_TYPE_SFIXED32: { const int32_t *const s32p = (const int32_t *)ptr; value = prop_number_create_integer(*s32p); goto checkalloc; } case PB_TYPE_INT64: case PB_TYPE_SINT64: case PB_TYPE_SFIXED64: { const int64_t *const s64p = (const int64_t *)ptr; value = prop_number_create_integer(*s64p); goto checkalloc; } case PB_TYPE_ENUM: error = pb_prop_encode_enum(ptr, field->pbpf_u.enumeration, &value); if (error) goto fail; break; case PB_TYPE_FLOAT: pb_assert(!"protobuf proplib does not deal in floats"); return EIO; case PB_TYPE_DOUBLE: pb_assert(!"protobuf proplib does not deal in doubles"); return EIO; case PB_TYPE_BYTES: { const struct pb_bytes *const bytes = (const struct pb_bytes *)ptr; const void *content; size_t size; content = pb_bytes_ptr(bytes, &size); pb_assert(size == 0 || content != NULL); value = prop_data_create_data(content, size); goto checkalloc; } case PB_TYPE_STRING: { const struct pb_string *const string = (const struct pb_string *)ptr; const char *content; /* * We truncate strings with embedded NULs, because we * have no other option with proplib. */ content = pb_string_ptr(string); pb_assert(content[pb_string_len(string)] == '\0'); value = prop_string_create_cstring(content); goto checkalloc; } case PB_TYPE_MSG: { const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)ptr; pb_assert(msg_hdr->pbmh_msgdesc == type->pbt_u.msg.msgdesc); error = pb_prop_encode_by_hdr(msg_hdr, field->pbpf_u.message, &value); if (error) goto fail; break; } default: return EIO; /* XXX */ } /* Success! */ *valuep = value; return 0; fail: pb_assert(error); return error; } static int pb_prop_encode_enum(const unsigned char *ptr, const struct pb_prop_enumeration *enumeration, prop_object_t *valuep) { int32_t number; const char *string; prop_object_t value; int error; number = *(const int32_t *)ptr; string = pb_prop_enumerand_by_number(enumeration, number); if (string == NULL) { error = EIO; goto fail; } value = prop_string_create_cstring_nocopy(string); if (value == NULL) { error = ENOMEM; goto fail; } /* Success! */ *valuep = value; return 0; fail: pb_assert(error); return error; } static const char * pb_prop_enumerand_by_number(const struct pb_prop_enumeration *enumeration, int32_t number) { size_t start = 0, end = enumeration->pbpen_nenumerands; while (start < end) { const size_t i = (start + ((end - start) / 2)); if (number < enumeration->pbpen_by_number[i].pbped_number) end = i; else if (number > enumeration->pbpen_by_number[i].pbped_number) start = (i + 1); else return enumeration->pbpen_by_number[i].pbped_string; } return NULL; }