blob: e8e22bb86ecdf763bb49c28b12581deb84373fe3 [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.
*/
/*
* etchobj.c
*/
#include "etch_global.h"
#include "etchexcp.h"
unsigned int primitive_objsize[] =
{
sizeof(etch_byte), sizeof(etch_boolean), sizeof(etch_int8), sizeof(etch_int16),
sizeof(etch_int32), sizeof(etch_int64), sizeof(etch_float), sizeof(etch_double),
sizeof(etch_string),sizeof(etch_date),
};
/**
* destroy_object()
* default virtual destructor for an objmask* based object
* (other than etch_object), invoked by all other object dtors.
*/
int destroy_object(objmask* thisobj)
{
objmask* parentobj = NULL;
if (thisobj == NULL) return -1;
parentobj = thisobj->parent;
/* if a calling destructor did not already decrement the refcount ... */
if (!is_etchobj_refcount_decremented(thisobj))
if (thisobj->refcount > 0) /* if object is refcounted */
if (--thisobj->refcount > 0) /* destroy only if last ref */
return -1;
if (thisobj->result)
{
destroy_exception(thisobj->result->exception);
etch_free(thisobj->result);
}
if (!is_etchobj_static_shell(thisobj))
etch_free(thisobj);
if (parentobj && parentobj->destroy)
parentobj->destroy(parentobj);
return 0;
}
/**
* destroy_objectex()
* mark object as "refcount already decremented" and invoke destroy_object
*/
int destroy_objectex(objmask* thisobj)
{ /* we usurp hashkey for this purpose since object is being destroyed anyway */
if (thisobj) thisobj->hashkey = ETCH_NOREFCOUNT_MARKER;
return destroy_object(thisobj);
}
/**
* destroy_etch_object()
* default virtual destructor for an anonymous object wrapper.
* such a wrapper is final (it will not have a parent)
*/
int destroy_etch_object(etch_object* thisobj)
{
objmask* childobj = NULL;
int is_childobj_etchobj = 0, is_childobj_owned = 0, is_static_content = 0;
if (thisobj == NULL) return -1;
if (thisobj->refcount > 0) /* if object is refcounted */
if (--thisobj->refcount > 0) /* destroy only if last ref */
return -1;
if (thisobj->result)
{ /* a result object pointer is not part of static content since never copied */
destroy_exception(thisobj->result->exception);
etch_free(thisobj->result);
}
childobj = thisobj->value;
is_childobj_etchobj = thisobj->is_value_object;
is_childobj_owned = thisobj->is_value_owned;
is_static_content = is_etchobj_static_content(thisobj);
if (!is_etchobj_static_shell(thisobj))
etch_free(thisobj);
if (childobj && is_childobj_owned && !is_static_content)
{ if (is_childobj_etchobj)
childobj->destroy(childobj);
else etch_free(childobj);
}
return 0;
}
/**
* destroy_etch_object_value()
* destroy the content of an object wrapper.
* this is not called by the object destructor because it destroys the wrapper first.
*/
int destroy_etch_object_value(etch_object* x)
{
int result = 0;
if (is_etchobj_static_content(x))
result = -1;
else
if (x->is_value_owned)
if (x->is_value_object)
((objmask*)x->value)->destroy(x->value);
else etch_free(x->value);
else result = -1;
return result;
}
/**
* set_etch_assignable_arg_from()
* populate an argument to etchobj_is_assignable_from() from specified object
*/
void set_etch_assignable_arg_from(etch_objclass* arg, objmask* obj)
{
vtabmask* vtab = NULL;
memset(arg, 0, sizeof(etch_objclass));
if (NULL == obj) return;
arg->obj_type = obj->obj_type;
arg->class_id = obj->class_id;
arg->parent = obj->parent;
if (vtab = (vtabmask*) obj->vtab)
{ arg->vtable_class_id = vtab->class_id;
arg->inherits_from = vtab->inherits_from;
}
switch(obj->obj_type)
{ case ETCHTYPEB_NATIVEARRAY:
arg->numdims = ((etch_nativearray*)obj)->numdims;
arg->content_obj_type = ((etch_nativearray*)obj)->content_obj_type;
arg->content_class_id = ((etch_nativearray*)obj)->content_class_id;
break;
case ETCHTYPEB_ARRAYVAL:
arg->numdims = ((etch_collection_mask*)obj)->n;
arg->content_obj_type = ((etch_collection_mask*)obj)->content_obj_type;
arg->content_class_id = ((etch_collection_mask*)obj)->content_class_id;
break;
case ETCHTYPEB_ARRAYLIST:
arg->numdims = 1;
arg->content_obj_type = ((etch_collection_mask*)obj)->content_obj_type;
arg->content_class_id = ((etch_collection_mask*)obj)->content_class_id;
}
}
/**
* etchobj_is_assignable_from()
* determines if the class of thisobj is the same as, or is a superclass of,
* the class specified by that_class_id. tests whether the type represented
* by that_obj_type and that_class_id can be converted to the type of thisobj
* via an identity conversion or via a widening reference conversion.
*
* @param target the class of left side of the assignment (to).
* during validation, this is the class of object the validator expects
* to validate. if target object is an array, this is the array content class.
* @param source the class of right side of the assignment (from).
* during validation, this is the class of the object being validated.
* if target object is an array, this is the array content class.
*/
int etchobj_is_assignable_from(etch_objclass* target, etch_objclass* source)
{
if (source->class_id == target->class_id && source->numdims == 0)
return TRUE; /* identity case */
/* if left side is Object wrapper (not Object[]), anything can be assigned
* to it, since the c binding does not receive anything that is unwrapped.
* i.e. in java Object.class is not assignable from int.class; however here
* we don't have an int.class, only wrapped integers. if this were not the
* case this test would come after the etch primitives test, since a class
* not derived from object can't be assigned to object.
*/
if (is_etch_object_type(target->obj_type, target->class_id)) return TRUE;
if (target->obj_type == ETCHTYPEB_PRIMITIVE
|| source->obj_type == ETCHTYPEB_PRIMITIVE) return FALSE;
/* if left (target) side is an array, and right (source) side is an
* array of the same dimensions, and either of the same content type,
* or an array of Object, then it is assignable.
* this code is not robust - need to rethink assignability for default
* array validator with various validation object array types.
* currently we are validating very loosely for array types. we need
* a more general means of validating arrays, i.e. common attributes
* among array types (native, value, list)
*/
if (is_etch_objarray_type (target->obj_type, target->class_id)
|| is_etch_arraylist_type (target->obj_type, target->class_id)
|| is_etch_nativearray_type(target->obj_type, target->class_id))
{
if (target->numdims == source->numdims)
{
/* this line added to pass anything using default array validator */
if (target->content_obj_type == 0 && target->content_class_id == 0)
return TRUE;
if (target->content_class_id == CLASSID_UNWRAPPED)
return target->content_obj_type == source->content_obj_type;
if (target->content_class_id == CLASSID_OBJECT
|| target->content_class_id == source->content_class_id)
return TRUE;
}
return FALSE;
}
else /* if source inherits from target, source can be assigned to target */
if (source->parent)
{ /* inheritance model 2 - inherited objects are instantiated and chained */
objmask* thisparent = source->parent;
while(thisparent) /* walk source object's inheritance chain */
{
if (thisparent->class_id == target->class_id) return TRUE;
thisparent = thisparent->parent;
}
}
else
{ int ndx = 0;
etchparentinfo* parentinfo;
/* inheritance model 1 - inherited data flattened into single object */
while(1) /* iterate source object's inheritance list */
{
if (NULL == (parentinfo = get_next_etch_parentex
(source->class_id, source->inherits_from, ndx++)))
break;
if (parentinfo->class_id == target->class_id)
return TRUE;
}
}
return FALSE;
}
/**
* etchobj_is_assignable_from_ex()
* see comments for etchobj_is_assignable_from()
*/
int etchobj_is_assignable_fromobj(objmask* targetobj, objmask* sourceobj)
{
etch_objclass targetarg, sourcearg;
set_etch_assignable_arg_from(&targetarg, targetobj);
set_etch_assignable_arg_from(&sourcearg, sourceobj);
return etchobj_is_assignable_from(&targetarg, &sourcearg);
}
/*
* etchobj_assign_to()
* assign object b to object a, if legal to do so.
* handles assignments of wrapped objects, array objects, and scalar objects.
* @return 0 or -1
*/
objmask* etchobj_assign_to(objmask* a, objmask* b)
{
objmask* resultobj = a;
if (is_etch_object(a))
{
if (is_etch_object(b))
{ /* both sides are wrappers so just copy right value to left */
destroy_etch_object_value((etch_object*)a);
((etch_object*)a)->value = ((etch_object*)b)->value;
((etch_object*)a)->is_value_object = ((etch_object*)b)->is_value_object;
((etch_object*)a)->is_value_owned = ((etch_object*)b)->is_value_owned;
}
else /* left side is a wrapper, right side is not a wrapper */
{ /* so embed right side value inside left side wrapper */
destroy_etch_object_value((etch_object*)a);
((etch_object*)a)->value = b; /* wrap b inside a */
((etch_object*)a)->is_value_object = TRUE;
((etch_object*)a)->is_value_owned = FALSE;
}
}
else
if (is_etch_object(b))
{ /* left side is not a wrapper, right side is a wrapper */
/* we do not currently support copying right side wrapped content
* to the left side, although technically we could easily do so.
*/
resultobj = NULL;
}
else
if (!etchobj_is_assignable_fromobj(a, b))
{ /* source object is not legally assignable to target */
resultobj = NULL;
}
else /* neither side is a wrapper, right side is assignable to left */
if (is_etch_nativearray(a))
{ /* both sides are arrays per is_assignable_from(),
* attempt to copy right side array to left */
resultobj = (objmask*) etch_nativearray_assign_to
((etch_nativearray*) a, (etch_nativearray*) b);
}
else /* both sides are scalar */
if (b->parent != NULL)
{ /* inheritance type 2: parent object(s) is/are instantiated
* and chained to the inheritor. with this type of inheritance,
* it is possible that the memory reference we return is not the
* same as the target reference we pass. this is because we can't
* copy source to target to do the assignment, since the child
* object we would want to assign to it may in fact be larger
* than the target object.
*/
objmask* thisparent = b;
resultobj = NULL;
while(thisparent) /* walk the parent chain */
{ if (thisparent->class_id == a->class_id)
{ resultobj = thisparent;
break;
}
else thisparent = thisparent->parent;
}
}
else /* inheritance type 1: parent data are members of child object. */
{ /* since the right side is derived from left, and since we enforce
* a rule that derived objects must append instance data to the end
* of the parent object, we can simply copy memory from the source
* to the target, limited to the known shorter length of the target;
* and then restore the individual fields which should be retained.
* this scheme of course depends on object constructors populating
* obj->length with sizeof(instantiated struct), in all cases. */
/* save off items we need to restore post-copy */
objmask save, *saved = &save;
memcpy(saved, a, sizeof(objmask));
/* mark target shell immutable so destructor will not free it */
set_etchobj_static_shell(a);
/* invoke target destructor to free any content owned by target */
a->destroy(a);
/* copy source to target */
memcpy(a, b, a->length);
/* mark target as a copy, its content not owned by it */
a->is_copy = TRUE;
clear_etchobj_static_shell(a);
set_etchobj_static_content(a);
/* finally restore the retained target fields */
a->obj_type = saved->obj_type;
a->class_id = saved->class_id;
a->refcount = saved->refcount;
a->result = saved->result;
a->destroy = saved->destroy;
a->clone = saved->clone;
a->get_hashkey = saved->get_hashkey;
if (is_etchobj_static_shell(saved))
set_etchobj_static_shell(a);
}
return resultobj;
}
/**
* etch_addref()
* increment object's reference count. if previously zero, begins reference
* counting of an object. obj.refcount > 0 indicates that the object is
* reference counted. conversely, if obj.refcount is zero, an object is not
* reference counted. if an object is refcounted, then invocation of its
* destructor implies decrementing the refcount, destroying the object only
* if the refcount is now zero.
*/
unsigned etch_addref(objmask* thisobj)
{
return ++thisobj->refcount;
}
/**
* etch_release()
* decrement object's reference count and destroy the object if now zero.
* returns new reference count. zero indicates the object was destroyed.
* use of this method is optional, since invocation of a refcounted object's
* destructor does an implicit decrement of the refcount. however it is
* included since coding destroy() might be misleading to the reader.
*/
unsigned etch_release(objmask* thisobj)
{
return destroy_object(thisobj) == 0? 0: thisobj->refcount;
}
/**
* etch_release_wrapper()
* see comments above at etch_release()
*/
unsigned etch_release_wrapper(etch_object* thisobj)
{
return destroy_etch_object(thisobj) == 0? 0: thisobj->refcount;
}
/**
* destroy_string()
*/
int destroy_string(etch_string* thisp)
{
if (thisp->refcount > 0 && --thisp->refcount > 0) return -1;
if (!is_etchobj_static_content(thisp))
etch_free(thisp->v.value); /* OK if null */
destroy_objectex((objmask*)thisp);
return 0;
}
/**
* default virtual copy constructor for etch object.
*/
objmask* clone_object(objmask* pthis)
{
void* object_value = NULL;
void* pnew = NULL;
unsigned objsize = pthis->length;
if (objsize < sizeof(objmask)) objsize = sizeof(objmask);
pnew = etch_malloc (objsize, pthis->obj_type);
memcpy(pnew, pthis, objsize);
return (objmask*) pnew;
}
/**
* default virtual copy constructor for objects requiring deep copy
*/
objmask* clone_null(objmask* pthis)
{
return NULL;
}
/**
* clone_string()
*/
etch_string* clone_string(etch_string* thisp)
{
etch_string* newobj = (etch_string*) clone_object((objmask*)thisp);
newobj->v.value = thisp->encoding == ETCH_ENCODING_UTF16?
(void*) new_wchar(thisp->v.valw): (void*) new_char (thisp->v.valc);
return newobj;
}
/**
* new_etchresult()
* generate etchobject result object
*/
etchresult* new_etchresult(const int result, const int reason)
{
etchresult* newresult = etch_malloc(sizeof(etchresult), ETCHTYPEB_RESULT);
memset(newresult, 0, sizeof(etchresult));
newresult->resultcode = result;
newresult->reasoncode = reason;
return newresult;
}
/**
* defgethashkey
* default hashkey computation for an etch object
*/
unsigned defgethashkey(objmask *obj)
{
void* hashitem = obj; /* uses the object address as hash source */
if (NULL == hashitem) return 0;
return obj->hashkey = etchhash((char*)&hashitem, sizeof(void*), 0);
}
/**
* new_object()
* basic object constructor
* sets type, class, size, default destructor, and copy constructor.
*/
objmask* new_object(const int objsize, const unsigned short obj_type, const unsigned short class_id)
{
objmask* newobj = etch_malloc(objsize, obj_type);
memset(newobj, 0, objsize);
newobj->obj_type = obj_type;
newobj->class_id = class_id;
newobj->length = objsize;
newobj->destroy = destroy_object;
newobj->clone = clone_object;
newobj->get_hashkey = defgethashkey;
newobj->get_hashkey(newobj);
return newobj;
}
/**
* clone_etch_object()
* clone_etch_object copy constructor
*/
objmask* clone_etch_object(objmask* thatobj)
{
objmask* newobj = clone_object(thatobj);
((etch_object*) newobj)->is_value_owned = FALSE;
newobj->is_copy = TRUE;
return newobj;
}
/**
* new_etch_object()
* wrapper object constructor
*/
etch_object* new_etch_object(const unsigned short class_id, objmask* childobj)
{
etch_object* newobj = (etch_object*) new_object
(sizeof(etch_object), ETCHTYPEB_ETCHOBJECT, class_id);
newobj->is_value_object = FALSE; /* when TRUE dtor calls destroy() */
newobj->is_value_owned = TRUE; /* when TRUE dtor frees content */
newobj->value = childobj;
newobj->destroy = destroy_etch_object;
newobj->clone = clone_etch_object;
return newobj;
}
/**
* short_type()
* return a 16-bit type code of 2 8-bit parts
*/
short short_type(unsigned i, unsigned j)
{
return (short) ( ( ((byte)i) << 16 ) | ((byte)j) );
}
/**
* clear_lastresult()
* clear the global lastresult object
*/
void clear_lastresult()
{
(lastresobj = &_lastresobj)->result = &lastresult;
if (lastresult.exception) destroy_exception(lastresult.exception);
memset(&lastresult, 0, sizeof(struct etchresult));
}
/**
* verify_object()
* verify that the object passed is of the specified type and class.
* zero is a match for either so pass zero to not validate either.
*/
int verify_object(objmask* obj, const unsigned short type, const unsigned short id, void** out)
{
if (obj == NULL) return -1;
if (type != 0 && obj->obj_type != type) return -1;
if (id != 0 && obj->class_id != id) return -1;
return 0;
}
/**
* get_base_vtable()
* walks a vtable chain returning the final vtab in the chain
*/
void* get_base_vtable(objmask* obj)
{
vtabmask* basevtab = obj->vtab;
while (basevtab && basevtab->vtab) basevtab = basevtab->vtab;
return basevtab;
}
/**
* destroy_vtable()
*/
int destroy_vtable(objmask* vtab)
{
etchparentinfo* inheritlist = ((vtabmask*)vtab)->inherits_from;
if (inheritlist && !is_etchobj_static_content(vtab))
free(inheritlist); /* vtables not tracked */
if (!is_etchobj_static_shell(vtab))
free(vtab); /* vtables not tracked */
return 0;
}
/**
* get_class_cachekey()
* get the unique value used for keying the indicated class in a class cache.
*/
unsigned get_class_cachekey(unsigned short obj_type, unsigned short class_id)
{
unsigned key = (obj_type << 16) | class_id;
return etchhash(&key, 4, 0);
}
/**
* get_vtable_cachehkey()
* calculates ad returns the *cache* key for specified vtable object
* the hashkey on the vtable object is not an object key, it is a class key,
* since vtables are cached by class.
*/
unsigned get_vtable_cachehkey(unsigned short class_id)
{
return get_class_cachekey(ETCHTYPEB_VTABLE, class_id);
}
/**
* get_vtabobj_hashkey()
* sets and gets the *cache* key for specified vtable object
* the hashkey on the vtable object is not an object key, it is a class key,
* since vtables are cached by class.
*/
unsigned get_vtabobj_hashkey(objmask* vtabobj)
{
return vtabobj->hashkey = get_vtable_cachehkey(vtabobj->class_id);
}
/**
* new_vtable()
* instantiate a new virtual function table of the specified type,
* defaulting all methods to those of specified parent if requested
*/
void* new_vtable(const void* parentvtab, const size_t size, const short classid)
{
objmask* newvtab = malloc(size); /* vtable memory is not tracked */
if (parentvtab)
memcpy(newvtab, parentvtab, size);
else memset(newvtab, 0, size);
newvtab->obj_type = ETCHTYPEB_VTABLE;
newvtab->class_id = classid;
newvtab->length = (unsigned) size;
newvtab->destroy = destroy_vtable;
newvtab->clone = clone_object;
newvtab->get_hashkey = get_vtabobj_hashkey;
newvtab->get_hashkey(newvtab);
return newvtab;
}
/**
* get_vtab_inheritance_list()
* add an inheritance list to the specified object, or fetch existing list.
* an inheritance list exists in the vtable since we don't need to duplicate it
* for every instance. if there is no vtable in the specified object, a place-
* holder vtable is instantiated in the object. recall that vtables are freed
* when the global cache is cleared. the first entry in an inheritance list
* contains the list attributes, therefore the list is one-based.
* if an appropriately-sized list is already cached, the cached list is
* returned. if a shorter list exists it is resized, copied, and returned.
* @param size total number of entries to be allocated
* @param count number of populated entries
*/
etchparentinfo* get_vtab_inheritance_list(objmask* obj,
const short size, const short count, const short vtabclass)
{
etchparentinfo *oldlist = NULL, *newlist = NULL;
/* if such a list is already cached, return it now */
vtabmask* vtab = obj->vtab? obj->vtab: cache_find(get_vtable_cachehkey(vtabclass), 0);
oldlist = vtab? vtab->inherits_from: NULL;
if (oldlist && oldlist->list_size >= size)
{
oldlist[0].list_count = count;
obj->vtab = vtab;
return oldlist;
}
newlist = new_etch_inheritance_list(size, count, oldlist);
if (newlist == NULL) return NULL;
/* note that we are creating a placeholder vtable here. we could not add
* virtuals to such a vtable, since this vtable consists of the vtable
* header only, per sizeof(vtabmask), following.
*/
if (vtab == NULL)
{ vtab = new_vtable(NULL, sizeof(vtabmask), vtabclass);
vtab->inherits_from = newlist;
cache_insert(vtab->hashkey, vtab, FALSE);
obj->vtab = vtab;
}
vtab->inherits_from = newlist;
return newlist;
}
/**
* new_etch_inheritance_list()
* allocate and return an inheritance list of the specified size
* @param size total number of entries to be allocated
* @param count number of populated entries
*/
etchparentinfo* new_etch_inheritance_list(const short size, const short count,
etchparentinfo* oldlist)
{
etchparentinfo *newlist = NULL;
const int newbytes = size * sizeof(etchparentinfo), MAXPARENTS = 15;
if (count < 0 || count > MAXPARENTS || size < 0 || count > size)
return NULL;
newlist = malloc(newbytes); /* vtables not tracked */
memset(newlist, 0, newbytes);
if (oldlist) /* we may be expanding an existing list, copy into new list */
{ const int oldbytes = oldlist->list_size * sizeof(etchparentinfo);
memcpy(newlist, oldlist, oldbytes);
free(oldlist); /* vtables not tracked */
oldlist = NULL;
}
else newlist[0].list_count = count;
newlist[0].list_size = size; /* list attributes are in first entry */
return newlist;
}
/**
* is_derives_from_object()
* determine if specified object derives from object.
* all objmask-masked objects are etch c objects by definition; however it is
* here that we would artificially specify that certain wrapped objects are not
* derived from object in the logical etch sense, if such a need arises.
*/
int is_derives_from_object(objmask* obj)
{
return obj? is_derives_from_object_class(obj->class_id): FALSE;
}
/**
* is_derives_from_object_class()
* see comments at is_derives_from_object()
*/
int is_derives_from_object_class(const unsigned short class_id)
{
return TRUE;
}
/**
* get_next_etch_parent()
* see comments at get_next_etch_parentex() below
*/
etchparentinfo* get_next_etch_parent(objmask* obj, int current_index)
{
etchparentinfo* inherit_list = obj && obj->vtab? obj->vtab->inherits_from: NULL;
return get_next_etch_parentex(obj->class_id, inherit_list, current_index);
}
/**
* get_next_etch_parentex()
* returns a non-disposable reference to etchparentinfo struct containing the
* class of next parent in this object's inheritance hierarchy, relative to the
* specified index. if specified object does in fact inherit from other than
* object, its inheritance list must have been previously instantiated via
* get_vtab_inheritance_list(), above, and populated accordingly, presumably
* int the object's constructor. the inheritance list implicitly ends with
* object, if the specified object's class derives from object; however object
* does not explicitly appear in the list and in fact must not be so populated.
* @param obj the etch object for which a parent is requested.
* @param current_index the index of the currently requested parent. on the
* first call specify zero, on subsequent calls increment current_index.
* @return etchparentinfo as described above, or NULL if no more parents.
* the returned reference is valid while its containing inheritance list remains
* instantiated, which ordinarily is while its associated vtable exists, which
* ordinarily is until service teardown.
*/
etchparentinfo* get_next_etch_parentex
(const unsigned short class_id, etchparentinfo* inherit_list, int current_index)
{
static etchparentinfo object_parent = { ETCHTYPEB_ETCHOBJECT, CLASSID_OBJECT };
etchparentinfo* nextparent = NULL;
if (NULL == inherit_list && current_index > 0);
else
if ((NULL == inherit_list) || (current_index == inherit_list[0].list_count))
if (is_derives_from_object_class(class_id))
nextparent = &object_parent;
else;
else
if (current_index < inherit_list[0].list_count)
nextparent = &inherit_list[++current_index]; /* list is one-based */
return nextparent;
}
/**
* new_primitive()
* allocate, initialize and return a primitive object
*/
objmask* new_primitive(const unsigned obj_len, const unsigned short class_id)
{
objmask* newobj = new_object(obj_len, ETCHTYPEB_PRIMITIVE, class_id);
newobj->destroy = destroy_object;
newobj->clone = clone_object;
newobj->get_hashkey = etch_number_get_hashkey;
return newobj;
}
/**
* new_wchar()
* wide character string clone
*/
wchar_t* new_wchar(const wchar_t* s)
{
#pragma warning(disable:4996) /* wcscpy unsafe warning */
unsigned bytelen;
wchar_t* clone;
if (NULL == s) return NULL;
bytelen = (unsigned)(wcslen(s) + 1) * sizeof(wchar_t);
clone = etch_malloc(bytelen, ETCHTYPEB_STRING);
wcscpy(clone, s);
return clone;
}
/**
* new_char()
* narrow character string clone
*/
char* new_char(const char* s)
{
char* clone;
if (NULL == s) return NULL;
clone = etch_malloc(strlen(s) + 1, ETCHTYPEB_STRING);
return strcpy(clone, s);
}
/**
* new_byte()
*/
etch_byte* new_byte(const signed char v)
{
etch_byte* newobj = (etch_byte*) new_primitive
(sizeof(struct etch_byte), CLASSID_PRIMITIVE_BYTE);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_boolean()
*/
etch_boolean* new_boolean(boolean v)
{
etch_boolean* newobj = (etch_boolean*) new_primitive
(sizeof(struct etch_boolean), CLASSID_PRIMITIVE_BOOL);
newobj->value = v? TRUE: FALSE;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_int8()
*/
etch_int8* new_int8(signed char v)
{
etch_int8* newobj = (etch_int8*) new_primitive
(sizeof(struct etch_int8), CLASSID_PRIMITIVE_INT8);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_int16()
*/
etch_int16* new_int16(short v)
{
etch_int16* newobj = (etch_int16*) new_primitive
(sizeof(struct etch_int16), CLASSID_PRIMITIVE_INT16);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_int32()
*/
etch_int32* new_int32(int v)
{
etch_int32* newobj = (etch_int32*) new_primitive
(sizeof(struct etch_int32), CLASSID_PRIMITIVE_INT32);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_int64()
*/
etch_int64* new_int64(int64 v)
{
etch_int64* newobj = (etch_int64*) new_primitive
(sizeof(struct etch_int64), CLASSID_PRIMITIVE_INT64);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_float()
*/
etch_float* new_float(float v)
{
etch_float* newobj = (etch_float*) new_primitive
(sizeof(struct etch_float), CLASSID_PRIMITIVE_FLOAT);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_double()
*/
etch_double* new_double(double v)
{
etch_double* newobj = (etch_double*) new_primitive
(sizeof(struct etch_double), CLASSID_PRIMITIVE_DOUBLE);
newobj->value = v;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* etch_string_get_hashkey
* hashkey computation override for an etch_string.
* hash key is computed using the raw string as hash source.
*/
unsigned etch_string_get_hashkey(objmask* etchobj)
{
etch_string* sobj = (etch_string*) etchobj;
sobj->hashkey = ETCH_ENCODING_UTF16 == sobj->encoding?
etch_get_wchar_hashkey(sobj->v.valw):
etch_get_char_hashkey(sobj->v.valc);
return sobj->hashkey;
}
/**
* etch_string_init()
* private constructor for opaque string
*/
etch_string* etch_string_init(const void* s, const unsigned char encoding)
{
etch_string* newobj = (etch_string*)
new_primitive(sizeof(struct etch_string), CLASSID_STRING);
switch(encoding)
{ case ETCH_ENCODING_UTF16:
case ETCH_ENCODING_ASCII:
case ETCH_ENCODING_UTF8:
newobj->encoding = encoding;
break;
default: newobj->encoding = ETCH_ENCODING_DEFAULT;
}
if (s)
{ switch(encoding)
{ case ETCH_ENCODING_UTF16:
newobj->char_count = (unsigned) wcslen((wchar_t*)s);
newobj->byte_count = (newobj->char_count + 1) * sizeof(wchar_t);
break;
default:
newobj->char_count = (unsigned) strlen((char*)s);
newobj->byte_count = (newobj->char_count + 1) * sizeof(char);
}
}
else newobj->is_null = TRUE;
newobj->destroy = destroy_string;
newobj->clone = clone_string;
return newobj;
}
/**
* new_string()
* clones supplied string
* @param s a raw string to be assigned to the new string object.
* caller retains ownership of s.
*/
etch_string* new_string(const void* s, const unsigned char encoding)
{
etch_string* newobj = etch_string_init(s, encoding);
if (s)
{ switch(encoding)
{ case ETCH_ENCODING_UTF16:
newobj->v.valw = etch_malloc(newobj->byte_count, ETCHTYPEB_STRING);
wcscpy(newobj->v.valw, (wchar_t*)s);
break;
default:
newobj->v.valc = etch_malloc(newobj->byte_count, ETCHTYPEB_STRING);
strcpy(newobj->v.valc, (char*)s);
}
}
newobj->get_hashkey = etch_string_get_hashkey;
newobj->get_hashkey((objmask*)newobj);
return newobj;
}
/**
* new_stringw()
* convenience constructor for string type ETCH_ENCODING_UTF16;
* @param s a raw string to be assigned to the new string object.
* caller retains ownership of s.
*/
etch_string* new_stringw(const void* s)
{
return new_string(s, ETCH_ENCODING_UTF16);
}
/**
* new_stringa()
* convenience constructor for string type ETCH_ENCODING_UTF8);
* @param s a raw string to be assigned to the new string object.
* caller retains ownership of s.
*/
etch_string* new_stringa(const void* s)
{
return new_string(s, ETCH_ENCODING_UTF8);
}
/**
* new_string_from()
* does not clone supplied string
* @param s a disposable raw string to be assigned to the new string object.
* caller relinquishes ownership of s.
*/
etch_string* new_string_from(const void* s, const unsigned char encoding)
{
etch_string* newobj = etch_string_init(s, encoding);
newobj->v.value = (void*) s;
return newobj;
}
/**
* new_etch_event()
*/
etch_event* new_etch_event(const unsigned short class_id, const int value)
{
etch_event* newobj = (etch_event*) new_int32(value);
if (class_id) newobj->class_id = class_id;
return newobj;
}
/**
* new_etch_query()
*/
etch_query* new_etch_query(const unsigned short class_id, const int value)
{
etch_query* newobj = (etch_query*) new_int32(value);
if (class_id) newobj->class_id = class_id;
return newobj;
}
/**
* new_etch_control()
*/
etch_control* new_etch_control(const unsigned short class_id, const int value)
{
etch_control* newobj = (etch_control*) new_int32(value);
if (class_id) newobj->class_id = class_id;
return newobj;
}
/**
* new_date()
*/
etch_date* new_date()
{
etch_date* newobj = (etch_date*)
new_primitive(sizeof(struct etch_date), CLASSID_DATE);
time (&newobj->value);
newobj->ticks = clock();
return newobj;
}
/**
* new_who()
* a who is an etch_object wrapper around some etch object type, its purpose
* to be a disposable object which opaquely specifies the object which is the
* sender or receiver component of a method.
* @param whoobj the object which is the actual source or destination.
* if this object is a nondisposable refrerence, of course it must be assured
* that the object is not destroyed prior to destruction of the etch_who
* which references it.
* @param is_owned if TRUE the etch_who destructor will destroy the whoobj.
*/
etch_who* new_who(void* whoobj, const int is_owned)
{
etch_who* newobj = (etch_who*) new_etch_object(CLASSID_WHO, whoobj);
newobj->is_value_object = TRUE;
newobj->is_value_owned = is_owned != FALSE;
return newobj;
}
/**
* new_nullobj()
* instantiate and return a logically null object
*/
objmask* new_nullobj()
{
objmask* obj = (objmask*) new_etch_object(CLASSID_NONE, NULL);
obj->is_null = TRUE;
return obj;
}
/**
* etch_get_char_hashkey
* hashkey computation using a narrow string as source
*/
unsigned etch_get_char_hashkey(const char* s)
{
unsigned keybytelen = 0, hashkey = 0;
if (NULL != s)
keybytelen = (unsigned) strlen(s);
if (keybytelen)
hashkey = etchhash(s, keybytelen, 0);
return hashkey;
}
/**
* etch_get_wchar_hashkey
* hashkey computation using a unicode string as source
*/
unsigned etch_get_wchar_hashkey(const wchar_t* s)
{
unsigned keybytelen = 0, hashkey = 0;
if (NULL != s)
keybytelen = (unsigned) (wcslen(s) * sizeof(wchar_t));
if (keybytelen)
hashkey = etchhash(s, keybytelen, 0);
return hashkey;
}
/**
* etch_number_get_hashkey
* hashkey computation override for an etch wrapped primitive number.
* hash key is computed using the numeric value as hash source.
*/
unsigned etch_number_get_hashkey(objmask* etchobj)
{
unsigned bytelength, hashkey;
switch(etchobj->class_id)
{ case CLASSID_PRIMITIVE_INT32: case CLASSID_PRIMITIVE_FLOAT: bytelength = 4; break;
case CLASSID_PRIMITIVE_INT64: case CLASSID_PRIMITIVE_DOUBLE: case CLASSID_DATE: bytelength = 8; break;
case CLASSID_PRIMITIVE_INT16: bytelength = 2; break;
default: bytelength = 1;
}
hashkey = etchhash(&((etch_int64*)etchobj)->value, bytelength, 0);
return etchobj->hashkey = hashkey;
}