blob: 906a4574dcf52ee33702c16468fb02e2da66d767 [file]
/* $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_natarray.c
* etch_nativearray implementation
* does allocation and subscripting for n-dimensional arrays stored as a single
* byte vector, to overcome the well-known C compiler issues with passing around
* multi dimensioned arrays when the low order dimension is not known at compile
* time. the mapping here from byte vector to array tests out as k&r compliant,
* this is verified by constructing from a static array using new_nativearray_from
* and testing that the get methods return the same result as statically indexed
* array items. as it stands, this code handles a max of 3 dimensions. it can be
* generalized to n dimensions, however the caller would have to allocate, populate,
* and pass arrays of subscripts for this to happen.
*/
#include <stdio.h>
#include "etch_global.h"
etch_nativearray* init_nativearray
(unsigned short, const size_t, const int, const int, const int, const int);
int etch_nativearray_put1 (etch_nativearray*, void*, int);
int etch_nativearray_put2 (etch_nativearray*, void*, int, int);
int etch_nativearray_put3 (etch_nativearray*, void*, int, int, int);
int etch_nativearray_get1 (etch_nativearray*, void*, int);
int etch_nativearray_get2 (etch_nativearray*, void*, int, int);
int etch_nativearray_get3 (etch_nativearray*, void*, int, int, int);
int etch_nativearray_set_content_type(etch_nativearray*);
/*
* new_nativearray()
* etch_nativearray constructor
* note that when creating arrays, the dimensions are passed low-order first,
* so for x[2][3][4], dim0 is 4, dim1 is 3, dim2 is 2. for y[7][9], dim0 is 9,
* dim1 is 7, and dim2 is zero of course.
*/
etch_nativearray* new_nativearray (unsigned short class_id, const size_t itemsize,
const int numdims, const int dim0, const int dim1, const int dim2)
{
etch_nativearray* newarray = init_nativearray(class_id, itemsize, numdims, dim0, dim1, dim2);
if (NULL == newarray) return NULL;
newarray->values = etch_malloc(newarray->bytecount, ETCHTYPEB_BYTES);
memset(newarray->values, 0, newarray->bytecount);
newarray->is_content_owned = TRUE;
etch_nativearray_set_content_type(newarray);
return newarray;
}
/*
* new_nativearray_from()
* etch_nativearray constructor from existing byte vector
* @param values source array, caller retains ownership.
*/
etch_nativearray* new_nativearray_from (void* values, unsigned short class_id, const size_t itemsize,
const int numdims, const int dim0, const int dim1, const int dim2)
{
etch_nativearray* newarray = init_nativearray(class_id, itemsize, numdims, dim0, dim1, dim2);
if (NULL == newarray) return NULL;
newarray->values = values;
newarray->is_content_owned = FALSE;
etch_nativearray_set_content_type(newarray);
return newarray;
}
/*
* new_nativearray_of()
* etch_nativearray constructor for array of objects of specified type and class
*/
etch_nativearray* new_nativearray_of
(unsigned short content_obj_type, unsigned short content_class_id,
const int numdims, const int dim0, const int dim1, const int dim2)
{
etch_nativearray* newarray = init_nativearray
(CLASSID_ARRAY_OBJECT, sizeof(void*), numdims, dim0, dim1, dim2);
if (NULL == newarray) return NULL;
newarray->content_obj_type = content_obj_type;
newarray->content_class_id = content_class_id;
return newarray;
}
/*
* init_nativearray()
* etch_nativearray private constructor
* assumes max number of dimensions is 3
*/
etch_nativearray* init_nativearray (unsigned short class_id, const size_t itemsize,
const int numdims, const int dim0, const int dim1, const int dim2)
{
etch_nativearray* newarray = NULL;
int i,j;
if (numdims < 1 || itemsize < 1) return NULL;
if (numdims > 1 && dim1 < 1) return NULL;
if (numdims > 2 && dim2 < 1) return NULL;
newarray = newarray = (etch_nativearray*) new_object
(sizeof(etch_nativearray), ETCHTYPEB_NATIVEARRAY, class_id);
newarray->numdims = numdims;
newarray->itemsize = itemsize;
/* dimension array contains the array dimensions,
* e.g., for x[2][3][4], [0] is 4, [1] is 3, [2] is 2
*/
newarray->dimension[0] = dim0;
newarray->dimension[1] = numdims > 1? dim1: 0;
newarray->dimension[2] = numdims > 2? dim2: 0;
/* dimsize array contains size in bytes of the next lower dimension,
* stored so that the values need not be calculated on each array access.
* [0] is item size, so for int x[5][7][9], [0] is sizeof(int),
* [1] is 9*sizeof(int), [2] is 7*9*sizeof(int)
*/
newarray->dimsize[0] = itemsize;
newarray->bytecount = itemsize * dim0;
for(i = 1, j = 0; i < numdims; i++, j++)
{
newarray->dimsize[i] = newarray->dimension[j] * newarray->dimsize[j];
newarray->bytecount *= newarray->dimension[i];
}
newarray->clone = clone_null;
newarray->destroy = destroy_nativearray;
newarray->put1 = etch_nativearray_put1;
newarray->put2 = etch_nativearray_put2;
newarray->put3 = etch_nativearray_put3;
newarray->get1 = etch_nativearray_get1;
newarray->get2 = etch_nativearray_get2;
newarray->get3 = etch_nativearray_get3;
return newarray;
}
/**
* destroy_nativearray_content()
* destroy nativearray's content vector.
* return -1 if content not owned by nativearray and therefore not destroyed;
* otherwise return zero, regardless of whether content was null.
*/
int destroy_nativearray_content(etch_nativearray* a)
{
int result = 0;
if (!is_etchobj_static_content(a) && a->is_content_owned)
etch_free(a->values); /* OK if values null */
else result = -1;
return result;
}
/**
* destroy_nativearray()
* etch_nativearray destructor
*/
int destroy_nativearray(etch_nativearray* a)
{
if (a->refcount > 0 && --a->refcount > 0) return -1;
if (a->is_content_owned && !is_etchobj_static_content(a))
destroy_nativearray_content(a);
destroy_objectex((objmask*)a);
return 0;
}
/*
* new_subarray()
* construct and return subaarray[i] of this array.
* subarray points into parent content and so does not own its content
*/
etch_nativearray* new_subarray(etch_nativearray* a, const int i)
{
etch_nativearray* subarray = NULL;
const int sub_numdims = a->numdims - 1;
if (a->numdims > 1 && i < (int) a->dimension[sub_numdims])
{
const size_t sub_bytelen = a->dimsize[sub_numdims];
const size_t vector_offset = i * sub_bytelen;
byte* subvector = (byte*) a->values + vector_offset;
subarray = new_nativearray_from(subvector,
a->class_id, (int) a->itemsize, sub_numdims,
(int) a->dimension[0], (int) a->dimension[1], 0);
subarray->bytecount = sub_bytelen;
subarray->content_obj_type = a->content_obj_type;
subarray->content_class_id = a->content_class_id;
subarray->is_content_owned = a->is_content_owned;
subarray->counts[0] = a->counts[0]; /* user-maintained counts */
subarray->counts[1] = a->counts[1];
subarray->counts[2] = 0;
}
return subarray;
}
/*
* etch_nativearray_assign_to()
* assign array b to array a, if legal to do so.
*/
etch_nativearray* etch_nativearray_assign_to(etch_nativearray* a, etch_nativearray* b)
{
if (!is_etch_nativearray(a) || !is_etch_nativearray(b)) return NULL;
/* arrays must be either of same type, or the left side an array of
* object; and the dimensionalities of the two arrays must be the same.
*/
if(((a->class_id != b->class_id) && (a->class_id != CLASSID_ARRAY_OBJECT))
|| (a->numdims != b->numdims))
return NULL;
destroy_nativearray_content(a); /* destroy a content if owned */
a->values = b->values; /* assign b content to a */
a->itemsize = b->itemsize;
a->bytecount = b->bytecount;
memcpy(a->dimension, b->dimension, sizeof(a->dimension));
memcpy(a->dimsize, b->dimsize, sizeof(a->dimsize));
memcpy(a->counts, b->counts, sizeof(a->counts));
a->content_obj_type = b->content_obj_type;
a->content_class_id = b->content_class_id;
a->is_content_owned = FALSE; /* b still owns array content */
a->is_copy = TRUE;
return a;
}
/*
* etch_nativearray_get_element()
* return element[i] of specified array, which, if array is multi-dimensioned,
* will be another native array, otherwise an etch object. for example, if
* the supplied array is byte[2][4], a native array of byte[4] is returned.
* if the supplied array is byte[4], an etch_byte object is returned.
* returned subarrays do not own content, their content references the content
* of their parent.
*/
objmask* etch_nativearray_get_element(etch_nativearray* a, const int i)
{
int result = 0;
objmask* wrapped_element = NULL;
etch_nativearray* subarray = NULL;
const int sub_numdims = a->numdims - 1;
if (sub_numdims < 0) return NULL;
if (sub_numdims > 0)
return (objmask*) new_subarray(a, i);
result = etch_nativearray_get_wrapped_component(a, i, &wrapped_element);
return wrapped_element;
}
/*
* etch_nativearray_set_content_type()
* set content_obj_type and content_class_id based on array class_id.
* returns zero if content type was set, otherwise -1
*/
int etch_nativearray_set_content_type(etch_nativearray* a)
{
unsigned short obj_type = 0;
int result = 0;
switch(a->class_id)
{
case CLASSID_ARRAY_BYTE: obj_type = ETCHTYPEB_BYTE; break;
case CLASSID_ARRAY_BOOL: obj_type = ETCHTYPEB_BOOL; break;
case CLASSID_ARRAY_INT8: obj_type = ETCHTYPEB_INT8; break;
case CLASSID_ARRAY_INT16: obj_type = ETCHTYPEB_INT16; break;
case CLASSID_ARRAY_INT32: obj_type = ETCHTYPEB_INT32; break;
case CLASSID_ARRAY_INT64: obj_type = ETCHTYPEB_INT64; break;
case CLASSID_ARRAY_FLOAT: obj_type = ETCHTYPEB_IEEE32; break;
case CLASSID_ARRAY_DOUBLE: obj_type = ETCHTYPEB_IEEE64; break;
}
if (obj_type)
{ a->content_obj_type = obj_type;
a->content_class_id = CLASSID_UNWRAPPED;
}
else switch(a->class_id)
{
case CLASSID_ARRAY_OBJECT:
a->content_obj_type = ETCHTYPEB_ETCHOBJECT;
a->content_class_id = CLASSID_OBJECT;
break;
case CLASSID_ARRAY_STRING:
a->content_obj_type = ETCHTYPEB_STRING;
a->content_class_id = CLASSID_STRING;
break;
default:
result = -1; /* indicates content type not set */
}
return result;
}
/**
* put1()
* insert item to singly dimensioned array
* array access can be generalized by passing an array of subscripts; however
* since we are currently handling max 3 dimensions, the calling sequence is
* much simpler when we have separate accessors for each dimensionality.
*/
int etch_nativearray_put1(etch_nativearray* a, void* value, int i)
{
size_t offset = etch_nativearray_off1(a, i);
if (value == NULL || offset < 0) return -1;
memcpy((byte*)a->values + offset, value, a->itemsize);
return 0;
}
/**
* put2()
* insert item to 2-dimensional array
*/
int etch_nativearray_put2(etch_nativearray* a, void* value, int i, int j)
{
size_t offset = etch_nativearray_off2(a, i, j);
if (value == NULL || offset < 0) return -1;
memcpy((byte*)a->values + offset, value, a->itemsize);
return 0;
}
/**
* put3()
* insert item to 3-dimensional array
*/
int etch_nativearray_put3(etch_nativearray* a, void* value, int i, int j, int k)
{
size_t offset = etch_nativearray_off3(a, i, j, k);
if (value == NULL || offset < 0) return -1;
memcpy((byte*)a->values + offset, value, a->itemsize);
return 0;
}
/**
* get1()
* access and return item from singly dimensioned array
*/
int etch_nativearray_get1(etch_nativearray* a, void* value, int i)
{
size_t offset = etch_nativearray_off1(a, i);
if (value == NULL || offset < 0) return -1;
memcpy(value, (byte*)a->values + offset, a->itemsize);
return 0;
}
/**
* get2()
* access and return item from 2-dimensional array
*/
int etch_nativearray_get2(etch_nativearray* a, void* value, int i, int j)
{
size_t offset = etch_nativearray_off2(a, i, j);
if (value == NULL || offset < 0) return -1;
memcpy(value, (byte*)a->values + offset, a->itemsize);
return 0;
}
/**
* get3()
* access and return item from 3-dimensional array
*/
int etch_nativearray_get3(etch_nativearray* a, void* value, int i, int j, int k)
{
size_t offset = etch_nativearray_off3(a, i, j, k);
if (value == NULL || offset < 0) return -1;
memcpy(value, (byte*)a->values + offset, a->itemsize);
return 0;
}
/*
* etch_nativearray_off1()
* calculate byte offset into byte vector for a 1-dimension array
*/
size_t etch_nativearray_off1(etch_nativearray* a, int i)
{
size_t offset = 0;
if (i >= (int) a->dimension[0] || i < 0) return -1;
offset = i * a->dimsize[0];
return offset;
}
/*
* etch_nativearray_off2()
* calculate byte offset into byte vector for a 2-dimension array
*/
size_t etch_nativearray_off2(etch_nativearray* a, int i, int j)
{
size_t offset = 0;
if ((i >= (int) a->dimension[1] || i < 0)
|| (j >= (int) a->dimension[0] || j < 0)) return -1;
offset = (i * a->dimsize[1]) + (j * a->dimsize[0]);
return offset;
}
/*
* etch_nativearray_off3()
* calculate byte offset into byte vector for a 3-dimension array
*/
size_t etch_nativearray_off3(etch_nativearray* a, int i, int j, int k)
{
size_t offset = 0;
if ((i >= (int) a->dimension[2] || i < 0)
|| (j >= (int) a->dimension[1] || j < 0)
|| (k >= (int) a->dimension[0] || k < 0)) return -1;
offset = (i * a->dimsize[2]) + (j * a->dimsize[1]) + (k * a->dimsize[0]);
return offset;
}
/**
* etch_nativearray_get_component_type()
* java.lang.class.getcomponenttype returns the class of an array's members.
* we instead identify the etch object type and class id of the array's members,
* returning results in the passed parameter struct reference.
*/
int etch_nativearray_get_component_type
(etch_object* classobj, etch_component_type_params* p)
{
int result = 0;
if (!p) return -1;
memset(p, 0, sizeof(etch_component_type_params));
if (!is_etch_nativearray(classobj)) return -1;
p->origl_array = (etch_nativearray*) classobj;
p->origl_obj_type = p->origl_array->content_obj_type;
p->origl_class_id = p->origl_array->content_class_id;
p->dimensions = p->origl_array->numdims;
p->final_array = p->origl_array;
p->final_obj_type = p->origl_obj_type;
p->final_class_id = p->origl_class_id;
/* we could have here an array of (pointers to) array. recurse the array
* until array content is no longer array.
*/
while(is_etch_nativearray(p->final_obj_type))
{
/* the content of the current array is array,
* so peek at item[0] in this array of arrays */
etch_nativearray* content_array = p->final_array->bytecount >= sizeof(void*)?
(etch_nativearray*) ((etch_nativearray**) p->final_array->values)[0]: NULL;
if (!is_etch_nativearray(content_array))
{ result = -1;/* sanity check, [0] not array, however content */
break; /* type indicated array, logic error, exception */
}
p->final_array = content_array;
p->final_obj_type = content_array->content_obj_type;
p->final_class_id = content_array->content_class_id;
if (is_etch_nativearray(p->final_obj_type))
p->dimensions += p->final_array->numdims;
}
return result;
}
/**
* etch_nativearray_get_wrapped_component()
* get wrapped component[i] from the single-dimensioned native array.
* caller owns returned object.
* todo: test that if array content was wrapped, the is_value_owned
* setting is sufficient for tranparent memory management of returned object.
*/
int etch_nativearray_get_wrapped_component(etch_nativearray* a, const int i, objmask** out)
{
const int numentries = (int) a->dimension[0];
const int is_wrapped_content = a->content_class_id != 0;
int result = 0;
union
{ char vbyte; short vint16; int vint32; int64 vint64;
float vfloat;double vdouble;void* vaddr; int64 all;
} v;
if (a->numdims != 1 || i >= numentries) return -1;
v.all = 0;
if (is_wrapped_content)
{ /* content of the native array is wrapped, e.g, etch_object*,
* so we return the item as is */
result = a->get1(a, &v.vaddr, i);
*out = v.vaddr;
}
else switch(a->content_obj_type)
{ /* content of the native array is unwrapped, e.g, int[], so we wrap
* the array item with an etch object prior to returning it */
case ETCHTYPEB_BYTE:
result = a->get1(a, &v.vbyte, i);
*out = (objmask*) new_byte(v.vbyte);
break;
case ETCHTYPEB_BOOL:
result = a->get1(a, &v.vbyte, i);
*out = (objmask*) new_boolean(v.vbyte);
break;
case ETCHTYPEB_INT8:
result = a->get1(a, &v.vbyte, i);
*out = (objmask*) new_int8(v.vbyte);
break;
case ETCHTYPEB_INT16:
result = a->get1(a, &v.vint16, i);
*out = (objmask*) new_int16(v.vint16);
break;
case ETCHTYPEB_INT32:
result = a->get1(a, &v.vint32, i);
*out = (objmask*) new_int32(v.vint32);
break;
case ETCHTYPEB_INT64:
result = a->get1(a, &v.vint64, i);
*out = (objmask*) new_int64(v.vint64);
break;
case ETCHTYPEB_IEEE32:
result = a->get1(a, &v.vfloat, i);
*out = (objmask*) new_float(v.vfloat);
break;
case ETCHTYPEB_IEEE64:
result = a->get1(a, &v.vdouble, i);
*out = (objmask*) new_double(v.vdouble);
break;
case ETCHTYPEB_STRING:
result = a->get1(a, &v.vaddr, i);
*out = (objmask*) new_string(v.vaddr, ETCH_ENCODING_UTF16);
break;
default: /* item is an anonymous pointer to a value owned by caller */
{ etch_object* wrapper = NULL;
result = a->get1(a, &v.vaddr, i);
wrapper = new_etch_object(a->content_class_id, v.vaddr);
/* we clear is_value_owned so value will not be freed by dtor */
wrapper->is_value_object = wrapper->is_value_owned = FALSE;
*out = (objmask*) wrapper;
}
} /* switch(a->content_obj_type) */
return result;
}