blob: 89ffccaae31a89cc2723977cdd4d6955d6a7f131 [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.
*/
#include "serialization/EtchBinaryTaggedDataInput.h"
#include "support/EtchRuntime.h"
static const char* TAG = "EtchBinaryTaggedDataInput";
EtchBinaryTaggedDataInput::EtchBinaryTaggedDataInput(EtchRuntime* runtime, EtchValueFactory* vf)
: EtchBinaryTaggedData(vf), mRuntime(runtime), mBuffer(NULL), mLengthBudget(0) {
//get int validator
EtchValidatorInt::Get(runtime, 0, mIntValidator);
//get string validator
capu::SmartPointer<EtchValidator> stringValidator;
EtchValidatorString::Get(runtime, 0, stringValidator);
//create combo validator
mIntOrStrValidator = new EtchComboValidator(mIntValidator, stringValidator);
}
EtchBinaryTaggedDataInput::~EtchBinaryTaggedDataInput() {
}
status_t EtchBinaryTaggedDataInput::readMessage(capu::SmartPointer<EtchFlexBuffer> buf, capu::SmartPointer<EtchMessage> &message) {
mBuffer = buf;
status_t ret;
// lengthBudget is how many array elements total are reasonable to
// allocate while parsing this message. the largest value comes as each
// byte of the incoming message turned into an array element. the total
// will always be lower than this as often it takes multiple bytes to
// make a single array element. so, each time we make an array, we
// deduct the specified length from lengthBudget. if the result is
// negative, then either the incoming message is misformed or someone is
// trying to spoof us.
mLengthBudget = buf->getAvailableBytes();
ret = startMessage(message);
if (ret != ETCH_OK) {
mBuffer = NULL;
mLengthBudget = 0;
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Deserialization of Message header has failed");
return ret;
}
ret = readKeysAndValues(message);
if (ret != ETCH_OK) {
mBuffer = NULL;
mLengthBudget = 0;
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Deserialization of Message body has failed");
return ret;
}
ret = endMessage(message);
if (ret != ETCH_OK) {
mBuffer = NULL;
mLengthBudget = 0;
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Deserialization of Message end has failed");
return ret;
}
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readStruct(capu::SmartPointer<EtchStructValue> &result) {
status_t ret;
ret = startStruct(result);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading header of struct has failed");
return ret;
}
ret = readKeysAndValues(result);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading body of struct has failed");
return ret;
}
ret = endStruct(result);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading end of struct has failed");
return ret;
}
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readArray(capu::SmartPointer<EtchValidator> v, EtchArrayValue *&result) {
status_t ret;
ret = startArray(result);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading header of array has failed");
return ret;
}
ret = readValues(result, v);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading body of array has failed");
return ret;
}
ret = endArray(result);
if (ret != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Reading end of array has failed");
return ret;
}
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readKeysAndValues(capu::SmartPointer<EtchStructValue> sv) {
status_t ret;
EtchType* t = sv->getType();
while (true) {
EtchField key;
ret = readField(t, key);
if (ret != ETCH_OK) {
break;
}
capu::SmartPointer<EtchValidator> v;
if (t->getValidator(key, v) == ETCH_OK) { // read the value but ignore it.
capu::SmartPointer<EtchObject> obj;
if (readValue(v, obj) == ETCH_OK) {
sv->put(key, obj);
}
} else {
capu::SmartPointer<EtchObject> obj;
capu::SmartPointer<EtchValidator> val;
EtchValidatorObject::Get(mRuntime, 0, val);
readValue(val, obj);
}
}
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readValues(EtchArrayValue *av, capu::SmartPointer<EtchValidator> v) {
capu::SmartPointer<EtchValidator> validator;
v->getElementValidator(validator);
while (true) {
capu::SmartPointer<EtchObject> value;
readValue(validator, true, value);
if (value == NONE()) {
break;
}
av->add(value);
}
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::startMessage(capu::SmartPointer<EtchMessage> &result) {
capu::int8_t version = 0;
mBuffer->getByte(version);
if (version != VERSION()) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Incorrect serializer version");
return ETCH_EIO;
}
EtchType *type = NULL;
if (readType(type) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Type information is not read correctly");
return ETCH_EIO;
}
capu::int32_t length = 0;
if (readLength(length) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Length of message is not read correctly");
return ETCH_EIO;
}
result = new EtchMessage(type, mVf, length);
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::endMessage(capu::SmartPointer<EtchMessage> msg) {
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::startStruct(capu::SmartPointer<EtchStructValue> &result) {
EtchType* t = NULL;
if (readType(t) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "type of struct is not read correctly");
return ETCH_ERROR;
}
capu::int32_t length;
if (readLength(length) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Length of struct is not read correctly");
return ETCH_ERROR;
}
result = new EtchStructValue(t, mVf, length);
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::endStruct(capu::SmartPointer<EtchStructValue> result) {
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::startArray(EtchArrayValue *& result) {
capu::int8_t type;
mBuffer->getByte(type);
EtchType* customStructType = NULL;
if (type == EtchTypeCode::CUSTOM || type == EtchTypeCode::STRUCT) {
if (readType(customStructType) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "type of array is not read correctly");
return ETCH_ERROR;
}
} else {
customStructType = NULL;
}
capu::int32_t dim;
if (readIntegerValue(dim) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "dimension of array is not read correctly");
return ETCH_EINVAL;
}
if (dim <= 0 || dim > (capu::int32_t)EtchValidator::MAX_NDIMS) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "dimension of array is not within predefined limits");
return ETCH_EINVAL;
}
capu::int32_t length;
if (readLength(length) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "length of array is not read correctly");
return ETCH_ERROR;
}
capu::SmartPointer<EtchNativeArrayBase> array;
if (allocNativeArray(type, customStructType, dim, length, array) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "Allocation of array with specified type has failed");
return ETCH_ERROR;
}
result = new EtchArrayValue(array, length, type, customStructType, dim);
result->setIndex(0);
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::endArray(EtchArrayValue *array) {
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readType(EtchType *&type) {
capu::SmartPointer<EtchObject> obj;
if (readValue(mIntOrStrValidator, false, obj) != ETCH_OK) {
return ETCH_ERROR;
}
if (obj->getObjectType()->equals(EtchInt32::TYPE())) {
EtchInt32* id = (EtchInt32*) obj.get();
if (mVf->getType(id->get(), type) != ETCH_OK) {
char num[100];
capu::StringUtils::Sprintf(num, 100, "%d", id->get());
EtchString str(num);
type = new EtchType(id->get(), str);
}
return ETCH_OK;
}
if (obj->getObjectType()->equals(EtchString::TYPE())) {
EtchString* name = (EtchString*) obj.get();
if (mVf->getType(*name, type) != ETCH_OK) {
type = new EtchType(*name);
}
return ETCH_OK;
}
return ETCH_ERROR;
}
status_t EtchBinaryTaggedDataInput::readField(EtchType *type, EtchField& field) {
capu::SmartPointer<EtchObject> obj;
if (readValue(mIntOrStrValidator, true, obj) != ETCH_OK) {
return ETCH_ERROR;
}
if (obj == NONE()) {
return ETCH_ENOT_EXIST;
}
if (obj->getObjectType()->equals(EtchInt32::TYPE())) {
EtchInt32* id = (EtchInt32*) obj.get();
if (type->getField(id->get(), &field) != ETCH_OK) {
char num[100];
capu::StringUtils::Sprintf(num, 100, "%d", id->get());
EtchString str(num);
EtchField f(id->get(), str);
//TODO: Create field on heap and use smartpointers
field = f;
}
return ETCH_OK;
}
if (obj->getObjectType()->equals(EtchString::TYPE())) {
EtchString* name = (EtchString*) obj.get();
if (type->getField(*name, &field) != ETCH_OK) {
EtchField f(*name);
//TODO: Create field on heap and use smartpointers
field = f;
}
return ETCH_OK;
}
return ETCH_ERROR;
}
status_t EtchBinaryTaggedDataInput::readLength(capu::int32_t& result) {
capu::int32_t length = 0;
if (readIntegerValue(length) != ETCH_OK) {
CAPU_LOG_ERROR(mRuntime->getLogger(), TAG, "length information is not read correctly");
return ETCH_ERROR;
}
if ((length < 0) || (length > mLengthBudget)) {
return ETCH_EIO;
}
mLengthBudget -= length;
result = length;
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::readIntegerValue(capu::int32_t& result) {
capu::SmartPointer<EtchObject> tmp;
if (readValue(mIntValidator, tmp) != ETCH_OK) {
return ETCH_ERROR;
}
result = capu::smartpointer_cast<EtchInt32>(tmp)->get();
return ETCH_OK;
}
status_t EtchBinaryTaggedDataInput::validateValue(capu::SmartPointer<EtchValidator> v, capu::SmartPointer<EtchObject> value, capu::SmartPointer<EtchObject>& result) {
// v == null more or less implies that a field is not known
// for a type. thus we don't care about the field value as
// we are going to ignore it. therefore, return null.
if (v.get() == NULL)
return ETCH_OK;
if (value.get() == NULL)
return ETCH_OK;
if (v->validateValue(value, result) == ETCH_OK) {
return ETCH_OK;
} else {
return ETCH_ERROR;
}
}
status_t EtchBinaryTaggedDataInput::validateValue(capu::SmartPointer<EtchValidator> v, capu::bool_t noneOk, capu::SmartPointer<EtchObject> value, capu::SmartPointer<EtchObject>& result) {
if (noneOk && (value == NONE())) {
result = value;
return ETCH_OK;
}
return validateValue(v, value, result);
}
status_t EtchBinaryTaggedDataInput::validate(capu::SmartPointer<EtchValidator> v, capu::SmartPointer<EtchObject> value) {
// v == null more or less implies that a field is not known
// for a type. thus we don't care about the field value as
// we are going to ignore it. therefore, return null.
if (v.get() == NULL)
return ETCH_ERROR;
if (value.get() == NULL)
return ETCH_ERROR;
if (v->validate(value)) {
return ETCH_OK;
} else {
return ETCH_ERROR;
}
}
status_t EtchBinaryTaggedDataInput::readValue(capu::SmartPointer<EtchValidator> v, capu::SmartPointer<EtchObject>& result) {
return readValue(v, false, result);
}
status_t EtchBinaryTaggedDataInput::readValue(capu::SmartPointer<EtchValidator> v, capu::bool_t noneOk, capu::SmartPointer<EtchObject>& result) {
capu::int8_t type;
if (mBuffer->getByte(type) != ETCH_OK) {
return ETCH_ERROR;
}
switch (type) {
case EtchTypeCode::Null:
return validateValue(v, NULL, result);
case EtchTypeCode::NONE:
return validateValue(v, noneOk, NONE(), result);
case EtchTypeCode::BOOLEAN_FALSE:
{
capu::SmartPointer<EtchBool> boolean = new EtchBool(false);
if (validateValue(v, boolean, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Boolean false value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::BOOLEAN_TRUE:
{
capu::SmartPointer<EtchBool> boolean = new EtchBool(true);
if (validateValue(v, boolean, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Boolean true value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::BYTE:
{
capu::int8_t tmp;
if (mBuffer->getByte(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchByte> byte = new EtchByte(tmp);
if (validateValue(v, byte, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Byte value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::SHORT:
{
capu::int16_t tmp;
if (mBuffer->getShort(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchShort> shortNum = new EtchShort(tmp);
if (validateValue(v, shortNum, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Short value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::INT:
{
capu::int32_t tmp;
if (mBuffer->getInteger(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchInt32> intNum = new EtchInt32(tmp);
if (validateValue(v, intNum, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Int value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::LONG:
{
capu::int64_t tmp;
if (mBuffer->getLong(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchLong> longNum = new EtchLong(tmp);
if (validateValue(v, longNum, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Long value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::FLOAT:
{
capu::float_t tmp;
if (mBuffer->getFloat(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchFloat> floatNum = new EtchFloat(tmp);
if (validateValue(v, floatNum, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Float value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::DOUBLE:
{
capu::double_t tmp;
if (mBuffer->getDouble(tmp) != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchDouble> doubleNum = new EtchDouble(tmp);
if (validateValue(v, doubleNum, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Double value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::BYTES:
{
status_t ret;
capu::int8_t *buffer = NULL;
capu::uint32_t bufferSize = 0;
ret = readBytes(buffer, bufferSize);
if (ret != ETCH_OK) {
return ETCH_ERROR;
}
capu::SmartPointer<EtchNativeArray<capu::int8_t> > narray = new EtchNativeArray<capu::int8_t > (bufferSize);
narray->set(0, buffer, bufferSize, 0, bufferSize);
delete[] buffer;
ret = validateValue(v, narray, result);
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Byte Array has been received");
return ret;
}
// reserved for future use:
// case TypeCode.BOOLS:
// case TypeCode.SHORTS:
// case TypeCode.INTS:
// case TypeCode.LONGS:
// case TypeCode.FLOATS:
// case TypeCode.DOUBLES:
case EtchTypeCode::ARRAY:
{
status_t ret;
EtchArrayValue *arrayval = NULL;
ret = readArray(v, arrayval);
if (ret != ETCH_OK) {
if (arrayval != NULL)
delete arrayval;
return ETCH_ERROR;
}
capu::SmartPointer<EtchObject> array;
ret = fromArrayValue(arrayval, array);
if (ret != ETCH_OK) {
delete arrayval;
return ETCH_ERROR;
}
delete arrayval;
if (validate(v, array) != ETCH_OK) {
return ETCH_ERROR;
} else {
result = array;
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Array has been received");
return ETCH_OK;
}
}
case EtchTypeCode::EMPTY_STRING:
{
EtchString *str = new EtchString(NULL, 0, mVf->getStringEncoding());
if (validateValue(v, str, result) != ETCH_OK) {
delete str;
return ETCH_ERROR;
} else {
result = str;
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Empty String value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::STRING:
{
status_t ret;
capu::int8_t *buffer = NULL;
capu::uint32_t bufferSize;
ret = readBytes(buffer, bufferSize);
if (ret != ETCH_OK)
return ETCH_ERROR;
capu::SmartPointer<EtchString> str = new EtchString(buffer, bufferSize, mVf->getStringEncoding());
delete [] buffer;
ret = validateValue(v, str, result);
if (ret != ETCH_OK) {
return ETCH_ERROR;
} else {
result = str;
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "String value has been received");
return ETCH_OK;
}
}
case EtchTypeCode::STRUCT:
case EtchTypeCode::CUSTOM:
{
capu::SmartPointer<EtchStructValue> sv;
if (readStruct(sv) != ETCH_OK) {
return ETCH_ERROR;
}
capu::SmartPointer<EtchObject> obj;
if (mVf->importCustomValue(sv.get(), obj) != ETCH_OK) {
return ETCH_ERROR;
}
if (validateValue(v, obj, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Custom value has been received");
return ETCH_OK;
}
}
default:
{
if (type >= EtchTypeCode::MIN_TINY_INT && type <= EtchTypeCode::MAX_TINY_INT) {
capu::SmartPointer<EtchByte> tmp = new EtchByte(type);
if (validateValue(v, tmp, result) != ETCH_OK) {
return ETCH_ERROR;
} else {
CAPU_LOG_TRACE(mRuntime->getLogger(), TAG, "Tiny Int value has been received");
return ETCH_OK;
}
}
return ETCH_ERANGE;
}
}
}
status_t EtchBinaryTaggedDataInput::readBytes(capu::int8_t*& array, capu::uint32_t &length) {
capu::int32_t _length;
if (readLength(_length) != ETCH_OK)
return ETCH_ERROR;
array = new capu::int8_t[_length];
mBuffer->get(array, (capu::uint32_t) 0, (capu::uint32_t) _length, length);
if (_length != (capu::int32_t) length) {
delete[] array;
return ETCH_ERROR;
}
return ETCH_OK;
}