You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by jd...@apache.org on 2009/04/22 19:25:51 UTC
svn commit: r767594 [13/43] - in /incubator/etch/trunk/binding-c/runtime/c:
./ ext/ ext/hashtab/ ext/lib/ inc/ lib/ project/ project/$etchstop/
project/bin/ project/etch/ project/logcli/ project/logsrv/ project/notes/
project/test/ project/test/logcli/...
Added: incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdi.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdi.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdi.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdi.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1195 @@
+/* $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_tdi.c -- binary tagged data input implementation.
+ */
+
+#include "etch_binary_tdi.h"
+#include "etch_global.h"
+#include "etch_structval.h"
+#include "etch_arrayval.h"
+#include "etch_tagdata.h"
+#include "etch_defvalufact.h"
+#include "etch_encoding.h"
+#include "etchexcp.h"
+#include "etchlog.h"
+char* ETCHBTDI = "BTDI";
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * private method signatures
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+binary_tagged_data_input* clone_binary_tagged_data_input(binary_tagged_data_input*);
+i_binary_tdi* new_binarytdi_vtable();
+int destroy_binary_tagged_data_input(binary_tagged_data_input*);
+
+etch_message* bintdi_start_message (tagged_data_input*);
+int bintdi_end_message (tagged_data_input*, etch_message*);
+etch_message* bintdi_read_message(tagged_data_input*, etch_flexbuffer*);
+
+etch_structvalue* bintdi_start_struct(tagged_data_input*);
+etch_structvalue* bintdi_read_struct (tagged_data_input*);
+int bintdi_end_struct (tagged_data_input*, etch_structvalue*);
+
+etch_arrayvalue* bintdi_start_array(tagged_data_input*);
+etch_arrayvalue* bintdi_read_array (tagged_data_input*, etch_validator*);
+int bintdi_end_array(tagged_data_input*, etch_arrayvalue*);
+
+etch_arrayvalue* bintdi_alloc_array(tagged_data_input*, const byte array_content_type,
+ etch_type* custom_type, const int ndims, const int nelements);
+
+int bintdi_read_keys_values(binary_tagged_data_input*, etch_structvalue*);
+int bintdi_read_values(binary_tagged_data_input*, etch_arrayvalue*, etch_validator*);
+etch_object* bintdi_read_value (binary_tagged_data_input*, etch_validator*);
+etch_object* bintdi_read_valuex(binary_tagged_data_input*, etch_validator*, boolean);
+etch_type* bintdi_get_custom_structtype(etch_object*,
+ const unsigned short, const unsigned short);
+etch_type* bintdi_read_type(binary_tagged_data_input*);
+etch_int32* bintdi_read_intvalue(binary_tagged_data_input*);
+int bintdi_read_value_rawint(binary_tagged_data_input* tdi, int* out);
+
+etch_validator* bintdi_get_validator_for(binary_tagged_data_input* tdi, etch_field* field);
+etch_object* bintdi_validate_value(binary_tagged_data_input*,
+ etch_validator*, boolean is_none_ok, etch_object*);
+etch_object* bintdi_validate_valuex(binary_tagged_data_input*,
+ etch_validator*, boolean is_none_ok, etch_object*);
+
+int bintdi_get_native_type(const signed char external_typecode, etch_array_id_params* out);
+int bintdi_get_component_type(tagged_data_input*, const byte array_content_type,
+ etch_type* custom_type, const int dims, etch_array_id_params* out);
+byte bintagdata_get_native_typecode(const unsigned short, const unsigned short);
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_binary_tagdata_input()
+ * binary_tagged_data_input constructor
+ */
+binary_tagged_data_input* new_binary_tagdata_input(etch_value_factory* vf)
+{
+ i_binary_tdi* vtab = NULL;
+
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) new_object
+ (sizeof(binary_tagged_data_input), ETCHTYPEB_BINARYTDI, CLASSID_BINARYTDI);
+
+ tdi->destroy = destroy_binary_tagged_data_input;
+ tdi->clone = clone_binary_tagged_data_input;
+ tdi->vf = vf;
+
+ vtab = cache_find(get_vtable_cachehkey(CLASSID_BINARYTDI_VTAB), 0);
+
+ if(!vtab)
+ { vtab = new_binarytdi_vtable();
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ tdi->vtab = vtab;
+ tdi->vtor_int = etchvtor_int32_get(0);
+ tdi->static_nullobj = etchtagdata_new_nullobj(TRUE);
+ tdi->static_eodmarker = etchtagdata_new_eodmarker(TRUE);
+ tdi->static_emptystring = etchtagdata_new_emptystring(TRUE);
+ return tdi;
+}
+
+
+/**
+ * new_binary_tdi()
+ * casts result to generic tdi for use by interfaces
+ */
+tagged_data_input* new_binary_tdi(etch_value_factory* vf)
+{
+ return (tagged_data_input*) new_binary_tagdata_input(vf);
+}
+
+
+/**
+ * destroy_binary_tagged_data_input()
+ */
+int destroy_binary_tagged_data_input(binary_tagged_data_input* tdi)
+{
+ if (tdi->refcount > 0 && --tdi->refcount > 0) return -1;
+
+ if (!is_etchobj_static_content(tdi))
+ if (tdi->impl)
+ tdi->impl->destroy(tdi->impl);
+
+ /* destroy private instance data */
+ etch_free(tdi->static_nullobj);
+ etch_free(tdi->static_eodmarker);
+ clear_etchobj_static_all(tdi->static_emptystring);
+ tdi->static_emptystring->destroy(tdi->static_emptystring);
+
+ return destroy_objectex((objmask*)tdi);
+}
+
+
+/**
+ * clone_tagged_data_input()
+ * tdi copy constructor. if the tdi object implements a separate instance data
+ * object, that object is cloned as well.
+ */
+binary_tagged_data_input* clone_binary_tagged_data_input(binary_tagged_data_input* tdi)
+{
+ binary_tagged_data_input* newtdi = (binary_tagged_data_input*) clone_object((objmask*) tdi);
+ newtdi->impl = tdi->impl? tdi->impl->clone(tdi->impl): NULL;
+ return newtdi;
+}
+
+
+/**
+ * new_new_binarytdi_vtable()
+ * constructor for binary tdi virtual function table
+ */
+i_binary_tdi* new_binarytdi_vtable()
+{
+ etchparentinfo* inheritlist = new_etch_inheritance_list(3, 2, NULL);
+
+ i_binary_tdi* vtab
+ = new_vtable(NULL, sizeof(i_binary_tdi), CLASSID_BINARYTDI_VTAB);
+
+ /* i_tagged_data_input */
+ vtab->start_message = bintdi_start_message;
+ vtab->read_message = bintdi_read_message;
+ vtab->end_message = bintdi_end_message;
+ vtab->start_struct = bintdi_start_struct;
+ vtab->read_struct = bintdi_read_struct;
+ vtab->end_struct = bintdi_end_struct;
+ vtab->start_array = bintdi_start_array;
+ vtab->read_array = bintdi_read_array;
+ vtab->end_array = bintdi_end_array;
+
+ /* i_tagdata */
+ vtab->check_value = etchtagdata_check_value;
+ vtab->get_native_type = bintdi_get_native_type;
+ vtab->get_native_type_code = bintagdata_get_native_typecode;
+ vtab->get_custom_structtype = bintdi_get_custom_structtype;
+
+ /* inheritance */
+ inheritlist[1].obj_type = ETCHTYPEB_TAGDATAINP;
+ inheritlist[1].class_id = CLASSID_TAGDATAINP;
+ inheritlist[2].obj_type = ETCHTYPEB_TAGDATA;
+ inheritlist[2].class_id = CLASSID_TAGDATA;
+
+ return vtab;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read message
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_message()
+ * starts reading a message from the stream.
+ * returns NULL if the wire data was insufficient to construct a
+ * message object, otherwise returns the new message object,
+ * of the type read off the wire herein.
+ */
+etch_message* bintdi_start_message(tagged_data_input* tdix)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ etch_type* typeobj = NULL; /* not owned here */
+ etch_message* newmsg = NULL; /* disposable, returned */
+ int message_itemcount = 0, result = 0;
+ signed char wire_version = 0;
+
+ do
+ { result = etch_flexbuf_get_byte (tdi->flexbuf, &wire_version);
+
+ if (wire_version != ETCH_BINTAGDATA_CURRENT_VERSION)
+ { etchlog(ETCHBTDI, ETCHLOG_ERROR,"message version expected %d found %d",
+ ETCH_BINTAGDATA_CURRENT_VERSION, wire_version);
+ break;
+ }
+
+ /* bintdi_read_type returns us a non-disposable reference to a global type,
+ * which we then give to the new message, which does not own its type.
+ */
+ if (NULL == (typeobj = bintdi_read_type (tdi))) break;
+
+ if (-1 != bintdi_read_value_rawint (tdi, &message_itemcount))
+ newmsg = new_message(typeobj, message_itemcount, tdi->vf);
+
+ } while(0);
+
+ if (newmsg)
+ etchlog(ETCHBTDI, ETCHLOG_DEBUG, "message receive starts\n");
+ else etchlog(ETCHBTDI, ETCHLOG_ERROR, "error starting message receive\n");
+
+ return newmsg;
+}
+
+
+/**
+ * bintdi_end_message()
+ * end message deserialization
+ */
+int bintdi_end_message(tagged_data_input* tdi, etch_message* msg)
+{
+ int result = 0;
+ /* if the newly-deserialized message is not a reply message,
+ * there is nothing to do here, since bintdi_read_keys_values()
+ * already read the eod marker. if it is a reply message, and
+ * if the message contains an exception, we will ensure that the
+ * message result object both exists and reflects that exception.
+ */
+
+ if (message_get_in_reply_to (msg))
+ {
+ etch_field* xkey = builtins._mf_msg;
+ /* look for an exception in the newly-deserialized message */
+ etch_exception* excpobj = (etch_exception*) message_get (msg, xkey);
+
+ if (is_etch_exception(excpobj))
+ { /* look for a result object in the message */
+ etch_field* resobj_key = builtins._mf_result;
+ objmask* resobj = message_get (msg, resobj_key);
+ etchlog(ETCHBTDI, ETCHLOG_DEBUG, "exception found in message\n");
+
+ /* if no result object, make the exception the result object */
+ if (NULL == resobj)
+ { resobj = excpobj->clone(excpobj);
+ result = message_put (msg, resobj_key, resobj);
+ }
+
+ /* ensure that the result object reflects the exception */
+ if (!is_exception(resobj))
+ { etchexception* x = (etchexception*) excpobj->value;
+ etch_throw (resobj, x->excptype, x->excptext, ETCHEXCP_COPYTEXT);
+ }
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * bintdi_read_message()
+ * "non-static" read message version, accepts a tdi
+ */
+etch_message* bintdi_read_message (tagged_data_input* tdix, etch_flexbuffer* fbuf)
+{
+ etch_message* newmsg = NULL;
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ tdi->flexbuf = fbuf;
+
+ if (NULL == (newmsg = bintdi_start_message(tdix)))
+ return NULL;
+
+ /* todo verify that this deserialization failure is eventually session_notify'ed */
+ if (-1 == bintdi_read_keys_values (tdi, newmsg->sv))
+ { newmsg->destroy(newmsg);
+ return NULL;
+ }
+
+ bintdi_end_message(tdix, newmsg);
+ return newmsg;
+}
+
+
+/**
+ * bintdi_read_message_fromf()
+ * reads a message from the supplied flex buffer
+ */
+etch_message* bintdi_read_message_fromf (etch_value_factory* vf, etch_flexbuffer* fbuf)
+{
+ binary_tagged_data_input* tdi = new_binary_tagdata_input(vf);
+
+ return bintdi_read_message((tagged_data_input*) tdi, fbuf);
+}
+
+
+/**
+ * bintdi_read_message_from()
+ * reads a message from the supplied data buffer
+ */
+etch_message* bintdi_read_message_from (etch_value_factory* vf, byte* buf,
+ const int bufsize)
+{
+ etch_flexbuffer* fbuf = new_flexbuffer_from(buf, bufsize, bufsize, 0);
+
+ return bintdi_read_message_fromf(vf, fbuf);
+}
+
+
+/**
+ * bintdi_read_message_fromo()
+ * reads a message from the supplied data buffer, at the specified offset
+ */
+etch_message* bintdi_read_message_fromo (etch_value_factory* vf, byte* buf,
+ const int bufsize, const int msglen, const int offset)
+{
+ etch_flexbuffer* fbuf = new_flexbuffer_from(buf, bufsize, msglen, offset);
+
+ return bintdi_read_message_fromf(vf, fbuf);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read struct
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_struct()
+ * starts reading a struct from the stream.
+ */
+etch_structvalue* bintdi_start_struct(tagged_data_input* tdix)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ etch_structvalue* newsv = NULL;
+ etch_type* thistype = NULL;
+ int struct_itemcount = 0;
+
+ /* bintdi_read_type returns us a non-disposable reference to a global type,
+ * which we then give to the new structvalue, which does not own its type.
+ */
+ if (NULL == (thistype = bintdi_read_type(tdi))) return NULL;
+
+ if (-1 != bintdi_read_value_rawint(tdi, &struct_itemcount))
+ newsv = new_structvalue(thistype, struct_itemcount);
+
+ return newsv;
+}
+
+
+/**
+ * bintdi_end_struct()
+ * does nothing since read_keys_values read to end of stream
+ */
+int bintdi_end_struct(tagged_data_input* tdi, etch_structvalue* sv)
+{
+ return 0;
+}
+
+
+/**
+ * bintdi_read_struct()
+ * read a struct out of the buffer, create and return a new struct value
+ */
+etch_structvalue* bintdi_read_struct(tagged_data_input* tdix)
+{
+ int result = 0;
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+
+ etch_structvalue* newsv = tdi->vtab->start_struct((tagged_data_input*) tdi);
+ if (NULL == newsv) return NULL;
+
+ if (-1 == bintdi_read_keys_values(tdi, newsv))
+ { newsv->destroy(newsv);
+ return NULL;
+ }
+
+ bintdi_end_struct(tdix, newsv);
+ return newsv;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read array
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_array()
+ */
+etch_arrayvalue* bintdi_start_array(tagged_data_input* tdix)
+{
+ etch_type* custom_type = NULL; /* not owned here */
+ etch_arrayvalue* arrayval = NULL; /* owned by caller */
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ int numdimensions = 0, numelements = 0, result = 0;
+ signed char array_content_type = 0;
+
+ result = etch_flexbuf_get_byte(tdi->flexbuf, &array_content_type);
+
+ switch(array_content_type)
+ { case ETCH_XTRNL_TYPECODE_CUSTOM:
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ /* bintdi_read_type returns a non-disposable type reference */
+ custom_type = bintdi_read_type(tdi);
+ if (NULL == custom_type) return NULL;
+ }
+
+ bintdi_read_value_rawint(tdi, &numdimensions);
+ bintdi_read_value_rawint(tdi, &numelements);
+
+ /* create the arrayvalue. we pass no memory ownership here */
+ arrayval = bintdi_alloc_array(tdix, array_content_type, custom_type,
+ numdimensions, numelements);
+
+ return arrayval;
+}
+
+
+/**
+ * bintdi_end_array()
+ * ends array in progress
+ */
+int bintdi_end_array(tagged_data_input* tdi, etch_arrayvalue* x)
+{
+ return 0;
+}
+
+
+/**
+ * bintdi_read_array()
+ * read an array out of the buffer, create and return a new array object
+ *
+ * todo (eventually). arrays are not optimal. following the java binding model,
+ * an array on the wire is read into an array_value. in c, this means an array
+ * of objects, one object per array element. this can make for very large data
+ * structures, 60 bytes of overhead per array element. we would like to instead
+ * use the etch_nativearray format, where an array is a blob of bytes, with
+ * attributes and methods describing how to index into the array. however
+ * it was not known if doing so would paint the c binding into a corner at
+ * some point, so for now, we create the arrayvalue just as the java binding,
+ * and export that structure to an etch_nativearray when and if we need it.
+ */
+etch_arrayvalue* bintdi_read_array(tagged_data_input* tdix, etch_validator* vtor)
+{
+ int result = 0;
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+
+ etch_arrayvalue* newarray = bintdi_start_array((tagged_data_input*) tdi);
+ if (NULL == newarray) return NULL;
+
+ if (-1 == bintdi_read_values(tdi, newarray, vtor))
+ { newarray->destroy(newarray);
+ return NULL;
+ }
+
+ bintdi_end_array((tagged_data_input*)tdi, newarray);
+
+ return newarray;
+}
+
+
+/**
+ * bintdi_alloc_array()
+ * allocate an arrayvalue in which to read data for the pending array.
+ * java binding allocates a native array here. we can't do that yet since we
+ * don't have the attributes of each dimension until we get the entire array
+ * out of the stream buffer, and we want to avoid lookahead if possible.
+ * @return new array object, or NULL indicating exception condition
+ */
+etch_arrayvalue* bintdi_alloc_array(tagged_data_input* tdi, const byte array_content_type,
+ etch_type* custom_type, const int ndims, const int nelements)
+{
+ etch_array_id_params arraytype;
+ etch_arrayvalue* arrayval = NULL;
+
+ if (-1 == bintdi_get_component_type(tdi, array_content_type,
+ custom_type, ndims, &arraytype))
+ return NULL;
+
+ /* remarks regarding ownership of content for these arrayvalue objects.
+ * first, recall that their content is etch objects wrapping data read from
+ * the data buffer, and that the data so wrapped is owned by the wrapper.
+ * ownership of memory for those base data objects is assigned up the line
+ * until some object assumes responsibility. content for these arrayvalues
+ * is either those wrapper objects, at dimension[0], or arrayvalue objects
+ * at the higher dimensions. in all cases, the arryavalue owns its content,
+ * and therefore its content will be destroyed with the arrayvalue. since
+ * the destruction is recursive, destroying the top object destroys it all.
+ */
+ arrayval = new_arrayvalue(array_content_type, custom_type,
+ ndims, nelements, 0, FALSE, ETCHARRAYLIST_SYNCHRONIZED);
+
+ if (NULL == arrayval) return NULL;
+
+ /* validator will validate array based on this class */
+ arrayval->class_id = arraytype.array_class_id;
+
+ /* content type will be used later when we need to create a nativearray */
+ arrayval->content_obj_type = arraytype.content_obj_type;
+ arrayval->content_class_id = arraytype.content_class_id;
+ return arrayval;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * read from stream and reconstruct objects
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_intvalue()
+ * convenience method used when an encoded integer is expected next in
+ * the buffer, to read that value from the buffer and return it as an
+ * etch_int32 object, ownership of which belongs to the caller.
+ */
+etch_int32* bintdi_read_intvalue(binary_tagged_data_input* tdi)
+{
+ return (etch_int32*) bintdi_read_value(tdi, tdi->vtor_int);
+}
+
+
+/**
+ * bintdi_read_bytes()
+ * read all bytes from the buffer, returning those bytes
+ * @param extra pad bytes e.g. when we are reading a string and need a null term
+ */
+byte* bintdi_read_bytes(binary_tagged_data_input* tdi, const int extra, int* psize)
+{
+ byte* buf = NULL;
+ int bytecount = 0, newbufsize = 0;
+
+ if (-1 == bintdi_read_value_rawint(tdi, &bytecount)) return NULL;
+
+ newbufsize = bytecount + extra;
+
+ buf = etch_malloc(newbufsize, ETCHTYPEB_BYTES);
+ memset(buf, 0, newbufsize);
+
+ etch_flexbuf_get_fully(tdi->flexbuf, buf, bytecount);
+
+ if (psize) *psize = newbufsize;
+
+ return buf;
+}
+
+
+/**
+ * bintdi_read_type()
+ * read a type ID from the buffer, map to and return the associated static type
+ */
+etch_type* bintdi_read_type(binary_tagged_data_input* tdi)
+{
+ etch_type* type = NULL;
+ int type_id = 0;
+
+ if (0 == bintdi_read_value_rawint(tdi, &type_id))
+ type = tdi->vf->vtab->get_type_by_id(tdi->vf, type_id);
+
+ /* note that the type object returned by valuefactory.get_type_by_id
+ * is not disposable, it is a reference into the global types map.
+ * and we are returning that nondisposable reference here */
+
+ return type;
+}
+
+
+/**
+ * bintdi_read_bytearray()
+ * read all bytes from the buffer, return a native array wrapping the resultant byte array
+ * todo: is this OK to return nativearray in one case, and arrayvalue in another?
+ * can we handle the nativearray format across the board? we can at least use nativearray
+ * for byte blobs. even if we use arravalue format we should at least use nativearray
+ * as the base of the arrayvalue, since if we need to be able to access elements, we can't
+ * simply reflect to an array as does java, we need the subscripting of the nativearray.
+ */
+etch_nativearray* bintdi_read_bytearray(binary_tagged_data_input* tdi)
+{
+ int size = 0;
+ etch_nativearray* newarray = NULL;
+ byte* buf = bintdi_read_bytes(tdi, 0, &size); /* buf is disposable, newarray will own it */
+ newarray = new_nativearray_from(buf, CLASSID_ARRAY_BYTE, sizeof(byte), 1, size, 0, 0);
+ newarray->is_content_owned = TRUE;
+ return newarray;
+}
+
+
+/**
+ * bintdi_read_string()
+ * read all bytes from the buffer and translate to unicode C string
+ * @return the wrapped string
+ */
+etch_string* bintdi_read_string(binary_tagged_data_input* tdi)
+{
+ int result = 0, size = 0;
+ wchar_t* widestring = NULL;
+ etch_string* newstr = NULL;
+ byte* buf = NULL;
+
+ const int configured_encoding
+ = tdi->vf? get_etch_string_encoding(tdi->vf): ETCH_ENCODING_DEFAULT;
+
+ const int nulltermsize = configured_encoding == ETCH_ENCODING_UTF8?
+ sizeof(wchar_t): sizeof(char);
+
+ buf = bintdi_read_bytes(tdi, nulltermsize, &size); /* we own this memory */
+
+ switch(configured_encoding)
+ { case ETCH_ENCODING_UTF8: /* string on the wire is utf-8 */
+ result = etch_utf8_to_unicode(&widestring, buf);
+ etch_free(buf);
+ break;
+ case ETCH_ENCODING_ASCII: /* string on the wire is ascii */
+ result = etch_ansi_to_unicode(&widestring, buf);
+ etch_free(buf);
+ break; /* string on the wire is utf-16 */
+ default: widestring = (wchar_t*) buf;
+ }
+
+ /* construct string object, relinquishing ownership of string buffer */
+ newstr = new_string_from(widestring, ETCH_ENCODING_UTF16);
+ return newstr;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read structured content
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_keys_values()
+ * deserializes a message from a buffer.
+ * read all key/value pairs from buffer, populating the specified struct.
+ * @param tdi caller retains.
+ * @param sv caller retains.
+ * @return 0 success, -1 deserialization failed, caller should throw exception.
+ */
+int bintdi_read_keys_values (binary_tagged_data_input* tdi, etch_structvalue* sv)
+{
+ etch_type* svtype = sv->struct_type;
+ etch_validator* thisvtor = NULL; /* non-disposable ref to type vtor */
+ etch_object* thisobj = NULL; /* disposable return from read_value */
+ etch_object* thisvalue = NULL; /* disposable return from read_value */
+ etch_field* key_field = NULL; /* non-disposable ref to static field */
+ etch_field* key_clone = NULL; /* disposable copy of structvalue key */
+ etch_int32* this_idobj = NULL; /* non-disposable copy of thisobj */
+ int result = 0;
+
+ while(result == 0)
+ {
+ thisobj = bintdi_read_valuex(tdi, tdi->vtor_int, TRUE); /* disposable */
+ if (etchtagdata_is_eod (thisobj)) break; /* data end marker found */
+
+ key_clone = NULL; thisvalue = NULL; result = -1;
+
+ this_idobj = (etch_int32*) thisobj;
+ key_field = etchtype_get_field_by_id (svtype, this_idobj->value);
+
+ if (NULL == key_field)
+ { etchlog(ETCHBTDI, ETCHLOG_ERROR,"field lookup failed\n");
+ break;
+ }
+
+ thisvtor = (etch_validator*) etchtype_get_validator_by_name
+ (svtype, key_field->name); /* returns us a non-disposable ref */
+
+ thisvalue = bintdi_read_value(tdi, thisvtor); /* returns a disposable ref */
+ if (NULL == thisvalue) break; /* validation or other deserialization error */
+
+ key_clone = key_field->clone (key_field);
+
+ /* structvalue_put() expects disposable key and value objects, i.e. it will
+ * call destructors on the objects when the struct is destroyed. however if
+ * the put() fails, we still own the objects, which are accounted for below.
+ * note also that this contract differs from the etch_message interface to
+ * a struct, which eats its put parameters regardless of outcome.
+ */
+ result = structvalue_put (sv, key_clone, (objmask*) thisvalue);
+
+ /* if put was OK we have relinquished ownership of valuobj */
+ /* key_field was relinquished regardless. */
+
+ ETCHOBJ_DESTROY(thisobj);
+ }
+
+ if (-1 == result)
+ { /* some error, usually failed validation */
+ etchlog(ETCHBTDI, ETCHLOG_ERROR,"message deserialization failed\n");
+ ETCHOBJ_DESTROY(key_clone);
+ ETCHOBJ_DESTROY(thisvalue);
+ }
+
+ ETCHOBJ_DESTROY(thisobj);
+ return result;
+}
+
+
+/**
+ * bintdi_read_values()
+ * read values from buffer, populating the specified array with the values so read
+ */
+int bintdi_read_values (binary_tagged_data_input* tdi,
+ etch_arrayvalue* av, etch_validator* vtor)
+{
+ etch_validator* ev = vtor? vtor->element_validator(vtor): NULL;
+ etch_object* thisobj = 0;
+ int result = 0;
+ if (!av || !vtor) return -1;
+
+ while(result == 0)
+ {
+ thisobj = bintdi_read_valuex(tdi, ev, TRUE);
+
+ if (NULL == thisobj) return -1;
+ if (etchtagdata_is_eod(thisobj)) break;
+
+ /* relinquish ownership of thisobj to the arrayvalue */
+ result = arrayvalue_add(av, thisobj);
+ if (0 == result) thisobj = NULL;
+ }
+
+ if (thisobj) thisobj->destroy(thisobj);
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read tokens
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_value()
+ */
+etch_object* bintdi_read_value(binary_tagged_data_input* tdi, etch_validator* vtor)
+{
+ return bintdi_read_valuex(tdi, vtor, FALSE);
+}
+
+
+/**
+ * bintdi_read_valuex()
+ * reads a tag byte from the buffer, and based on the tag, reads zero or more
+ * bytes from the buffer, appropriate to the data type indicated by the tag.
+ * @return a *disposable* object which appropriately wraps the bytes or object
+ * read from input buffer. however if the data tag indicated a null object, a
+ * nullobj is returned, or if the tag indicated end of data, an eod object is
+ * returned. if validation fails on the object, NULL is returned, otherwise an
+ * object is always returned on which the caller is expected to call destroy().
+ */
+etch_object* bintdi_read_valuex (binary_tagged_data_input* tdi, etch_validator* v,
+ boolean is_none_ok)
+{ /* if this method returns NULL it must first destroy any object created
+ * herein. normally this is accomplished in bintdi_validate_value.
+ */
+ union_alltypes u;
+ signed char objtype = 0;
+
+ if (-1 == etch_flexbuf_get_byte (tdi->flexbuf, &objtype)) return NULL;
+
+ switch(objtype)
+ {
+ case ETCH_XTRNL_TYPECODE_NULL:
+ /* returns the instance null object. it is considered disposable because
+ * caller can and will call destroy() on it. however the destructor will
+ * have no effect, the null object is destroyed in the tdi destructor.*/
+ return (etch_object*) bintdi_validate_valuex (tdi, v, FALSE,
+ (etch_object*) tdi->static_nullobj);
+
+ case ETCH_XTRNL_TYPECODE_NONE:
+ /* returns the instance eod object. it is considered disposable because
+ * caller can and will call destroy() on it. however the destructor will
+ * have no effect, the eod object is destroyed in the tdi destructor. */
+ return bintdi_validate_value(tdi, v, is_none_ok,
+ (etch_object*) tdi->static_eodmarker);
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_boolean(FALSE));
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_boolean(TRUE));
+
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ if (-1 == etch_flexbuf_get_byte(tdi->flexbuf, &u.vbyte)) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_byte(u.vbyte));
+
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ if (-1 == etch_flexbuf_get_short(tdi->flexbuf, &u.vint16)) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_int16(u.vint16));
+
+ case ETCH_XTRNL_TYPECODE_INT:
+ if (-1 == etch_flexbuf_get_int(tdi->flexbuf, &u.vint32)) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_int32(u.vint32));
+
+ case ETCH_XTRNL_TYPECODE_LONG:
+ if (-1 == etch_flexbuf_get_long(tdi->flexbuf, &u.vint64)) break;
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_int64(u.vint64));
+
+ case ETCH_XTRNL_TYPECODE_FLOAT:
+ if (-1 == etch_flexbuf_get_float(tdi->flexbuf, &u.vfloat)) break;
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_float(u.vfloat));
+
+ case ETCH_XTRNL_TYPECODE_DOUBLE:
+ if (-1 == etch_flexbuf_get_double(tdi->flexbuf, &u.vdouble)) break;
+ return bintdi_validate_value(tdi, v, FALSE, (void*) new_double(u.vdouble));
+
+ case ETCH_XTRNL_TYPECODE_BYTES:
+ /* must return arrayvalue for symmetry with tdo */
+ /* todo modify arrayvalue to not populate objects when so requested */
+ u.vnatarray = bintdi_read_bytearray(tdi);
+ u.varrayval = new_arrayvalue_from(u.vnatarray, ETCH_XTRNL_TYPECODE_BYTES,
+ NULL, (int) u.vnatarray->bytecount, 0, FALSE);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.varrayval);
+
+ case ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) tdi->static_emptystring);
+
+ case ETCH_XTRNL_TYPECODE_STRING:
+ u.vstring = bintdi_read_string(tdi);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.vstring);
+
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ u.vsv = bintdi_read_struct((void*) tdi);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.vsv);
+
+ case ETCH_XTRNL_TYPECODE_ARRAY:
+ u.varrayval = bintdi_read_array((tagged_data_input*) tdi, v);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.varrayval);
+
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ {
+ etch_structvalue* keys_values /* acquire struct */
+ = bintdi_read_struct((tagged_data_input*) tdi);
+
+ objmask* reconstituted_object /* relinquish struct */
+ = tdi->vf->vtab->import_custom_value(tdi->vf, keys_values);
+
+ return bintdi_validate_value (tdi, v, FALSE, (void*) reconstituted_object);
+ }
+
+ default:
+ if (is_inrange_tiny(objtype))
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_byte(objtype));
+ }
+
+ return NULL;
+}
+
+
+/**
+ * bintdi_read_value_rawint()
+ * read an integer value from the buffer, returning the 32-bit primitive
+ * in the out parameter.
+ * @param out a pointer to an int to receive the value read from the buffer.
+ * @return 0 success, -1 if an integer could not be read from the buffer
+ */
+int bintdi_read_value_rawint(binary_tagged_data_input* tdi, int* out)
+{
+ int thisint = 0, result = 0;
+ signed char objtype = 0;
+ union_alltypes u;
+
+ if (0 != etch_flexbuf_get_byte(tdi->flexbuf, &objtype))
+ result = -1;
+ else
+ if (is_inrange_tiny(objtype))
+ thisint = objtype;
+ else switch(objtype)
+ {
+ case ETCH_XTRNL_TYPECODE_INT:
+ if (0 == (result = etch_flexbuf_get_int(tdi->flexbuf, &u.vint32)))
+ thisint = u.vint32;
+ break;
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ if (0 == (result = etch_flexbuf_get_short(tdi->flexbuf, &u.vint16)))
+ thisint = u.vint16;
+ break;
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ if (0 == (result = etch_flexbuf_get_byte(tdi->flexbuf, &u.vbyte)))
+ thisint = u.vbyte;
+ break;
+ default: result = -1;
+ }
+
+ *out = thisint;
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * utility methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_get_component_type()
+ *
+ * @return etch c obj_type and class_id of an array of specified external type,
+ * and its content, or -1 indicating exception condition.
+ */
+int bintdi_get_component_type(tagged_data_input* tdi, const byte array_content_type,
+ etch_type* custom_type, const int dims, etch_array_id_params* out)
+{
+ int result = 0;
+ unsigned int classx = 0;
+ if (NULL == out) return -1;
+ memset(out, 0, sizeof(etch_array_id_params));
+
+ switch(array_content_type)
+ {
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ out->array_obj_type = ETCHTYPEB_ARRAYVAL;
+ out->array_class_id = CLASSID_ARRAY_OBJECT;
+
+ if((NULL != custom_type)
+ && (0 != (classx = etchtype_get_component_type(custom_type))))
+ ETCHGETCLASS(classx, out->content_obj_type, out->content_class_id);
+
+ if (0 == out->content_obj_type)
+ { out->content_obj_type = ETCHTYPEB_STRUCTVAL;
+ out->content_class_id = CLASSID_STRUCTVALUE;
+ }
+
+ default:
+ result = bintdi_get_native_type(array_content_type, out);
+ }
+
+ return result;
+
+}
+
+
+/*
+ * bintdi_get_native_type()
+ * returns the internal object types and class ids corresponding to the
+ * external byte typecode indicating content type of an array on the wire.
+ * class_id may or may not be significant, depending on particular obj_type.
+ */
+int bintdi_get_native_type(const signed char external_typecode, etch_array_id_params* out)
+{
+ int result = 0;
+ memset(out, 0, sizeof(etch_array_id_params));
+ out->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ out->array_obj_type = ETCHTYPEB_NATIVEARRAY;
+
+
+ switch(external_typecode)
+ {
+ case ETCH_XTRNL_TYPECODE_INT:
+ out->content_class_id = CLASSID_PRIMITIVE_INT32;
+ out->array_class_id = CLASSID_ARRAY_INT32;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_STRING:
+ case ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ out->content_class_id = CLASSID_STRING;
+ out->array_class_id = CLASSID_ARRAY_STRING;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ out->content_class_id = CLASSID_PRIMITIVE_BYTE;
+ out->array_class_id = CLASSID_ARRAY_BYTE;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_LONG:
+ out->content_class_id = CLASSID_PRIMITIVE_INT64;
+ out->array_class_id = CLASSID_ARRAY_INT64;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ out->content_class_id = CLASSID_PRIMITIVE_INT16;
+ out->array_class_id = CLASSID_ARRAY_INT16;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_DOUBLE:
+ out->content_class_id = CLASSID_PRIMITIVE_DOUBLE;
+ out->array_class_id = CLASSID_ARRAY_DOUBLE;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE:
+ out->content_class_id = CLASSID_PRIMITIVE_BOOL;
+ out->array_class_id = CLASSID_ARRAY_BOOL;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_FLOAT:
+ out->content_class_id = CLASSID_PRIMITIVE_FLOAT;
+ out->array_class_id = CLASSID_ARRAY_FLOAT;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_ANY:
+ case ETCH_XTRNL_TYPECODE_ARRAY:
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ case ETCH_XTRNL_TYPECODE_NULL:
+ out->content_obj_type = ETCHTYPEB_ETCHOBJECT;
+ out->content_class_id = CLASSID_OBJECT;
+ out->array_class_id = CLASSID_ARRAY_OBJECT;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BYTES:
+ out->content_obj_type = ETCHTYPEB_ARRAYVAL;
+ out->content_class_id = CLASSID_ARRAY_BYTE;
+ out->array_class_id = CLASSID_ARRAY_BYTE;
+ break;
+
+ default:
+ if (is_inrange_tiny(external_typecode))
+ {
+ out->content_class_id = CLASSID_PRIMITIVE_INT8;
+ out->array_class_id = CLASSID_ARRAY_INT8;
+ }
+ else
+ { out->content_obj_type = ETCHTYPEB_NONE;
+ out->content_class_id = CLASSID_NONE;
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * bintagdata_get_native_typecode()
+ * returns the external type code corresponding to internal type.
+ * etchtagdata_get_native_typecode() override
+ */
+byte bintagdata_get_native_typecode
+ (const unsigned short obj_type, const unsigned short class_id)
+{
+ byte xtype = 0;
+
+ static const byte primitives[10]
+ = {ETCH_XTRNL_TYPECODE_CUSTOM, /* CLASSID_NONE = 0x0 */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_PRIMITIVE_BYTE = 0x1 */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, /* CLASSID_PRIMITIVE_BOOL = 0x2 */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_PRIMITIVE_INT8 = 0x3 */
+ ETCH_XTRNL_TYPECODE_SHORT, /* CLASSID_PRIMITIVE_INT16 = 0x4 */
+ ETCH_XTRNL_TYPECODE_INT, /* CLASSID_PRIMITIVE_INT32 = 0x5 */
+ ETCH_XTRNL_TYPECODE_LONG, /* CLASSID_PRIMITIVE_INT64 = 0x6 */
+ ETCH_XTRNL_TYPECODE_FLOAT, /* CLASSID_PRIMITIVE_FLOAT = 0x7 */
+ ETCH_XTRNL_TYPECODE_DOUBLE, /* CLASSID_PRIMITIVE_DOUBLE = 0x8 */
+ ETCH_XTRNL_TYPECODE_STRING /* CLASSID_STRING = 0x9 */
+ };
+
+ switch(obj_type)
+ {
+ case ETCHTYPEB_PRIMITIVE:
+ if (class_id <= CLASSID_STRING)
+ xtype = primitives[class_id];
+ else xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ break;
+
+ case ETCHTYPEB_ETCHOBJECT:
+ xtype = ETCH_XTRNL_TYPECODE_ANY;
+ break;
+
+ default:
+ xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ }
+
+ return xtype;
+}
+
+
+/*
+ * bintdi_get_custom_structtype()
+ * override of etchtagdata_get_custom_structtype.
+ * defers to value factory to return a non-disposable struct type
+ * for the specified class.
+ */
+etch_type* bintdi_get_custom_structtype(etch_object* thisx,
+ const unsigned short obj_type, const unsigned short class_id)
+{
+ etch_type *static_type = NULL;
+ binary_tagged_data_input *tdi = (binary_tagged_data_input*) thisx;
+ etch_value_factory *vf = tdi->vf;
+ if(vf) static_type = vf->vtab->get_custom_struct_type(vf, class_id);
+ return static_type;
+}
+
+
+/**
+ * bintdi_validate_value()
+ * not an override.
+ * &return an object *of the type being validated*, or null. this may be the same
+ * object as the passed value, or may be different. for example if we are working
+ * with an array of int, and a zero value was serialized, it will have been
+ * deserialized into an etch_byte, and the int validator validate_value will
+ * create and return an etch_int32 in its stead.
+ * null return indicates a validation error. null object return indicates value
+ * read was logically null. eod object return indicates end of data. if validation
+ * fails on a object, that object's destructor is invoked here.
+ */
+etch_object* bintdi_validate_value (binary_tagged_data_input* tdi,
+ etch_validator* vtor, boolean is_none_ok, etch_object* value)
+{
+ etch_object* resultobj = NULL;
+
+ if (NULL == vtor)
+ { resultobj = NULL;
+ etchlog(ETCHBTDI, ETCHLOG_ERROR, "no validator for type %x class %x\n",
+ value->obj_type, value->class_id);
+ ETCHOBJ_DESTROY(value);
+ }
+ else
+ if (NULL == value)
+ resultobj = NULL;
+ else
+ if (etchtagdata_is_eod(value) && is_none_ok)
+ resultobj = value;
+ else
+ if (etchtagdata_is_null(value) && is_none_ok)
+ resultobj = value;
+ #if(0)
+ /* we can't turn off validation since the validator ensures that the
+ * result object is of the expected type, e.g. we may have read an
+ * etch_in1t6 but are populating an array of etch_int32.
+ */
+ else
+ if (!config.is_validate_on_read)
+ resultobj = value;
+ #endif
+ else
+ if (NULL == (resultobj = vtor->validate_value (vtor, value)))
+ { etchlog(ETCHBTDI, ETCHLOG_ERROR, "validation failed for type %x class %x\n",
+ value->obj_type, value->class_id);
+ ETCHOBJ_DESTROY(value);
+ /* todo it would be nice to get an exception back across the wire here
+ * rather than kibosh the session, but not sure what the path would be
+ * to get it there, since we don't have a message yet at this point.
+ */
+ }
+
+ /* resultobj may be the same object as value, or may be different.
+ * if value was not an object of the type being validated, i.e. the validator
+ * is the int validator but the value object is an etch_byte representing zero,
+ * resultobj will be an etch_int32. if validation failed resultobj is null.
+ */
+ return resultobj;
+}
+
+
+/**
+ * bintdi_validate_valuex()
+ * invokes bintdi_validate_value on a value object, and if the validated object
+ * to be returned is not the same object as the passed value object, that value
+ * object's destructor is invoked. within the tdi, this will not necessarily
+ * destroy the value object, as the tdi can pass protected static objects,
+ * such as an object representing null, for validation.
+ * @return a validated object of the same class as that of the supplied validator,
+ * which may or may not be the same object as the passed value object.
+ */
+etch_object* bintdi_validate_valuex(binary_tagged_data_input* tdi,
+ etch_validator* vtor, boolean is_none_ok, etch_object* valueobj)
+{
+ etch_object* resultobj = bintdi_validate_value(tdi, vtor, is_none_ok, valueobj);
+
+ if (resultobj && valueobj && (resultobj != valueobj))
+ valueobj->destroy(valueobj);
+
+ return resultobj;
+}
\ No newline at end of file
Added: incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdo.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdo.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdo.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/bindings/msg/etch_binary_tdo.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,802 @@
+/* $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;
+}
+