Files
pvData/src/factory/PVUnion.cpp

253 lines
6.8 KiB
C++

/*PVUnion.cpp*/
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#include <cstddef>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <vector>
#define epicsExportSharedSymbols
#include <pv/pvData.h>
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
namespace epics { namespace pvData {
#define PVUNION_UNDEFINED_INDEX -1
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
PVUnion::PVUnion(UnionConstPtr const & unionPtr)
: PVField(unionPtr),
unionPtr(unionPtr),
selector(PVUNION_UNDEFINED_INDEX), // to allow out-of-order static initialization
value(),
variant(unionPtr->isVariant())
{
}
#undef PVUNION_UNDEFINED_INDEX
PVUnion::~PVUnion() {}
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)
{
if (variant && index != UNDEFINED_INDEX)
throw std::invalid_argument("index out of bounds");
// no change
if (selector == index && !variant)
return value;
if (index == UNDEFINED_INDEX)
{
selector = UNDEFINED_INDEX;
value.reset();
return value;
}
else if (index < 0 || size_t(index) >= 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 : static_cast<int32>(unionPtr->getFieldIndex(fieldName));
if (index == -1)
throw std::invalid_argument("no such fieldName");
return select(index);
}
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)
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
throw std::invalid_argument("index out of bounds");
else if (!value)
throw std::invalid_argument("Can't set defined index w/ NULL");
else if (value->getField() != unionPtr->getField(index))
throw std::invalid_argument("selected field and its introspection data do not match");
}
selector = index;
this->value = value;
postPut();
}
void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
{
int32 index = variant ? -1 : static_cast<int32>(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) {
pflusher->ensureBuffer(1);
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())
{
// try to reuse existing field instance
if (!value.get() || *value->getField() != *field)
value = pvDataCreate->createPVField(field);
value->deserialize(pbuffer, pcontrol);
}
else
value.reset();
}
else
{
int32 previousSelector = selector;
selector = static_cast<int32>(SerializeHelper::readSize(pbuffer, pcontrol));
if (selector != UNDEFINED_INDEX)
{
if (selector != previousSelector)
{
FieldConstPtr field = unionPtr->getField(selector);
// try to reuse existing field instance
if (!value.get() || *value->getField() != *field)
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);
const PVField::const_shared_pointer& 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;
}
void PVUnion::copy(const PVUnion& from)
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
if(*getUnion() != *from.getUnion())
throw std::invalid_argument("union definitions do not match");
copyUnchecked(from);
}
void PVUnion::copyUnchecked(const PVUnion& from)
{
const PVField::const_shared_pointer& fromValue = from.get();
if (from.getUnion()->isVariant())
{
if (fromValue.get() == 0)
{
set(PVField::shared_pointer());
}
else
{
PVFieldPtr toValue = get();
if (toValue.get() == 0 || *toValue->getField() != *fromValue->getField())
{
toValue = pvDataCreate->createPVField(fromValue->getField());
toValue->copyUnchecked(*fromValue);
set(toValue);
}
else
{
toValue->copyUnchecked(*fromValue);
postPut();
}
}
}
else
{
if (fromValue.get() == 0)
{
select(PVUnion::UNDEFINED_INDEX);
}
else
{
select(from.getSelectedIndex())->copyUnchecked(*fromValue);
}
postPut();
}
}
}}