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;   
+}
+