| /* $Id$ | |
| * | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to you under the Apache License, Version | |
| * 2.0 (the "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /* | |
| * etch_binary_tdo.c -- binary tagged data output implementation. | |
| */ | |
| #include "etch_binary_tdo.h" | |
| #include "etch_defvalufact.h" | |
| #include "etch_arrayval.h" | |
| #include "etch_encoding.h" | |
| #include "etch_global.h" | |
| #include "etchlog.h" | |
| char* ETCHBTDO = "BTDO"; | |
| byte bintagdata_get_native_typecode(const unsigned short, const unsigned short); | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * private signatures | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| i_binary_tdo* new_binarytdo_vtable(); | |
| int destroy_binary_tagged_data_output(binary_tagged_data_output*); | |
| binary_tagged_data_output* clone_binary_tagged_data_output(binary_tagged_data_output*); | |
| int bintdo_start_message(tagged_data_output*, etch_message*); | |
| int bintdo_write_message(tagged_data_output*, etch_message*, etch_flexbuffer*); | |
| int bintdo_end_message (tagged_data_output*, etch_message*); | |
| int bintdo_start_struct (tagged_data_output*, etch_structvalue*); | |
| int bintdo_write_struct (tagged_data_output*, etch_structvalue*); | |
| int bintdo_end_struct (tagged_data_output*, etch_structvalue*); | |
| int bintdo_start_array (tagged_data_output*, etch_arrayvalue*); | |
| int bintdo_write_array (tagged_data_output*, etch_arrayvalue*, etch_validator*); | |
| int bintdo_end_array (tagged_data_output*, etch_arrayvalue*); | |
| int bintdo_write_type (binary_tagged_data_output*, etch_type*); | |
| int bintdo_write_values(binary_tagged_data_output*, etch_arrayvalue*, etch_validator*); | |
| int bintdo_write_keys_values(binary_tagged_data_output*, etch_structvalue*); | |
| int bintdo_write_bytes_from (binary_tagged_data_output*, etch_nativearray*); | |
| int bintdo_write_value_rawint(binary_tagged_data_output*, const int); | |
| int bintdo_write_intvalue (binary_tagged_data_output*, const int); | |
| int bintdo_write_nonevalue(binary_tagged_data_output*); | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * constructors/destructors | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * new_binary_tdo() | |
| * binary_tagged_data_output constructor | |
| * @param vf a value factory. can be null. caller retains ownership. | |
| * @param fbuf the buffer to write to. can be null. caller retains ownership. | |
| */ | |
| binary_tagged_data_output* new_binary_tagdata_output(etch_value_factory* vf, etch_flexbuffer* fbuf) | |
| { | |
| i_binary_tdo* vtab = NULL; | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) new_object | |
| (sizeof(binary_tagged_data_output), ETCHTYPEB_TAGDATAOUT, CLASSID_TAGDATAOUT); | |
| tdo->destroy = destroy_binary_tagged_data_output; | |
| tdo->clone = clone_binary_tagged_data_output; | |
| tdo->flexbuf = fbuf; /* if caller passes buffer, caller owns it */ | |
| if (fbuf) tdo->is_flexbuf_owned = FALSE; | |
| tdo->vf = vf; | |
| vtab = cache_find(get_vtable_cachehkey(CLASSID_BINARYTDO_VTAB), 0); | |
| if(!vtab) | |
| { vtab = new_binarytdo_vtable(); | |
| cache_insert(vtab->hashkey, vtab, FALSE); | |
| } | |
| tdo->vtab = vtab; | |
| tdo->vtor_eod = etchvtor_eod_get(); | |
| tdo->vtor_int = etchvtor_int32_get(0); | |
| tdo->static_nullobj = etchtagdata_new_nullobj(TRUE); | |
| tdo->static_eodmarker = etchtagdata_new_eodmarker(TRUE); | |
| tdo->static_emptystring = etchtagdata_new_emptystring(TRUE); | |
| return tdo; | |
| } | |
| /** | |
| * new_binary_tdo() | |
| * casts result to generic tdo for use by interfaces | |
| */ | |
| tagged_data_output* new_binary_tdo(etch_value_factory* vf) | |
| { | |
| return (tagged_data_output*) new_binary_tagdata_output(vf, NULL); | |
| } | |
| /** | |
| * destroy_binary_tagged_data_output() | |
| */ | |
| int destroy_binary_tagged_data_output(binary_tagged_data_output* tdo) | |
| { | |
| if (tdo->refcount > 0 && --tdo->refcount > 0) return -1; | |
| if (!is_etchobj_static_content(tdo)) | |
| { | |
| if (tdo->impl) | |
| tdo->impl->destroy(tdo->impl); | |
| if (tdo->flexbuf && tdo->is_flexbuf_owned) | |
| tdo->flexbuf->destroy(tdo->flexbuf); | |
| } | |
| /* destroy private instance data */ | |
| etch_free(tdo->static_nullobj); | |
| etch_free(tdo->static_eodmarker); | |
| clear_etchobj_static_all(tdo->static_emptystring); | |
| tdo->static_emptystring->destroy(tdo->static_emptystring); | |
| return destroy_objectex((objmask*)tdo); | |
| } | |
| /** | |
| * clone_tagged_data_output() | |
| * tdo copy constructor. if the tdo object implements a separate instance data | |
| * object, that object is cloned as well. | |
| */ | |
| binary_tagged_data_output* clone_binary_tagged_data_output(binary_tagged_data_output* tdo) | |
| { | |
| binary_tagged_data_output* newtdo = (binary_tagged_data_output*) clone_object((objmask*) tdo); | |
| newtdo->impl = tdo->impl? tdo->impl->clone(tdo->impl): NULL; | |
| return newtdo; | |
| } | |
| /** | |
| * new_new_binarytdo_vtable() | |
| * constructor for binary tdo virtual function table | |
| */ | |
| i_binary_tdo* new_binarytdo_vtable() | |
| { | |
| etchparentinfo* inheritlist = new_etch_inheritance_list(3, 2, NULL); | |
| i_binary_tdo* vtab | |
| = new_vtable(NULL, sizeof(i_binary_tdo), CLASSID_BINARYTDO_VTAB); | |
| /* i_tagged_data_input */ | |
| vtab->start_message = bintdo_start_message; | |
| vtab->write_message = bintdo_write_message; | |
| vtab->end_message = bintdo_end_message; | |
| vtab->start_struct = bintdo_start_struct; | |
| vtab->write_struct = bintdo_write_struct; | |
| vtab->end_struct = bintdo_end_struct; | |
| vtab->start_array = bintdo_start_array; | |
| vtab->write_array = bintdo_write_array; | |
| vtab->end_array = bintdo_end_array; | |
| /* i_tagdata */ | |
| #if(0) | |
| vtab->check_value = etchtagdata_check_value; | |
| vtab->get_native_type = bintdo_get_native_type; | |
| vtab->get_native_type_code = bintdo_get_native_typecode; | |
| vtab->get_custom_structtype = bintdo_get_custom_structtype; | |
| #endif | |
| /* inheritance */ | |
| inheritlist[1].obj_type = ETCHTYPEB_TAGDATAOUT; | |
| inheritlist[1].class_id = CLASSID_TAGDATAOUT; | |
| inheritlist[2].obj_type = ETCHTYPEB_TAGDATA; | |
| inheritlist[2].class_id = CLASSID_TAGDATA; | |
| return vtab; | |
| } | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * write message | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * bintdo_start_message() | |
| * message is unique among serialized objects in that since message is at the | |
| * top level, no type byte is written to mark the start of a message. a version | |
| * number is written to identify the btd implementation version used to write | |
| * the message. | |
| */ | |
| int bintdo_start_message(tagged_data_output* tdox, etch_message* msg) | |
| { | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox; | |
| ETCH_ASSERT(tdo && msg); | |
| etch_flexbuf_put_byte(tdo->flexbuf, ETCH_BINTAGDATA_CURRENT_VERSION); | |
| return bintdo_start_struct(tdox, msg->sv); | |
| } | |
| /** | |
| * bintdo_write_message() | |
| * message is unique among serialized objects in that since message is at the | |
| * top level, no type byte is written to mark the start of a message. a version | |
| * number is written to identify the btd implementation version used to write | |
| * the message. | |
| */ | |
| int bintdo_write_message(tagged_data_output* tdox, etch_message* msg, | |
| etch_flexbuffer* fbuf) | |
| { | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox; | |
| ETCH_ASSERT(tdo && msg && fbuf); | |
| tdo->flexbuf = fbuf; | |
| if (-1 == bintdo_start_message(tdox, msg)) return -1; | |
| if (-1 == bintdo_write_keys_values(tdo, msg->sv)) return -1; | |
| return bintdo_end_message(tdox, msg); | |
| } | |
| /** | |
| * tdo_end_message() | |
| * marks the end of the message in process. | |
| */ | |
| int bintdo_end_message(tagged_data_output* tdox, etch_message* msg) | |
| { | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox; | |
| return bintdo_end_struct(tdox, msg->sv); | |
| } | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * write struct | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * bintdo_start_struct() | |
| * write the beginning of struct data. | |
| */ | |
| int bintdo_start_struct(tagged_data_output* tdox, etch_structvalue* sv) | |
| { | |
| etch_int32* sizeobj = NULL; | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox; | |
| ETCH_ASSERT(tdo && sv); | |
| /* caller has already written a bytecode to the buffer indicating struct follows */ | |
| if (-1 == bintdo_write_type(tdo, sv->struct_type)) return -1; | |
| return bintdo_write_value_rawint(tdo, structvalue_count(sv)); | |
| } | |
| /** | |
| * bintdo_write_struct() | |
| */ | |
| int bintdo_write_struct(tagged_data_output* tdox, etch_structvalue* sv) | |
| { | |
| ETCH_ASSERT(tdox && sv); | |
| if (-1 == bintdo_start_struct(tdox, sv)) return -1; | |
| if (-1 == bintdo_write_keys_values((binary_tagged_data_output*)tdox, sv)) return -1; | |
| return bintdo_end_struct(tdox, sv); | |
| } | |
| /** | |
| * bintdo_end_struct() | |
| * mark end of specified struct | |
| */ | |
| int bintdo_end_struct(tagged_data_output* tdox, etch_structvalue* sv) | |
| { | |
| return bintdo_write_nonevalue((binary_tagged_data_output*) tdox); | |
| } | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * write array | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * bintdo_start_array() | |
| * starts writing of an array | |
| */ | |
| int bintdo_start_array (tagged_data_output* tdox, etch_arrayvalue* av) | |
| { | |
| int errs = 0; | |
| binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox; | |
| ETCH_ASSERT(tdo && av); | |
| etch_flexbuf_put_byte (tdo->flexbuf, av->type_code); | |
| if (ETCH_XTRNL_TYPECODE_CUSTOM == av->type_code) | |
| errs += bintdo_write_type (tdo, av->custom_struct_type); | |
| errs += bintdo_write_value_rawint (tdo, av->dim); | |
| errs += bintdo_write_value_rawint (tdo, arrayvalue_count(av)); | |
| return errs? -1: 0; | |
| } | |
| /** | |
| * bintdo_write_array() | |
| */ | |
| int bintdo_write_array (tagged_data_output* tdox, | |
| etch_arrayvalue* av, etch_validator* vtor) | |
| { | |
| ETCH_ASSERT(tdox && vtor); | |
| if (!is_etch_arrayvalue(av)) return -1; | |
| if (-1 == bintdo_start_array (tdox, av)) return -1; | |
| if (-1 == bintdo_write_values ((binary_tagged_data_output*) tdox, av, vtor)) | |
| return -1; | |
| return bintdo_end_array(tdox, av); | |
| } | |
| /** | |
| * bintdo_end_array() | |
| * writes end of the array being read. | |
| */ | |
| int bintdo_end_array (tagged_data_output* tdo, etch_arrayvalue* av) | |
| { | |
| return bintdo_write_nonevalue ((binary_tagged_data_output*) tdo); | |
| } | |
| /** | |
| * bintdo_to_arrayvalue() | |
| * convert supplied native array to an etch_arrayvalue. | |
| * @param na the native array. caller retains. | |
| * @return an etch_arrayvalue. caller owns it. | |
| */ | |
| etch_arrayvalue* bintdo_to_arrayvalue (etch_nativearray* na) | |
| { | |
| etch_type* NULLTYPE = NULL; | |
| signed char content_typecode | |
| = arrayvalue_get_external_typecode (na->content_obj_type, na->content_class_id); | |
| /* todo we should calculate array size from native array | |
| * metadata rather than creating it using a default size */ | |
| etch_arrayvalue* av = new_arrayvalue_from (na, content_typecode, | |
| NULLTYPE, ETCH_DEFSIZE, ETCH_DEFSIZE, FALSE); | |
| if (NULL == av) | |
| etchlog(ETCHBTDO, ETCHLOG_ERROR, "nativearray conversion failed"); | |
| else av->is_array_owned = FALSE; /* 1/20 */ | |
| return av; | |
| } | |
| /** | |
| * normalize_etch_array() | |
| * validate parameter as an array type with dimension <= that specified, | |
| * and convert to arrayvalue if necessary. | |
| * @param a an arrayvalue or nativearray. caller retains. | |
| * @param maxdim maximum number of dimensions, zero means don't validate dimensions. | |
| * @return the passed array expressed as an to arrayvalue, or NULL if error. | |
| */ | |
| etch_arrayvalue* normalize_etch_array(void* a, const int maxdim) | |
| { | |
| etch_arrayvalue* av = NULL; | |
| if (is_etch_nativearray(a)) | |
| { etch_nativearray* na = (etch_nativearray*) a; | |
| if (0 == maxdim || na->numdims <= maxdim) | |
| av = bintdo_to_arrayvalue(na); | |
| } | |
| else | |
| if (is_etch_arrayvalue(a)) | |
| { etch_arrayvalue* xav = (etch_arrayvalue*) a; | |
| if (0 == maxdim || xav->dim <= maxdim) | |
| av = xav; | |
| } | |
| return av; | |
| } | |
| /* - - - - - - - - - - - - - - - - - - - - | |
| * disassemble objects and write bytes | |
| * - - - - - - - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * bintdo_write_nonevalue() | |
| * convenience method to write eod marker | |
| */ | |
| int bintdo_write_nonevalue(binary_tagged_data_output* tdo) | |
| { | |
| return bintdo_write_value(tdo, tdo->vtor_eod, tdo->static_eodmarker); | |
| } | |
| /** | |
| * bintdo_write_intvalue() | |
| * convenience method used when an encoded integer is expected next in | |
| * the buffer, to write such a value to the buffer. | |
| * | |
| * this method is no longer used, we now use bintdo_write_value_rawint() | |
| */ | |
| int bintdo_write_intvalue(binary_tagged_data_output* tdo, const int value) | |
| { | |
| etch_int32* intobj = new_int32(value); | |
| int result = bintdo_write_value(tdo, tdo->vtor_int, (objmask*) intobj); | |
| intobj->destroy(intobj); | |
| return result; | |
| } | |
| /** | |
| * bintdo_write_value_rawint() | |
| * write specified 32-bit integer value to the buffer | |
| */ | |
| int bintdo_write_value_rawint(binary_tagged_data_output* tdo, const int value) | |
| { | |
| size_t nout = 0; | |
| if (is_inrange_tiny(value)) | |
| nout = etch_flexbuf_put_byte(tdo->flexbuf, (signed char) value); | |
| else | |
| if (is_inrange_byte(value)) | |
| if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_BYTE)) | |
| nout = etch_flexbuf_put_byte(tdo->flexbuf, (signed char) value); | |
| else; | |
| else | |
| if (is_inrange_int16(value)) | |
| if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_SHORT)) | |
| nout = etch_flexbuf_put_short(tdo->flexbuf, (short) value); | |
| else; | |
| else | |
| if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_INT)) | |
| nout = etch_flexbuf_put_int(tdo->flexbuf, value); | |
| return nout? 0: -1; | |
| } | |
| /** | |
| * bintdo_write_type() | |
| * convenience method used when an etch_type is to be written to the buffer, | |
| * to write such a value to the buffer. caller owns the supplied type. | |
| * only the type's id is written. | |
| */ | |
| int bintdo_write_type(binary_tagged_data_output* tdo, etch_type* type) | |
| { | |
| return type? bintdo_write_value_rawint(tdo, type->id): -1; | |
| } | |
| /** | |
| * bintdo_get_bytes() | |
| * gets serialized bytes of the specified message. caller owns returned byte vector. | |
| * not sure what this is used for. | |
| * "static" method, no tdo is passed. | |
| * @return count of bytes | |
| */ | |
| int bintdo_get_bytes(etch_message* msg, etch_value_factory* vf, byte** out) | |
| { | |
| size_t bytecount = 0; | |
| etch_flexbuffer* fbuf = new_flexbuffer(0); /* tdo will own this */ | |
| binary_tagged_data_output* tdo = new_binary_tagdata_output(vf, fbuf); | |
| bintdo_write_message((tagged_data_output*)tdo, msg, fbuf); | |
| *out = etch_flexbuf_get_all(fbuf, &bytecount); /* new allocation */ | |
| tdo->destroy(tdo); | |
| return (int) bytecount; | |
| } | |
| /** | |
| * bintdo_write_bytes() | |
| * writes a byte vector to the buffer. | |
| */ | |
| int bintdo_write_bytes(binary_tagged_data_output* tdo, char* bytes, const int bytecount) | |
| { | |
| int result = bintdo_write_value_rawint(tdo, (int) bytecount); | |
| size_t nout = etch_flexbuf_put(tdo->flexbuf, bytes, 0, bytecount); | |
| return nout == bytecount? 0: -1; | |
| } | |
| /** | |
| * bintdo_write_bytes_from() | |
| * writes a byte vector from a native array to the buffer. | |
| * @param bytearray an etch_nativearray of single dimension and of content type byte. | |
| */ | |
| int bintdo_write_bytes_from(binary_tagged_data_output* tdo, etch_nativearray* bytearray) | |
| { | |
| /* we're assuming we always get a nativearray object and not a char*, | |
| * however i'm not sure yet exactly where the tdo input is created, | |
| * so i'm not positive this is the way is should be. update: perhaps this | |
| * method should be passed an arrayvalue, keep an eye on this. | |
| */ | |
| size_t bytecount = 0, nout = 0; | |
| int result = 0; | |
| if ((is_etch_nativearray(bytearray)) | |
| && (bytearray->class_id = CLASSID_ARRAY_BYTE) | |
| && (bytearray->numdims == 1)); | |
| else return -1; | |
| bytecount = bytearray->bytecount; /* or bytearray->dimsize[0], same thing */ | |
| result = bintdo_write_value_rawint(tdo, (int) bytecount); | |
| nout = etch_flexbuf_put(tdo->flexbuf, bytearray->values, 0, bytecount); | |
| return nout == bytecount? 0: -1; | |
| } | |
| /** | |
| * bintdo_write_string() | |
| * writes a string value to the buffer. | |
| */ | |
| int bintdo_write_string(binary_tagged_data_output* tdo, etch_string* s) | |
| { | |
| int result = 0, wire_encoding = 0, this_encoding = 0, is_new_memory = 0; | |
| int bytes_to_write = 0, bytes_written = 0; | |
| char* bytevector = NULL; | |
| if (NULL == s) return -1; | |
| wire_encoding = get_etch_string_encoding(tdo->vf); | |
| this_encoding = s->encoding; | |
| switch(wire_encoding) | |
| { | |
| case ETCH_ENCODING_UTF16: | |
| switch(this_encoding) | |
| { | |
| case ETCH_ENCODING_UTF16: | |
| bytevector = s->v.valc; | |
| bytes_to_write = s->char_count * sizeof(wchar_t); | |
| break; | |
| case ETCH_ENCODING_UTF8: | |
| case ETCH_ENCODING_ASCII: | |
| result = etch_utf8_to_unicode((wchar_t**)&bytevector, s->v.valc); | |
| if (bytevector) bytes_to_write = (int) wcslen((wchar_t*)bytevector); | |
| is_new_memory = TRUE; | |
| break; | |
| } | |
| break; | |
| case ETCH_ENCODING_UTF8: | |
| case ETCH_ENCODING_ASCII: | |
| switch(this_encoding) | |
| { | |
| case ETCH_ENCODING_UTF16: | |
| result = etch_unicode_to_utf8(&bytevector, s->v.valw); | |
| if (bytevector) bytes_to_write = (int) strlen(bytevector); | |
| is_new_memory = TRUE; | |
| break; | |
| case ETCH_ENCODING_UTF8: | |
| case ETCH_ENCODING_ASCII: | |
| bytevector = s->v.valc; | |
| bytes_to_write = s->char_count; | |
| break; | |
| } | |
| break; | |
| } | |
| if (NULL == bytevector) return -1; | |
| result = bintdo_write_value_rawint(tdo, bytes_to_write); | |
| bytes_written = (int) etch_flexbuf_put(tdo->flexbuf, bytevector, 0, bytes_to_write); | |
| result = bytes_written == bytes_to_write? 0: -1; | |
| if (is_new_memory) | |
| etch_free(bytevector); | |
| return result; | |
| } | |
| /** | |
| * bintdo_write_values() | |
| * write all values from the specified array | |
| */ | |
| int bintdo_write_values(binary_tagged_data_output* tdo, etch_arrayvalue* av, | |
| etch_validator* vtor) | |
| { | |
| int errs = 0; | |
| etch_validator* ev = vtor? vtor->element_validator(vtor): NULL; | |
| etch_iterator iterator; | |
| set_iterator(&iterator, av->list, &av->list->iterable); | |
| while(iterator.has_next(&iterator)) | |
| { | |
| errs += (0 != bintdo_write_value(tdo, ev, iterator.current_value)); | |
| iterator.next(&iterator); | |
| } | |
| return errs? -1: 0; | |
| } | |
| /** | |
| * bintdo_write_keys_values() | |
| * write key/value pairs from the struct to the buffer | |
| */ | |
| int bintdo_write_keys_values (binary_tagged_data_output* tdo, etch_structvalue* sv) | |
| { | |
| etch_type* struct_type = sv->struct_type; | |
| etch_validator* thisvtor = NULL; | |
| etch_field* thiskey = NULL; | |
| objmask* thisval = NULL; | |
| int result = 0; | |
| etch_iterator iterator; | |
| set_iterator(&iterator, sv->items, &sv->items->iterable); | |
| while(iterator.has_next(&iterator)) | |
| { | |
| thiskey = (etch_field*) iterator.current_key; | |
| thisval = (objmask*) iterator.current_value; | |
| ETCH_ASSERT(thiskey); | |
| thisvtor = (etch_validator*) | |
| etchtype_get_validator_by_name (struct_type, thiskey->name); | |
| if (NULL == thisvtor) | |
| { etchlog(ETCHBTDO, ETCHLOG_ERROR, "type '%s' missing validator '%s'\n", | |
| struct_type->aname, thiskey->aname); | |
| result = -1; | |
| break; | |
| } | |
| result = bintdo_write_value_rawint (tdo, thiskey->id); | |
| result = bintdo_write_value (tdo, thisvtor, thisval); | |
| if (-1 == result) break; | |
| iterator.next(&iterator); | |
| } | |
| return result; | |
| } | |
| /** | |
| * bintdo_write_value() | |
| * write specified value to the buffer | |
| * @param vtor validator for specified value, or null if none | |
| * @param value the value to be encoded and written, as a *non-disposable* object, | |
| * i.e. caller owns memory for the value object. | |
| */ | |
| int bintdo_write_value (binary_tagged_data_output* tdo, etch_validator* vtor, objmask* value) | |
| { | |
| int result = 0; | |
| size_t nout = 0; | |
| union_alltypes u; | |
| signed char external_typecode; | |
| if (config.is_validate_on_write) | |
| { /* we should disable validate on write in production */ | |
| if (NULL == value); /* don't recall why null value is not validated */ | |
| else | |
| if (!vtor || -1 == vtor->validate (vtor, (etch_object*) value)) | |
| { etchlog(ETCHBTDO, ETCHLOG_ERROR, "validation failed for type %x class %x\n", | |
| value->obj_type, value->class_id); | |
| return -1; | |
| } | |
| } | |
| /* determine tag (fyi signed only because using the java byte constants) */ | |
| external_typecode = etchtagdata_check_value((etch_object*) value); | |
| /* write tag */ | |
| if (sizeof(byte) != etch_flexbuf_put_byte(tdo->flexbuf, external_typecode)) | |
| return -1; | |
| switch(external_typecode) | |
| { | |
| case ETCH_XTRNL_TYPECODE_NULL: | |
| case ETCH_XTRNL_TYPECODE_NONE: | |
| case ETCH_XTRNL_TYPECODE_EMPTY_STRING: | |
| case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE: | |
| case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE: | |
| return 0; /* nothing to do, tag says it all */ | |
| case ETCH_XTRNL_TYPECODE_BYTE: | |
| if (0 == etchtagdata_byte_value((etch_object*) value, &u.vbyte)) | |
| nout = etch_flexbuf_put_byte(tdo->flexbuf, u.vbyte); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_INT: | |
| if (0 == etchtagdata_int32_value((etch_object*) value, &u.vint32)) | |
| nout = etch_flexbuf_put_int(tdo->flexbuf, u.vint32); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_LONG: | |
| if (0 == etchtagdata_int64_value((etch_object*) value, &u.vint64)) | |
| nout = etch_flexbuf_put_long(tdo->flexbuf, u.vint64); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_SHORT: | |
| if (0 == etchtagdata_int16_value((etch_object*) value, &u.vint16)) | |
| nout = etch_flexbuf_put_short(tdo->flexbuf, u.vint16); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_DOUBLE: | |
| if (0 == etchtagdata_double_value((etch_object*) value, &u.vdouble)) | |
| nout = etch_flexbuf_put_double(tdo->flexbuf, u.vdouble); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_FLOAT: | |
| if (0 == etchtagdata_float_value((etch_object*) value, &u.vfloat)) | |
| nout = etch_flexbuf_put_float(tdo->flexbuf, u.vfloat); | |
| break; | |
| case ETCH_XTRNL_TYPECODE_BYTES: | |
| /* we get an arrayvalue here. to do differently would be problematic | |
| * without rewriting higher levels to not work with arrayvalue. | |
| * perhaps we should simply pass arrayvalue to bintdo_write_bytes_from(). | |
| * TODO either accept a native array here, or change | |
| * bintdo_write_bytes_from() to accept an arrayvalue, or both. | |
| */ | |
| u.vnatarray = ((etch_arrayvalue*)value)->natarray; | |
| result = bintdo_write_bytes_from(tdo, u.vnatarray); | |
| return result; | |
| case ETCH_XTRNL_TYPECODE_ARRAY: | |
| /* if arriving here from client app we may get an etch_nativearray, | |
| * which we must convert to an arrayvalue now. | |
| * TODO write a version of bintdo_write_array which accepts a | |
| * nativearray, so we can avoid this to_arrayvalue() conversion. | |
| */ | |
| if (is_etch_nativearray(value)) | |
| u.varrayval = bintdo_to_arrayvalue((etch_nativearray*) value); | |
| else u.varrayval = (etch_arrayvalue*) value; | |
| /* fyi a null u.varrayval is handled by both bintdo_write_array() | |
| * and by ETCHOBJ_DESTROY() so we have omitted a null check here */ | |
| result = bintdo_write_array ((tagged_data_output*)tdo, u.varrayval, vtor); | |
| /* we want to destroy the array object only if it is a copy we made here; | |
| * otherwise the message owns it as a value, and will destroy it. */ | |
| if (is_etch_nativearray(value)) | |
| ETCHOBJ_DESTROY(u.varrayval); | |
| return result; | |
| case ETCH_XTRNL_TYPECODE_STRING: | |
| { etch_string* s = (etch_string*) value; | |
| result = bintdo_write_string(tdo, s); | |
| return result; | |
| } | |
| case ETCH_XTRNL_TYPECODE_CUSTOM: | |
| { | |
| etch_structvalue* sv = tdo->vf->vtab->export_custom_value(tdo->vf, value); | |
| if (NULL == sv) return -1; | |
| result = bintdo_write_struct((tagged_data_output*) tdo, sv); | |
| sv->destroy(sv); | |
| return result; | |
| } | |
| default: | |
| return is_inrange_tiny(external_typecode)? 0: -1; | |
| } | |
| return nout? 0: -1; | |
| } | |