blob: 6a22c72c6c5944ef816f6e87c76980299b1e64b1 [file] [log] [blame]
/* $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_cache.h"
#include "etch_structval.h"
#include "etch_arrayval.h"
#include "etch_tagged_data.h"
#include "etch_default_value_factory.h"
#include "etch_encoding.h"
#include "etch_exception.h"
#include "etch_nativearray.h"
#include "etch_log.h"
#include "etch_objecttypes.h"
#include "etch_mem.h"
static const char* LOG_CATEGORY = "etch_binary_tdi";
/* - - - - - - - - - - - - - - - - - - - -
* private method signatures
* - - - - - - - - - - - - - - - - - - - -
*/
i_binary_tdi* new_binarytdi_vtable();
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);
signed char bintagdata_get_native_typecode(const unsigned short, const unsigned short);
/* - - - - - - - - - - - - - - - - - - - -
* constructors/destructors
* - - - - - - - - - - - - - - - - - - - -
*/
/**
* destroy_binary_tagged_data_input()
*/
int destroy_binary_tagged_data_input(void* data)
{
binary_tagged_data_input* tdi = (binary_tagged_data_input*)data;
if (!is_etchobj_static_content(tdi))
etch_object_destroy(tdi->impl);
/* destroy private instance data */
etch_free(tdi->static_nullobj);
etch_free(tdi->static_eodmarker);
clear_etchobj_static_all(tdi->static_emptystring);
etch_object_destroy(tdi->static_emptystring);
return destroy_objectex((etch_object*)tdi);
}
/**
* clone_tagged_data_input()
* tdi copy constructor. if the tdi object implements a separate instance data
* object, that object is cloned as well.
*/
void* clone_binary_tagged_data_input(void* data)
{
binary_tagged_data_input* tdi = (binary_tagged_data_input*)data;
binary_tagged_data_input* newtdi = (binary_tagged_data_input*) clone_object((etch_object*) tdi);
newtdi->impl = tdi->impl? tdi->impl->clone(tdi->impl): NULL;
return newtdi;
}
/**
* 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);
((etch_object*)tdi)->destroy = destroy_binary_tagged_data_input;
((etch_object*)tdi)->clone = clone_binary_tagged_data_input;
tdi->vf = vf;
vtab = etch_cache_find(get_vtable_cachehkey(CLASSID_BINARYTDI_VTAB), 0);
if(!vtab)
{ vtab = new_binarytdi_vtable();
etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
}
((etch_object*)tdi)->vtab = (vtabmask*)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);
}
/**
* 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].o.obj_type = ETCHTYPEB_TAGDATAINP;
inheritlist[1].c.class_id = CLASSID_TAGDATAINP;
inheritlist[2].o.obj_type = ETCHTYPEB_TAGDATA;
inheritlist[2].c.class_id = CLASSID_TAGDATA;
vtab->inherits_from = inheritlist;
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;
byte wire_version = 0;
do
{ result = etch_flexbuf_get_byte (tdi->flexbuf, &wire_version);
if (wire_version != ETCH_BINTAGDATA_CURRENT_VERSION)
{
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_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) {
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "message receive starts\n");
}
else {
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_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;
etch_object* resobj = message_get (msg, resobj_key);
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "exception found in message\n");
//replace result with exception object
//TODO memory leak here? (resobj not destroyed, should we?)
resobj = ((etch_object*)excpobj)->clone(excpobj);
result = message_put (msg, resobj_key, resobj);
}
}
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)) {
etch_object_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)
{
binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
etch_structvalue* newsv = NULL;
newsv = ((struct i_binary_tdi*)((etch_object*)tdi)->vtab)->start_struct((tagged_data_input*) tdi);
if(newsv){
if (-1 == bintdi_read_keys_values(tdi, newsv)) {
etch_object_destroy(newsv);
newsv = NULL;
}else
{
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;
byte array_content_type = 0;
result = etch_flexbuf_get_byte(tdi->flexbuf, &array_content_type);
switch((signed char)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);
if(numdimensions <= 0 || numelements < 0){
return NULL;
}
/* 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)
{
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)) {
etch_object_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, 0);
if (NULL == arrayval) {
return NULL;
}
/* validator will validate array based on this class */
((etch_object*)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;
if(bytecount >= 0 && (size_t) bytecount <= etch_flexbuf_avail(tdi->flexbuf)) {
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 = ((struct i_value_factory*)((etch_object*)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_etch_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 = etch_encoding_get_sizeof_terminator(configured_encoding);
buf = bintdi_read_bytes(tdi, nulltermsize, &size); /* we own this memory */
if(buf) {
// TODO: pool
result = etch_encoding_transcode_to_wchar(&widestring, buf, configured_encoding, size, NULL);
etch_free(buf);
/* construct string object, relinquishing ownership of string buffer */
newstr = new_string_from(widestring, etch_encoding_for_wchar());
}
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 */
//could not read next key field
if(!thisobj) {
break;
}
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)
{
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"field lookup failed, trying to skip that value\n");
//skip the value of unknown keys...
thisvalue = bintdi_read_value(tdi, etchvtor_object_get(0)); /* returns a disposable ref */
//if this fails, we cannot know where we are, so stop
if(thisvalue == NULL) {
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"cant skip value, error in deserialization\n");
result = -1;
break;
} else {//else we can continue
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"skipped the value for the unknown key\n");
result = 0;
continue;
}
}
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 = (etch_field*)etch_object_clone_func(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, (etch_object*) thisvalue);
//printf("read_keys_values: got field %s, value: %p\n",key_clone->aname,thisvalue);
/* if put was OK we have relinquished ownership of valuobj */
/* key_field was relinquished regardless. */
etch_object_destroy(thisobj);
thisobj = NULL;
}
if (-1 == result)
{ /* some error, usually failed validation */
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"message deserialization failed\n");
etch_object_destroy(key_clone);
key_clone = NULL;
etch_object_destroy(thisvalue);
thisvalue = NULL;
}
etch_object_destroy(thisobj);
thisobj = NULL;
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)
{
int counter = 0;
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;
counter++;
}
etch_object_destroy(ev);
etch_object_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, (byte*)&objtype))
return NULL;
switch((signed char)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, (byte*)(&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);
return bintdi_validate_value (tdi, v, FALSE, (void*) u.vnatarray);
/*
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);
*/
//return (etch_object*)u.vnatarray;
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_object* reconstituted_object = NULL;
/* acquire struct */
etch_structvalue* keys_values = bintdi_read_struct((tagged_data_input*) tdi);
//deserialization failed
if(! keys_values) {
return NULL;
}
/* relinquish struct */
reconstituted_object = ((struct i_value_factory*)((etch_object*)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_for_signed_chars(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, (byte*)&objtype))
result = -1;
else
if (is_inrange_tiny_for_signed_chars(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, (byte*)&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)
{
if (NULL == out) return -1;
memset(out, 0, sizeof(etch_array_id_params));
return bintdi_get_native_type(array_content_type, out);
}
/*
* 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_for_signed_chars(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
*/
signed char 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 = ((struct i_value_factory*)((etch_object*)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(! value)
return NULL;
if (NULL == vtor) {
resultobj = NULL;
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "no validator for type %x class %x\n", ((etch_object*)value)->obj_type, ((etch_object*)value)->class_id);
etch_object_destroy(value);
value = NULL;
return NULL;
}
else if (NULL == value) {
return NULL;
}
else if (etchtagdata_is_eod(value) && is_none_ok) {
resultobj = value;
}
else if (etchtagdata_is_null(value) && is_none_ok) {
resultobj = value;
}
else if (NULL == (resultobj = vtor->validate_value (vtor, value)))
{
ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "validation failed for type %x class %x\n", ((etch_object*)value)->obj_type, ((etch_object*)value)->class_id);
etch_object_destroy(value);
value = NULL;
/* 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))
etch_object_destroy(valueobj);
return resultobj;
}