diff --git a/pvDataApp/Makefile b/pvDataApp/Makefile index f676948..401f688 100644 --- a/pvDataApp/Makefile +++ b/pvDataApp/Makefile @@ -66,6 +66,8 @@ LIBSRCS += PVArray.cpp LIBSRCS += PVScalarArray.cpp LIBSRCS += PVStructure.cpp LIBSRCS += PVStructureArray.cpp +LIBSRCS += PVUnion.cpp +LIBSRCS += PVUnionArray.cpp LIBSRCS += PVDataCreateFactory.cpp LIBSRCS += Convert.cpp LIBSRCS += pvSubArrayCopy.cpp diff --git a/pvDataApp/factory/Compare.cpp b/pvDataApp/factory/Compare.cpp index f6780dc..79cd817 100644 --- a/pvDataApp/factory/Compare.cpp +++ b/pvDataApp/factory/Compare.cpp @@ -48,6 +48,16 @@ bool operator==(const Field& a, const Field& b) const StructureArray &B=static_cast(b); return A==B; } + case union_: { + const Union &A=static_cast(a); + const Union &B=static_cast(b); + return A==B; + } + case unionArray: { + const UnionArray &A=static_cast(a); + const UnionArray &B=static_cast(b); + return A==B; + } default: throw std::logic_error("Invalid Field type in comparision"); } @@ -94,6 +104,33 @@ bool operator==(const StructureArray& a, const StructureArray& b) return *(a.getStructure().get())==*(b.getStructure().get()); } +bool operator==(const Union& a, const Union& b) +{ + if(&a==&b) + return true; + if (a.getID()!=b.getID()) + return false; + size_t nflds=a.getNumberFields(); + if (b.getNumberFields()!=nflds) + return false; + + // std::equals does not work, since FieldConstPtrArray is an array of shared_pointers + FieldConstPtrArray af = a.getFields(); + FieldConstPtrArray bf = b.getFields(); + for (size_t i = 0; i < nflds; i++) + if (*(af[i].get()) != *(bf[i].get())) + return false; + + StringArray an = a.getFieldNames(); + StringArray bn = b.getFieldNames(); + return std::equal( an.begin(), an.end(), bn.begin() ); +} + +bool operator==(const UnionArray& a, const UnionArray& b) +{ + return *(a.getUnion().get())==*(b.getUnion().get()); +} + // PVXXX object comparison namespace { @@ -210,6 +247,59 @@ bool compareField(const PVStructureArray* left, const PVStructureArray* right) return true; } +bool compareField(const PVUnion* left, const PVUnion* right) +{ + UnionConstPtr ls = left->getUnion(); + + if(*ls!=*right->getUnion()) + return false; + + if (ls->isVariant()) + { + PVFieldPtr lval = left->get(); + if (lval.get() == 0) + return right->get().get() == 0; + else + return *(lval.get()) == *(right->get().get()); + } + else + { + int32 lix = left->getSelectedIndex(); + if (lix == right->getSelectedIndex()) + { + if (lix == PVUnion::UNDEFINED_INDEX || *(left->get()) == *(right->get().get())) + return true; + else + return false; + } + else + return false; + } +} + +bool compareField(const PVUnionArray* left, const PVUnionArray* right) +{ + if(*left->getUnionArray()->getUnion() + != *right->getUnionArray()->getUnion()) + return false; + + PVUnionArray::const_svector ld=left->view(), rd=right->view(); + + if(ld.size()!=rd.size()) + return false; + + PVUnionArray::const_svector::const_iterator lit, lend, rit; + + for(lit=ld.begin(), lend=ld.end(), rit=rd.begin(); + lit!=lend; + ++lit, ++rit) + { + if(**lit != **rit) + return false; + } + return true; +} + } // end namespace // untyped comparison @@ -228,6 +318,8 @@ bool operator==(const PVField& left, const PVField& right) case scalarArray: return compareField(static_cast(&left), static_cast(&right)); case structure: return compareField(static_cast(&left), static_cast(&right)); case structureArray: return compareField(static_cast(&left), static_cast(&right)); + case union_: return compareField(static_cast(&left), static_cast(&right)); + case unionArray: return compareField(static_cast(&left), static_cast(&right)); } throw std::logic_error("PVField with invalid type!"); } diff --git a/pvDataApp/factory/Convert.cpp b/pvDataApp/factory/Convert.cpp index 627c062..763b13b 100644 --- a/pvDataApp/factory/Convert.cpp +++ b/pvDataApp/factory/Convert.cpp @@ -84,7 +84,7 @@ size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const processed++; } else { - // structureArray not supported + // union, structureArray, unionArray not supported String message("Convert::fromString unsupported fieldType "); TypeFunc::toString(&message,type); throw std::logic_error(message); @@ -176,6 +176,18 @@ bool Convert::isCopyCompatible(FieldConstPtr const &from, FieldConstPtr const &t StructureArrayConstPtr yyy = static_pointer_cast(to); return isCopyStructureArrayCompatible(xxx,yyy); } + case union_: + { + UnionConstPtr xxx = static_pointer_cast(from); + UnionConstPtr yyy = static_pointer_cast(to); + return isCopyUnionCompatible(xxx,yyy); + } + case unionArray: + { + UnionArrayConstPtr xxx = static_pointer_cast(from); + UnionArrayConstPtr yyy = static_pointer_cast(to); + return isCopyUnionArrayCompatible(xxx,yyy); + } } String message("Convert::isCopyCompatible should never get here"); throw std::logic_error(message); @@ -211,6 +223,19 @@ void Convert::copy(PVFieldPtr const & from, PVFieldPtr const & to) copyStructureArray(fromArray,toArray); return; } + case union_: + { + PVUnionPtr xxx = static_pointer_cast(from); + PVUnionPtr yyy = static_pointer_cast(to); + copyUnion(xxx,yyy); + return; + } + case unionArray: { + PVUnionArrayPtr fromArray = static_pointer_cast(from); + PVUnionArrayPtr toArray = static_pointer_cast(to); + copyUnionArray(fromArray,toArray); + return; + } } } @@ -291,6 +316,19 @@ bool Convert::isCopyStructureCompatible( StructureArrayConstPtr yyy = static_pointer_cast(to); if(!isCopyStructureArrayCompatible(xxx,yyy)) return false; } + case union_: + { + UnionConstPtr xxx = static_pointer_cast(from); + UnionConstPtr yyy = static_pointer_cast(to); + if(!isCopyUnionCompatible(xxx,yyy)) return false; + } + break; + case unionArray: + { + UnionArrayConstPtr xxx = static_pointer_cast(from); + UnionArrayConstPtr yyy = static_pointer_cast(to); + if(!isCopyUnionArrayCompatible(xxx,yyy)) return false; + } break; } } @@ -380,10 +418,60 @@ void Convert::copyStructure(PVStructurePtr const & from, PVStructurePtr const & copyStructureArray(fromArray,toArray); break; } + case union_: + { + PVUnionPtr xxx = static_pointer_cast(fromData); + PVUnionPtr yyy = static_pointer_cast(toData); + copyUnion(xxx,yyy); + break; + } + case unionArray: + { + PVUnionArrayPtr fromArray = + static_pointer_cast(fromData); + PVUnionArrayPtr toArray = + static_pointer_cast(toData); + copyUnionArray(fromArray,toArray); + break; + } } } } +bool Convert::isCopyUnionCompatible( + UnionConstPtr const &from, UnionConstPtr const &to) +{ + return *(from.get()) == *(to.get()); +} + +void Convert::copyUnion(PVUnionPtr const & from, PVUnionPtr const & to) +{ + if(to->isImmutable()) { + if(from==to) return; + throw std::invalid_argument("Convert.copyUnion destination is immutable"); + } + if(from==to) return; + if(!isCopyUnionCompatible(from->getUnion(), to->getUnion())) { + throw std::invalid_argument("Illegal copyUnion"); + } + + PVFieldPtr fromValue = from->get(); + if (from->getUnion()->isVariant()) + { + if (fromValue.get() == 0) + to->set(PVFieldPtr()); + else + to->set(getPVDataCreate()->createPVField(fromValue)); // clone value // TODO + } + else + { + if (fromValue.get() == 0) + to->select(PVUnion::UNDEFINED_INDEX); + else + copy(fromValue, to->select(from->getSelectedIndex())); + } +} + bool Convert::isCopyStructureArrayCompatible( StructureArrayConstPtr const &from, StructureArrayConstPtr const &to) { @@ -403,6 +491,25 @@ void Convert::copyStructureArray( to->replace(from->view()); } +bool Convert::isCopyUnionArrayCompatible( + UnionArrayConstPtr const &from, UnionArrayConstPtr const &to) +{ + UnionConstPtr xxx = from->getUnion(); + UnionConstPtr yyy = to->getUnion(); + return isCopyUnionCompatible(xxx,yyy); +} + +void Convert::copyUnionArray( + PVUnionArrayPtr const & from, PVUnionArrayPtr const & to) +{ + if(from==to) { + return; + } else if(to->isImmutable()) { + throw std::invalid_argument("Convert.copyUnionArray destination is immutable"); + } + to->replace(from->view()); +} + void Convert::newLine(StringBuilder buffer, int indentLevel) { *buffer += "\n"; diff --git a/pvDataApp/factory/FieldCreateFactory.cpp b/pvDataApp/factory/FieldCreateFactory.cpp index 87f50ec..bc78bf2 100644 --- a/pvDataApp/factory/FieldCreateFactory.cpp +++ b/pvDataApp/factory/FieldCreateFactory.cpp @@ -56,7 +56,7 @@ struct ScalarArrayHashFunction { struct StructureHashFunction { size_t operator() (const Structure& /*structure*/) const { return 0; } - // TODO + // TODO hash // final int PRIME = 31; // return PRIME * Arrays.hashCode(fieldNames) + Arrays.hashCode(fields); }; @@ -934,7 +934,7 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl if (code == -1) return FieldConstPtr(); - int typeCode = code & 0xE0; + int typeCode = code & 0xEF; bool notArray = ((code & 0x10) == 0); if (notArray) { diff --git a/pvDataApp/factory/PVDataCreateFactory.cpp b/pvDataApp/factory/PVDataCreateFactory.cpp index adf813f..4123962 100644 --- a/pvDataApp/factory/PVDataCreateFactory.cpp +++ b/pvDataApp/factory/PVDataCreateFactory.cpp @@ -446,6 +446,14 @@ PVFieldPtr PVDataCreate::createPVField(FieldConstPtr const & field) StructureArrayConstPtr xx = static_pointer_cast(field); return createPVStructureArray(xx); } + case union_: { + UnionConstPtr xx = static_pointer_cast(field); + return createPVUnion(xx); + } + case unionArray: { + UnionArrayConstPtr xx = static_pointer_cast(field); + return createPVUnionArray(xx); + } } throw std::logic_error("PVDataCreate::createPVField should never get here"); } @@ -482,6 +490,21 @@ PVFieldPtr PVDataCreate::createPVField(PVFieldPtr const & fieldToClone) getConvert()->copyStructureArray(from, to); return to; } + case union_: + { + PVUnionPtr pvUnion + = static_pointer_cast(fieldToClone); + return createPVUnion(pvUnion); + } + case unionArray: + { + PVUnionArrayPtr from + = static_pointer_cast(fieldToClone); + UnionArrayConstPtr unionArray = from->getUnionArray(); + PVUnionArrayPtr to = createPVUnionArray(unionArray); + getConvert()->copyUnionArray(from, to); + return to; + } } throw std::logic_error("PVDataCreate::createPVField should never get here"); } @@ -614,6 +637,28 @@ PVStructurePtr PVDataCreate::createPVStructure( return PVStructurePtr(new PVStructure(structure)); } +PVUnionArrayPtr PVDataCreate::createPVUnionArray( + UnionArrayConstPtr const & unionArray) +{ + return PVUnionArrayPtr(new PVUnionArray(unionArray)); +} + +PVUnionPtr PVDataCreate::createPVUnion( + UnionConstPtr const & punion) +{ + return PVUnionPtr(new PVUnion(punion)); +} + +PVUnionPtr PVDataCreate::createPVVariantUnion() +{ + return PVUnionPtr(new PVUnion(getFieldCreate()->createVariantUnion())); +} + +PVUnionArrayPtr PVDataCreate::createPVVariantUnionArray() +{ + return PVUnionArrayPtr(new PVUnionArray(getFieldCreate()->createVariantUnionArray())); +} + PVStructurePtr PVDataCreate::createPVStructure( StringArray const & fieldNames,PVFieldPtrArray const & pvFields) { @@ -629,6 +674,7 @@ PVStructurePtr PVDataCreate::createPVStructure(PVStructurePtr const & structToCl { FieldConstPtrArray field; if(structToClone==0) { + // is this correct?! FieldConstPtrArray fields(0); StringArray fieldNames(0); StructureConstPtr structure = fieldCreate->createStructure(fieldNames,fields); @@ -640,6 +686,14 @@ PVStructurePtr PVDataCreate::createPVStructure(PVStructurePtr const & structToCl return pvStructure; } +PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone) +{ + PVUnionPtr punion(new PVUnion(unionToClone->getUnion())); + // set cloned value + punion->set(unionToClone->getSelectedIndex(), createPVField(unionToClone->get())); + return punion; +} + PVDataCreatePtr PVDataCreate::getPVDataCreate() { static PVDataCreatePtr pvDataCreate; diff --git a/pvDataApp/factory/PVField.cpp b/pvDataApp/factory/PVField.cpp index e9cd3b7..4477831 100644 --- a/pvDataApp/factory/PVField.cpp +++ b/pvDataApp/factory/PVField.cpp @@ -265,7 +265,9 @@ void PVField::computeOffset(const PVField * pvField) { switch(field->getType()) { case scalar: case scalarArray: - case structureArray:{ + case structureArray: + case union_: + case unionArray: { nextOffset++; pvField->fieldOffset = offset; pvField->nextFieldOffset = nextOffset; @@ -295,7 +297,9 @@ void PVField::computeOffset(const PVField * pvField,size_t offset) { switch(field->getType()) { case scalar: case scalarArray: - case structureArray: { + case structureArray: + case union_: + case unionArray: { nextOffset++; pvSubField->fieldOffset = offset; pvSubField->nextFieldOffset = nextOffset; diff --git a/pvDataApp/factory/PVStructure.cpp b/pvDataApp/factory/PVStructure.cpp index 850f6be..ed78156 100644 --- a/pvDataApp/factory/PVStructure.cpp +++ b/pvDataApp/factory/PVStructure.cpp @@ -38,6 +38,8 @@ PVDoublePtr PVStructure::nullPVDouble; PVStringPtr PVStructure::nullPVString; PVStructurePtr PVStructure::nullPVStructure; PVStructureArrayPtr PVStructure::nullPVStructureArray; +PVUnionPtr PVStructure::nullPVUnion; +PVUnionArrayPtr PVStructure::nullPVUnionArray; PVScalarArrayPtr PVStructure::nullPVScalarArray; static PVFieldPtr findSubField( @@ -520,6 +522,24 @@ PVStructurePtr PVStructure::getStructureField(String const &fieldName) return nullPVStructure; } +PVUnionPtr PVStructure::getUnionField(String const &fieldName) +{ + PVFieldPtr pvField = findSubField(fieldName,this); + if(pvField.get()==NULL) { + String message("fieldName "); + message += fieldName + " does not exist"; + this->message(message, errorMessage); + return nullPVUnion; + } + if(pvField->getField()->getType()==union_) { + return std::tr1::static_pointer_cast(pvField); + } + String message("fieldName "); + message += fieldName + " does not have type union "; + this->message(message, errorMessage); + return nullPVUnion; +} + PVScalarArrayPtr PVStructure::getScalarArrayField( String const &fieldName,ScalarType elementType) { @@ -569,6 +589,25 @@ PVStructureArrayPtr PVStructure::getStructureArrayField( return nullPVStructureArray; } +PVUnionArrayPtr PVStructure::getUnionArrayField( + String const &fieldName) +{ + PVFieldPtr pvField = findSubField(fieldName,this); + if(pvField.get()==NULL) { + String message("fieldName "); + message += fieldName + " does not exist"; + this->message(message, errorMessage); + return nullPVUnionArray; + } + if(pvField->getField()->getType()==unionArray) { + return std::tr1::static_pointer_cast(pvField); + } + String message("fieldName "); + message += fieldName + " does not have type unionArray "; + this->message(message, errorMessage); + return nullPVUnionArray; +} + String PVStructure::getExtendsStructureName() const { return extendsStructureName; diff --git a/pvDataApp/factory/PVUnion.cpp b/pvDataApp/factory/PVUnion.cpp new file mode 100644 index 0000000..4591eaa --- /dev/null +++ b/pvDataApp/factory/PVUnion.cpp @@ -0,0 +1,208 @@ +/*PVUnion.cpp*/ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mse + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::tr1::static_pointer_cast; +using std::size_t; + +namespace epics { namespace pvData { + +int32 PVUnion::UNDEFINED_INDEX = -1; +PVDataCreatePtr PVUnion::pvDataCreate = getPVDataCreate(); + + +PVUnion::PVUnion(UnionConstPtr const & unionPtr) +: PVField(unionPtr), + unionPtr(unionPtr), + selector(UNDEFINED_INDEX), + value(), + variant(unionPtr->isVariant()) +{ +} + +PVUnion::~PVUnion() +{ +} + +UnionConstPtr PVUnion::getUnion() const +{ + return unionPtr; +} + +PVFieldPtr PVUnion::get() const +{ + return value; +} + +int32 PVUnion::getSelectedIndex() const +{ + return selector; +} + +String PVUnion::getSelectedFieldName() const +{ + // no name for undefined and for variant unions + if (selector == UNDEFINED_INDEX) + return String(); + else + return unionPtr->getFieldName(selector); +} + +PVFieldPtr PVUnion::select(int32 index) +{ + // no change + if (selector == index) + return value; + + if (index == UNDEFINED_INDEX) + { + selector = UNDEFINED_INDEX; + value.reset(); + return value; + } + else if (variant) + throw std::invalid_argument("index out of bounds"); + else if (index < 0 || index > static_cast(unionPtr->getFields().size())) + throw std::invalid_argument("index out of bounds"); + + FieldConstPtr field = unionPtr->getField(index); + selector = index; + value = pvDataCreate->createPVField(field); + + return value; +} + +PVFieldPtr PVUnion::select(String const & fieldName) +{ + int32 index = variant ? -1 : unionPtr->getFieldIndex(fieldName); + if (index == -1) + throw std::invalid_argument("no such fieldName"); + + return select(index); +} + +void PVUnion::set(PVFieldPtr const & value) +{ + set(selector, value); +} + +void PVUnion::set(int32 index, PVFieldPtr const & value) +{ + if (variant && index != UNDEFINED_INDEX) + throw std::invalid_argument("index out of bounds"); + else if (!variant) + { + if (index == UNDEFINED_INDEX) + { + // for undefined index we accept only null values + if (value.get()) + throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX"); + } + else if (index < 0 || index > static_cast(unionPtr->getFields().size())) + throw std::invalid_argument("index out of bounds"); + + // value type must match + if (value->getField() != unionPtr->getField(index)) + throw std::invalid_argument("selected field and its introspection data do not match"); + } + + selector = index; + this->value = value; +} + +void PVUnion::set(String const & fieldName, PVFieldPtr const & value) +{ + int32 index = variant ? -1 : unionPtr->getFieldIndex(fieldName); + if (index == -1) + throw std::invalid_argument("no such fieldName"); + + set(index, value); +} + +void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const +{ + if (variant) + { + // write introspection data + if (value.get() == 0) + pbuffer->put((int8)-1); + else + { + pflusher->cachedSerialize(value->getField(), pbuffer); + value->serialize(pbuffer, pflusher); + } + } + else + { + // write selector value + SerializeHelper::writeSize(selector, pbuffer, pflusher); + // write value, no value for UNDEFINED_INDEX + if (selector != UNDEFINED_INDEX) + value->serialize(pbuffer, pflusher); + } +} + +void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) +{ + if (variant) + { + FieldConstPtr field = pcontrol->cachedDeserialize(pbuffer); + if (field.get()) + { + value = pvDataCreate->createPVField(field); + value->deserialize(pbuffer, pcontrol); + } + else + value.reset(); + } + else + { + selector = SerializeHelper::readSize(pbuffer, pcontrol); + if (selector != UNDEFINED_INDEX) + { + FieldConstPtr field = unionPtr->getField(selector); + value = pvDataCreate->createPVField(field); + value->deserialize(pbuffer, pcontrol); + } + else + value.reset(); + } +} + +std::ostream& PVUnion::dumpValue(std::ostream& o) const +{ + o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl; + { + format::indent_scope s(o); + PVFieldPtr fieldField = get(); + if (fieldField.get() == NULL) + o << format::indent() << "(none)" << std::endl; + else + { + Type type = fieldField->getField()->getType(); + if (type == scalar || type == scalarArray) + o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl; + else + o << *(fieldField.get()); + } + } + return o; +} + +}} diff --git a/pvDataApp/factory/PVUnionArray.cpp b/pvDataApp/factory/PVUnionArray.cpp new file mode 100644 index 0000000..44e598e --- /dev/null +++ b/pvDataApp/factory/PVUnionArray.cpp @@ -0,0 +1,212 @@ +/*PVUnionArray.cpp*/ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + */ +#include +#include +#include +#include +#include +#include +#include +#include + +using std::tr1::static_pointer_cast; +using std::size_t; + +namespace epics { namespace pvData { + +size_t PVUnionArray::append(size_t number) +{ + svector data(reuse()); + data.resize(data.size()+number); + + UnionConstPtr punion = unionArray->getUnion(); + + for(svector::reverse_iterator it = data.rbegin(); number; ++it, --number) + *it = getPVDataCreate()->createPVUnion(punion); + + size_t newLength = data.size(); + + const_svector cdata(freeze(data)); + swap(cdata); + + return newLength; +} + +bool PVUnionArray::remove(size_t offset,size_t number) +{ + if(number==0) + return true; + else if(offset+number>getLength()) + return false; + + svector vec(reuse()); + + size_t length = vec.size(); + + for(size_t i = offset; i+number < length; i++) { + vec[i].swap(vec[i + number]); + } + + vec.resize(length - number); + const_svector cdata(freeze(vec)); + swap(cdata); + + return true; +} + +void PVUnionArray::compress() { + svector vec(reuse()); // TODO: check for first NULL before realloc + + size_t length = vec.size(); + size_t newLength = 0; + + for(size_t i=0; iisCapacityMutable()) { + const_svector value; + swap(value); + if(value.capacity()isImmutable()) + THROW_EXCEPTION2(std::logic_error,"Immutable"); + const_svector value; + swap(value); + if(length == value.size()) { + // nothing + } else if(length < value.size()) { + value.slice(0, length); + } else { + svector mvalue(thaw(value)); + mvalue.resize(length); + value = freeze(mvalue); + } + swap(value); +} + +void PVUnionArray::swap(const_svector &other) +{ + if(this->isImmutable()) + THROW_EXCEPTION2(std::logic_error,"Immutable"); + + value.swap(other); +} + +void PVUnionArray::serialize(ByteBuffer *pbuffer, + SerializableControl *pflusher) const { + serialize(pbuffer, pflusher, 0, getLength()); +} + +void PVUnionArray::deserialize(ByteBuffer *pbuffer, + DeserializableControl *pcontrol) { + svector data(reuse()); + + size_t size = SerializeHelper::readSize(pbuffer, pcontrol); + data.resize(size); + + UnionConstPtr punion = unionArray->getUnion(); + + for(size_t i = 0; iensureData(1); + size_t temp = pbuffer->getByte(); + if(temp==0) { + data[i].reset(); + } + else { + if(data[i].get()==NULL) { + data[i] = getPVDataCreate()->createPVUnion(punion); + } + data[i]->deserialize(pbuffer, pcontrol); + } + } + replace(freeze(data)); // calls postPut() +} + +void PVUnionArray::serialize(ByteBuffer *pbuffer, + SerializableControl *pflusher, size_t offset, size_t count) const { + + const_svector temp(view()); + temp.slice(offset, count); + + SerializeHelper::writeSize(temp.size(), pbuffer, pflusher); + + for(size_t i = 0; igetRemaining()<1) + pflusher->flushSerializeBuffer(); + + if(temp[i].get()==NULL) { + pbuffer->putByte(0); + } + else { + pbuffer->putByte(1); + temp[i]->serialize(pbuffer, pflusher); + } + } +} + +std::ostream& PVUnionArray::dumpValue(std::ostream& o) const +{ + o << format::indent() << getUnionArray()->getID() << ' ' << getFieldName() << std::endl; + size_t length = getLength(); + if (length > 0) + { + format::indent_scope s(o); + + for (size_t i = 0; i < length; i++) + dumpValue(o, i); + } + + return o; +} + +std::ostream& PVUnionArray::dumpValue(std::ostream& o, std::size_t index) const +{ + const_svector temp(view()); + if(indexint(pvString)) + if (tint(unionArray)) THROW_EXCEPTION2(std::invalid_argument, "logic error unknown Type"); return names[t]; } diff --git a/pvDataApp/factory/printer.cpp b/pvDataApp/factory/printer.cpp index f6e3a96..5c8a68f 100644 --- a/pvDataApp/factory/printer.cpp +++ b/pvDataApp/factory/printer.cpp @@ -44,7 +44,13 @@ void PrinterBase::endStructure(const PVStructure&) {} void PrinterBase::beginStructureArray(const PVStructureArray&) {} void PrinterBase::endStructureArray(const PVStructureArray&) {} -void PrinterBase::encodeScalar(const PVScalar& pv) {} +void PrinterBase::beginUnion(const PVUnion&) {} +void PrinterBase::endUnion(const PVUnion&) {} + +void PrinterBase::beginUnionArray(const PVUnionArray&) {} +void PrinterBase::endUnionArray(const PVUnionArray&) {} + +void PrinterBase::encodeScalar(const PVScalar&) {} void PrinterBase::encodeArray(const PVScalarArray&) {} @@ -81,6 +87,14 @@ void PrinterBase::impl_print(const PVField& pv) endStructureArray(*static_cast(inprog.back())); break; + case union_: + endUnion(*static_cast(inprog.back())); + break; + + case unionArray: + endUnionArray(*static_cast(inprog.back())); + break; + default: assert(false); // oops! return; @@ -129,6 +143,35 @@ void PrinterBase::impl_print(const PVField& pv) todo.push_back(it->get()); } + todo.push_back(marker); + break; + } + case union_: { + const PVUnion &fld = *static_cast(next); + + inprog.push_back(next); + + beginUnion(fld); + PVFieldPtr val = fld.get(); + if (val.get()) // TODO print "(none)" ? + todo.push_back(val.get()); + + todo.push_back(marker); + break; + } + case unionArray: { + const PVUnionArray &fld = *static_cast(next); + PVUnionArray::const_svector vals(fld.view()); + + inprog.push_back(next); + + beginUnionArray(fld); + for(PVUnionArray::const_svector::const_iterator it=vals.begin(); + it!=vals.end(); ++it) + { + todo.push_back(it->get()); + } + todo.push_back(marker); break; } @@ -168,6 +211,25 @@ void PrinterPlain::beginStructureArray(const PVStructureArray& pv) void PrinterPlain::endStructureArray(const PVStructureArray&) {ilvl--;} +void PrinterPlain::beginUnion(const PVUnion& pv) +{ + indentN(S(), ilvl); + S() << pv.getUnion()->getID() << " " << pv.getFieldName() << std::endl; + ilvl++; +} + +void PrinterPlain::endUnion(const PVUnion&) {ilvl--;} + +void PrinterPlain::beginUnionArray(const PVUnionArray& pv) +{ + indentN(S(), ilvl); + S() << pv.getUnionArray()->getID() << " " + << pv.getFieldName() << "[] "; + ilvl++; +} + +void PrinterPlain::endUnionArray(const PVUnionArray&) {ilvl--;} + void PrinterPlain::encodeScalar(const PVScalar& pv) { indentN(S(), ilvl); diff --git a/pvDataApp/pv/convert.h b/pvDataApp/pv/convert.h index 6faead0..393df1f 100644 --- a/pvDataApp/pv/convert.h +++ b/pvDataApp/pv/convert.h @@ -30,6 +30,8 @@ bool operator==(const Scalar&, const Scalar&); bool operator==(const ScalarArray&, const ScalarArray&); bool operator==(const Structure&, const Structure&); bool operator==(const StructureArray&, const StructureArray&); +bool operator==(const Union&, const Union&); +bool operator==(const UnionArray&, const UnionArray&); static inline bool operator!=(const Field& a, const Field& b) {return !(a==b);} @@ -41,6 +43,10 @@ static inline bool operator!=(const Structure& a, const Structure& b) {return !(a==b);} static inline bool operator!=(const StructureArray& a, const StructureArray& b) {return !(a==b);} +static inline bool operator!=(const Union& a, const Union& b) +{return !(a==b);} +static inline bool operator!=(const UnionArray& a, const UnionArray& b) +{return !(a==b);} /** @@ -291,6 +297,38 @@ public: */ void copyStructureArray( PVStructureArrayPtr const & from, PVStructureArrayPtr const & to); + /** + * Are from and to valid arguments for copyUnion. + * They are only compatible if they have the same Union description. + * @param from from union. + * @param to union. + * @return (false,true) If the arguments (are not, are) compatible. + */ + bool isCopyUnionCompatible( + UnionConstPtr const & from, UnionConstPtr const & to); + /** + * Copy from a union pv to another union pv. + * NOTE: Only compatible nodes are copied. + * @param from The source. + * @param to The destination. + * @throws std::invalid_argument if the arguments are not compatible. + */ + void copyUnion(PVUnionPtr const & from, PVUnionPtr const & to); + /** + * Are from and to valid for copyUnionArray. + * @param from The from UnionArray. + * @param to The to UnionArray. + * @return (false,true) If the arguments (are not, are) compatible. + */ + bool isCopyUnionArrayCompatible( + UnionArrayConstPtr const & from, UnionArrayConstPtr const & to); + /** + * Copy from a union array to another union array. + * @param from The source array. + * @param to The destination array. + */ + void copyUnionArray( + PVUnionArrayPtr const & from, PVUnionArrayPtr const & to); /** * Convert a PV to a . * @param pv a PV diff --git a/pvDataApp/pv/printer.h b/pvDataApp/pv/printer.h index b9b60df..374960d 100644 --- a/pvDataApp/pv/printer.h +++ b/pvDataApp/pv/printer.h @@ -25,6 +25,12 @@ protected: virtual void beginStructureArray(const PVStructureArray&); virtual void endStructureArray(const PVStructureArray&); + virtual void beginUnion(const PVUnion&); + virtual void endUnion(const PVUnion&); + + virtual void beginUnionArray(const PVUnionArray&); + virtual void endUnionArray(const PVUnionArray&); + virtual void encodeScalar(const PVScalar&); virtual void encodeArray(const PVScalarArray&); virtual void encodeNull(); @@ -46,6 +52,12 @@ protected: virtual void beginStructureArray(const PVStructureArray&); virtual void endStructureArray(const PVStructureArray&); + virtual void beginUnion(const PVUnion&); + virtual void endUnion(const PVUnion&); + + virtual void beginUnionArray(const PVUnionArray&); + virtual void endUnionArray(const PVUnionArray&); + virtual void encodeScalar(const PVScalar&); virtual void encodeArray(const PVScalarArray&); virtual void encodeNull(); diff --git a/pvDataApp/pv/pvData.h b/pvDataApp/pv/pvData.h index cf24e77..01dcd2e 100644 --- a/pvDataApp/pv/pvData.h +++ b/pvDataApp/pv/pvData.h @@ -108,6 +108,9 @@ class PVScalarArray; class PVStructure; +class PVUnion; + + template class PVScalarValue; template class PVValueArray; @@ -164,6 +167,29 @@ typedef std::tr1::shared_ptr PVStructureArrayPtr; typedef std::vector PVStructureArrayPtrArray; typedef std::tr1::shared_ptr PVStructureArrayPtrArrayPtr; +/** + * typedef for a pointer to a PVUnion. + */ +typedef std::tr1::shared_ptr PVUnionPtr; +/** + * typedef for a pointer to a array of pointer to PVUnion. + */ +typedef std::vector PVUnionPtrArray; +typedef std::vector::iterator PVUnionPtrArray_iterator; +typedef std::vector::const_iterator PVUnionPtrArray_const__iterator; + +/** + * typedef for a pointer to a PVUnionArray. + */ + +typedef PVValueArray PVUnionArray; +typedef std::tr1::shared_ptr PVUnionArrayPtr; +typedef std::vector PVUnionArrayPtrArray; +typedef std::tr1::shared_ptr PVUnionArrayPtrArrayPtr; + +class PVDataCreate; +typedef std::tr1::shared_ptr PVDataCreatePtr; + /** * This class provides auxillary information about a PVField. * Each item is stored as a PVScalar. @@ -890,6 +916,12 @@ public: * @return Pointer to the field of null if a field with that name and type does not exist. */ PVStructurePtr getStructureField(String const &fieldName) ; + /** + * Get a union field with the specified name. + * @param fieldName The name of the field to get. + * @return Pointer to the field of null if a field with that name and type does not exist. + */ + PVUnionPtr getUnionField(String const &fieldName) ; /** * Get a scalarArray field with the specified name. * @param fieldName The name of the field to get. @@ -904,6 +936,12 @@ public: * @return Pointer to the field of null if a field with that name and type does not exist. */ PVStructureArrayPtr getStructureArrayField(String const &fieldName) ; + /** + * Get a unionArray field with the specified name. + * @param fieldName The name of the field to get. + * @return Pointer to the field of null if a field with that name and type does not exist. + */ + PVUnionArrayPtr getUnionArrayField(String const &fieldName) ; /** * Get the name if this structure extends another structure. * @return The string which may be null. @@ -976,6 +1014,8 @@ private: static PVStringPtr nullPVString; static PVStructurePtr nullPVStructure; static PVStructureArrayPtr nullPVStructureArray; + static PVUnionPtr nullPVUnion; + static PVUnionArrayPtr nullPVUnionArray; static PVScalarArrayPtr nullPVScalarArray; PVFieldPtrArray pvFields; @@ -984,6 +1024,142 @@ private: friend class PVDataCreate; }; + +class PVUnion : public PVField +{ +public: + POINTER_DEFINITIONS(PVUnion); + /** + * Destructor + */ + virtual ~PVUnion(); + typedef PVUnion & reference; + typedef const PVUnion & const_reference; + + /** + * Undefined index. + * Default value upon PVUnion construction. Can be set by the user. + * Corresponds to {@code null} value. + */ + static int32 UNDEFINED_INDEX; + + /** + * Get the introspection interface + * @return The interface. + */ + UnionConstPtr getUnion() const; + + /** + * Get the {@code PVField} value stored in the field. + * @return {@code PVField} value of field, {@code null} if {@code getSelectedIndex() == UNDEFINED_INDEX}. + */ + PVFieldPtr get() const; + + // TODO dynamic vs static cast? + template + std::tr1::shared_ptr get() const { + return std::tr1::static_pointer_cast(get()); + } + + /** + * Select field (set index) and get the field at the index. + * @param index index of the field to select. + * @return corresponding PVField (of undetermined value), {@code null} if {@code index == UNDEFINED_INDEX}. + * @throws {@code std::invalid_argument} if index is invalid (out of range). + */ + PVFieldPtr select(int32 index); + + // TODO dynamic vs static cast? + template + std::tr1::shared_ptr select(int32 index) { + return std::tr1::static_pointer_cast(select(index)); + } + + /** + * Select field (set index) and get the field by given name. + * @param fieldName the name of the field to select. + * @return corresponding PVField (of undetermined value). + * @throws {@code std::invalid_argument} if field does not exist. + */ + PVFieldPtr select(String const & fieldName); + + // TODO dynamic vs static cast? + template + std::tr1::shared_ptr select(String const & fieldName) { + return std::tr1::static_pointer_cast(select(fieldName)); + } + + /** + * Get selected field index. + * @return selected field index. + */ + int32 getSelectedIndex() const; + + /** + * Get selected field name. + * @return selected field name, empty string if field does not exist. + */ + String getSelectedFieldName() const; + + /** + * Set the {@code PVField} (by reference!) as selected field. + * If a value is not a valid union field an {@code std::invalid_argument} exception is thrown. + * @param value the field to set. + */ + void set(PVFieldPtr const & value); + /** + * Set the {@code PVField} (by reference!) as field at given index. + * If a value is not a valid union field an {@code std::invalid_argument} exception is thrown. + * Use {@code select(int)} to put by value. + * @param index index of a field to put. + * @param value the field to set. + * @see #select(int) + */ + void set(int32 index, PVFieldPtr const & value); + /** + * Set the {@code PVField} (by reference!) as field by given name. + * If a value is not a valid union field an {@code std::invalid_argument} exception is thrown. + * Use {@code select(String)} to put by value. + * @param fieldName Name of the field to put. + * @param value the field to set. + * @see #select(String) + */ + void set(String const & fieldName, PVFieldPtr const & value); + + /** + * Serialize. + * @param pbuffer The byte buffer. + * @param pflusher Interface to call when buffer is full. + */ + virtual void serialize( + ByteBuffer *pbuffer,SerializableControl *pflusher) const ; + /** + * Deserialize + * @param pbuffer The byte buffer. + * @param pflusher Interface to call when buffer is empty. + */ + virtual void deserialize( + ByteBuffer *pbuffer,DeserializableControl *pflusher); + /** + * Constructor + * @param punion The introspection interface. + */ + PVUnion(UnionConstPtr const & punion); + + virtual std::ostream& dumpValue(std::ostream& o) const; + +private: + friend class PVDataCreate; + static PVDataCreatePtr pvDataCreate; + + UnionConstPtr unionPtr; + + int32 selector; + PVFieldPtr value; + bool variant; +}; + + namespace detail { // adaptor to allow epics::pvData::shared_vector to hold a reference // to a shared_ptr > @@ -1301,6 +1477,105 @@ private: friend class PVDataCreate; }; + +/** + * This is provided by code that calls get. + */ +typedef PVArrayData UnionArrayData; + +/** + * Data class for a unionArray + */ +template<> +class PVValueArray : public detail::PVVectorStorage +{ + typedef detail::PVVectorStorage base_t; +public: + POINTER_DEFINITIONS(PVUnionArray); + typedef PVUnionPtr value_type; + typedef PVUnionPtr* pointer; + typedef const PVUnionPtr* const_pointer; + typedef PVArrayData ArrayDataType; + typedef std::vector vector; + typedef const std::vector const_vector; + typedef std::tr1::shared_ptr shared_vector; + typedef PVUnionArray &reference; + typedef const PVUnionArray& const_reference; + + //TODO: full namespace can be removed along with local typedef 'shared_vector' + typedef ::epics::pvData::shared_vector svector; + typedef ::epics::pvData::shared_vector const_svector; + /** + * Destructor + */ + virtual ~PVValueArray() {} + + virtual size_t getLength() const {return value.size();} + virtual size_t getCapacity() const {return value.capacity();} + + /** + * Set the array capacity. + * @param capacity The length. + */ + virtual void setCapacity(size_t capacity); + /** + * Set the array length. + * @param length The length. + */ + virtual void setLength(std::size_t length); + + /** + * Get the introspection interface + * @return The interface. + */ + UnionArrayConstPtr getUnionArray() const {return unionArray;} + /** + * Append new elements to the end of the array. + * @param number The number of elements to add. + * @return the new length of the array. + */ + virtual std::size_t append(std::size_t number); + /** + * Remove elements from the array. + * @param offset The offset of the first element to remove. + * @param number The number of elements to remove. + * @return (false,true) if the elements were removed. + */ + virtual bool remove(std::size_t offset,std::size_t number); + /** + * Compress. This removes all null elements from the array. + */ + virtual void compress(); + + virtual const_svector view() const { return value; } + virtual void swap(const_svector &other); + virtual void replace(const const_svector &other) { + value = other; + PVField::postPut(); + } + + virtual void serialize(ByteBuffer *pbuffer, + SerializableControl *pflusher) const; + virtual void deserialize(ByteBuffer *buffer, + DeserializableControl *pflusher); + virtual void serialize(ByteBuffer *pbuffer, + SerializableControl *pflusher, std::size_t offset, std::size_t count) const ; + + virtual std::ostream& dumpValue(std::ostream& o) const; + virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const; + +protected: + PVValueArray(UnionArrayConstPtr const & unionArray) + :base_t(unionArray) + ,unionArray(unionArray) + {} +private: + UnionArrayConstPtr unionArray; + const_svector value; + friend class PVDataCreate; +}; + + /** * Definitions for the various scalarArray types. */ @@ -1352,8 +1627,6 @@ typedef PVArrayData StringArrayData; typedef PVValueArray PVStringArray; typedef std::tr1::shared_ptr PVStringArrayPtr; -class PVDataCreate; -typedef std::tr1::shared_ptr PVDataCreatePtr; /** * This is a singlton class for creating data instances. */ @@ -1439,6 +1712,36 @@ public: * @return The PVStructure implementation. */ PVStructurePtr createPVStructure(PVStructurePtr const & structToClone); + /** + * Create an implementation of an array with union elements. + * @param unionArray The introspection interface. + * All elements share the same introspection interface. + * @return The PVUnionArray implementation. + */ + PVUnionArrayPtr createPVUnionArray(UnionArrayConstPtr const & unionArray); + /** + * Create implementation for PVUnion. + * @param union The introspection interface. + * @return The PVUnion implementation + */ + PVUnionPtr createPVUnion(UnionConstPtr const & punion); + /** + * Create implementation for PVUnion. + * @param unionToClone A union. Each subfield is cloned and added to the newly created union. + * @return The PVUnion implementation. + */ + PVUnionPtr createPVUnion(PVUnionPtr const & unionToClone); + /** + * Create variant union implementation. + * @return The variant PVUnion implementation. + */ + PVUnionPtr createPVVariantUnion(); + /** + * Create variant union array implementation. + * @return The variant PVUnionArray implementation. + */ + PVUnionArrayPtr createPVVariantUnionArray(); + private: PVDataCreate(); FieldCreatePtr fieldCreate; diff --git a/testApp/misc/testSerialization.cpp b/testApp/misc/testSerialization.cpp index 3549b5a..1fc6b86 100644 --- a/testApp/misc/testSerialization.cpp +++ b/testApp/misc/testSerialization.cpp @@ -140,7 +140,7 @@ void serializationTest(PVFieldPtr const & field) { } void testEquals() { - testDiag("Testing equals..."); + testDiag("Testing equals..."); // and non-initialized PVDataCreatePtr factory = getPVDataCreate(); testOk1(factory.get()!=NULL); @@ -167,6 +167,59 @@ void testEquals() { PVStructureArrayPtr structureArray1 = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure1->getStructure())); PVStructureArrayPtr structureArray2 = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure2->getStructure())); testOk1((*structureArray1)==(*structureArray2)); + + // variant union + PVUnionPtr variantUnion1 = factory->createPVVariantUnion(); + PVUnionPtr variantUnion2 = factory->createPVVariantUnion(); + testOk1((*variantUnion1)==(*variantUnion2)); + + variantUnion1->set(structure1); + variantUnion2->set(structure1); + testOk1((*variantUnion1)==(*variantUnion2)); + + variantUnion2->set(structureArray1); + testOk1((*variantUnion1)!=(*variantUnion2)); + + // variant union array + PVUnionArrayPtr variantUnionArray1 = factory->createPVVariantUnionArray(); + PVUnionArrayPtr variantUnionArray2 = factory->createPVVariantUnionArray(); + testOk1((*variantUnionArray1)==(*variantUnionArray2)); + + // union + UnionConstPtr punion = getFieldCreate()->createFieldBuilder()-> + add("double", pvDouble)-> + add("double2", pvDouble)-> + addStructureArray("nested")-> + setId("nestedId")-> + add("short", pvShort)-> + add("long", pvLong)-> + createNested()-> + addArray("intArray", pvInt)-> + createUnion(); + PVUnionPtr union1 = factory->createPVUnion(punion); + PVUnionPtr union2 = factory->createPVUnion(punion); + testOk1((*union1)==(*union2)); + + union1->select("double")->put(1.2); + union2->select("double")->put(1.2); + testOk1((*union1)==(*union2)); + + union2->select("double")->put(2.2); + testOk1((*union1)!=(*union2)); + + union2->select("double2")->put(1.2); + testOk1((*union1)!=(*union2)); + + union2->select("nested"); + testOk1((*union1)!=(*union2)); + + testOk1((*union1)!=(*variantUnion2)); + + PVUnionArrayPtr unionArray1 = factory->createPVUnionArray(getFieldCreate()->createUnionArray(punion)); + PVUnionArrayPtr unionArray2 = factory->createPVUnionArray(getFieldCreate()->createUnionArray(punion)); + testOk1((*unionArray1)==(*unionArray2)); + + testOk1((*variantUnionArray1)!=(*unionArray2)); } template @@ -322,32 +375,6 @@ void testArray() { testArrayType(sdata, NELEMENTS(sdata)); } -void testNonInitialized() { - testDiag("Testing non-initialized..."); - PVDataCreatePtr factory = getPVDataCreate(); - testOk1(factory.get()!=NULL); - - // be sure all is covered - for (int i = pvBoolean; i < pvString; i++) - { - ScalarType scalarType = static_cast(i); - - PVScalarPtr scalar = factory->createPVScalar(scalarType); - serializationTest(scalar); - - PVScalarArrayPtr array = factory->createPVScalarArray(scalarType); - serializationTest(array); - } - - // and a structure - PVStructurePtr structure = factory->createPVStructure(getStandardField()->timeStamp()); - serializationTest(structure); - - // and a structure array - PVStructureArrayPtr structureArray = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure->getStructure())); - serializationTest(structureArray); -} - void testStructure() { testDiag("Testing structure..."); @@ -371,6 +398,196 @@ void testStructure() { serializationTest(pvStructure); } +template +std::tr1::shared_ptr createPVScalar() +{ + return std::tr1::static_pointer_cast( + getPVDataCreate()->createPVScalar(PVT::typeCode) + ); +} + +template +std::tr1::shared_ptr createPVScalarArray() +{ + return std::tr1::static_pointer_cast( + getPVDataCreate()->createPVScalarArray(PVAT::typeCode) + ); +} + +void testUnion() { + testDiag("Testing union..."); + + PVDataCreatePtr factory = getPVDataCreate(); + testOk1(factory.get()!=NULL); + + + PVDoublePtr doubleValue = createPVScalar(); + PVIntPtr intValue = createPVScalar(); + + testDiag("\tVariant union test"); + + PVUnionPtr variant = factory->createPVVariantUnion(); + testOk1(variant.get()!=NULL); + testOk1(PVUnion::UNDEFINED_INDEX == variant->getSelectedIndex()); + testOk1("" == variant->getSelectedFieldName()); + serializationTest(variant); + + variant->set(doubleValue); + testOk1(doubleValue.get() == variant->get().get()); + testOk1(PVUnion::UNDEFINED_INDEX == variant->getSelectedIndex()); + testOk1("" == variant->getSelectedFieldName()); + serializationTest(variant); + + variant->set(intValue); + testOk1(intValue.get() == variant->get().get()); + testOk1(PVUnion::UNDEFINED_INDEX == variant->getSelectedIndex()); + testOk1("" == variant->getSelectedFieldName()); + serializationTest(variant); + + variant->set(PVUnion::UNDEFINED_INDEX, doubleValue); + testOk1(doubleValue.get() == variant->get().get()); + testOk1(PVUnion::UNDEFINED_INDEX == variant->getSelectedIndex()); + + variant->set(PVFieldPtr()); + testOk1(NULL == variant->get().get()); + + testDiag("\tVariant union array test"); + + PVUnionArrayPtr variantArray = factory->createPVVariantUnionArray(); + testOk1(variantArray.get()!=NULL); + + variantArray->setLength(6); + PVUnionArray::svector data; + + PVUnionPtr u = factory->createPVVariantUnion(); + data.push_back(u); + + u = factory->createPVVariantUnion(); + u->set(factory->createPVStructure(getStandardField()->timeStamp())); + data.push_back(u); + + u = factory->createPVVariantUnion(); + u->set(factory->createPVStructure(getStandardField()->control())); + data.push_back(u); + + data.push_back(PVUnionPtr()); + + variantArray->replace(freeze(data)); + serializationTest(variantArray); + + testDiag("\tVariant union test"); + + UnionConstPtr punion = getFieldCreate()->createFieldBuilder()-> + add("doubleValue", pvDouble)-> + add("intValue", pvInt)-> + createUnion(); + + u = factory->createPVUnion(punion); + testOk1(NULL!=u.get()); + + // null union test + testOk1(NULL==u->get().get()); + testOk1(PVUnion::UNDEFINED_INDEX == u->getSelectedIndex()); + testOk1("" == u->getSelectedFieldName()); + serializationTest(u); + + u->select("doubleValue")->put(12); + testOk1(12 == u->get()->get()); + testOk1(0 == u->getSelectedIndex()); + testOk1("doubleValue" == u->getSelectedFieldName()); + serializationTest(u); + + u->select("intValue")->put(543); + testOk1(543 == u->get()->get()); + testOk1(1 == u->getSelectedIndex()); + testOk1("intValue" == u->getSelectedFieldName()); + serializationTest(u); + + u->select(1)->put(5432); + testOk1(5432 == u->get()->get()); + serializationTest(u); + + testOk1(NULL==u->select(PVUnion::UNDEFINED_INDEX).get()); + testOk1(NULL==u->get().get()); + testOk1(PVUnion::UNDEFINED_INDEX == u->getSelectedIndex()); + testOk1("" == u->getSelectedFieldName()); + serializationTest(u); + + u->set("doubleValue", doubleValue); + testOk1(doubleValue.get() == u->get().get()); + testOk1(0 == u->getSelectedIndex()); + serializationTest(u); + + try + { + u->set(1, doubleValue); + testFail("field type does not match, but set allowed"); + } + catch (std::invalid_argument& ia) + { + // expected + testPass("PVUnion.set(int32, PVFieldPtr const&) field type does not match test"); + } + + try + { + u->select(120); + testFail("index out of bounds allowed"); + } + catch (std::invalid_argument& ia) + { + // expected + testPass("PVUnion.select(int32) index out of bounds test"); + } + + try + { + u->select(-2); + testFail("index out of bounds allowed"); + } + catch (std::invalid_argument& ia) + { + // expected + testPass("PVUnion.select(int32) index out of bounds test"); + } + + try + { + u->set(120, doubleValue); + testFail("index out of bounds allowed"); + } + catch (std::invalid_argument& ia) + { + // expected + testPass("PVUnion.set(int32, PVFieldPtr const&) index out of bounds test"); + } + + testDiag("\tUnion array test"); + + PVUnionArrayPtr unionArray = factory->createPVUnionArray(getFieldCreate()->createUnionArray(punion)); + testOk1(unionArray.get()!=NULL); + + unionArray->setLength(6); + data.clear(); + + u = factory->createPVUnion(punion); + data.push_back(u); + + u = factory->createPVUnion(punion); + u->select(0)->put(12); + data.push_back(u); + + u = factory->createPVUnion(punion); + u->select(1)->put(421); + data.push_back(u); + + data.push_back(PVUnionPtr()); + + unionArray->replace(freeze(data)); + serializationTest(unionArray); + +} + void testStructureArray() { testDiag("Testing structure array..."); @@ -461,7 +678,7 @@ void testIntrospectionSerialization() ScalarArrayConstPtr array = factory->createScalarArray(scalarType); serializatioTest(array); - } + } // and a structure StructureConstPtr structure = getStandardField()->timeStamp(); @@ -470,7 +687,31 @@ void testIntrospectionSerialization() // and a structure array StructureArrayConstPtr structureArray = factory->createStructureArray(structure); serializatioTest(structureArray); - } + + // variant union + UnionConstPtr variant = factory->createVariantUnion(); + serializatioTest(variant); + + // variant array union + UnionArrayConstPtr variantArray = factory->createVariantUnionArray(); + serializatioTest(variantArray); + + // union + UnionConstPtr punion = factory->createFieldBuilder()-> + add("double", pvDouble)-> + addStructureArray("nested")-> + setId("nestedId")-> + add("short", pvShort)-> + add("long", pvLong)-> + createNested()-> + addArray("intArray", pvInt)-> + createUnion(); + serializatioTest(punion); + + // union array + UnionArrayConstPtr punionArray = factory->createUnionArray(punion); + serializatioTest(punionArray); +} void testStringCopy() { String s1 = "abc"; @@ -483,7 +724,7 @@ void testStringCopy() { MAIN(testSerialization) { - testPlan(175); + testPlan(213); flusher = new SerializableControlImpl(); control = new DeserializableControlImpl(); @@ -493,12 +734,13 @@ MAIN(testSerialization) { testIntrospectionSerialization(); testEquals(); - testNonInitialized(); testScalar(); testArray(); testStructure(); testStructureArray(); + + testUnion(); delete buffer;