/*FieldCreateFactory.cpp*/ /* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ /** * @author mrk */ #include #include #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include #include #include #include #include #include #include using std::tr1::static_pointer_cast; using std::size_t; using std::string; namespace epics { namespace pvData { size_t Field::num_instances; struct Field::Helper { static unsigned hash(Field *fld) { std::ostringstream key; // hash the output of operator<<() // not efficient, but stable within this process. key<<(*fld); unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1); fld->m_hash = H; return H; } }; struct FieldCreate::Helper { template static void cache(const FieldCreate *create, std::tr1::shared_ptr& ent) { unsigned hash = Field::Helper::hash(ent.get()); Lock G(create->mutex); // we examine raw pointers stored in create->cache, which is safe under create->mutex std::pair itp(create->cache.equal_range(hash)); for(; itp.first!=itp.second; ++itp.first) { Field* cent(itp.first->second); FLD* centx(dynamic_cast(cent)); if(centx && compare(*centx, *ent)) { try{ ent = std::tr1::static_pointer_cast(cent->shared_from_this()); return; }catch(std::tr1::bad_weak_ptr&){ // we're racing destruction. // add a new entry. // Field::~Field is in the process of removing this old one. continue; } } } create->cache.insert(std::make_pair(hash, ent.get())); // cache cleaned from Field::~Field } }; Field::Field(Type type) : m_fieldType(type) , m_hash(0) { REFTRACE_INCREMENT(num_instances); } Field::~Field() { REFTRACE_DECREMENT(num_instances); } void Field::cacheCleanup() { const FieldCreatePtr& create(getFieldCreate()); Lock G(create->mutex); std::pair itp(create->cache.equal_range(m_hash)); for(; itp.first!=itp.second; ++itp.first) { Field* cent(itp.first->second); if(cent==this) { create->cache.erase(itp.first); return; } } } std::tr1::shared_ptr Field::build() const { FieldConstPtr self(shared_from_this()); return getPVDataCreate()->createPVField(self); } std::ostream& operator<<(std::ostream& o, const Field& f) { return f.dump(o); }; Scalar::Scalar(ScalarType scalarType) : Field(scalar),scalarType(scalarType) { if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) THROW_EXCEPTION2(std::invalid_argument, "Can't construct Scalar from invalid ScalarType"); } Scalar::~Scalar() { cacheCleanup(); } std::ostream& Scalar::dump(std::ostream& o) const { return o << format::indent() << getID(); } string Scalar::getID() const { static const string idScalarLUT[] = { "boolean", // pvBoolean "byte", // pvByte "short", // pvShort "int", // pvInt "long", // pvLong "ubyte", // pvUByte "ushort", // pvUShort "uint", // pvUInt "ulong", // pvULong "float", // pvFloat "double", // pvDouble "string" // pvString }; return idScalarLUT[scalarType]; } int8 Scalar::getTypeCodeLUT(ScalarType scalarType) { static const int8 typeCodeLUT[] = { 0x00, // pvBoolean 0x20, // pvByte 0x21, // pvShort 0x22, // pvInt 0x23, // pvLong 0x24, // pvUByte 0x25, // pvUShort 0x26, // pvUInt 0x27, // pvULong 0x42, // pvFloat 0x43, // pvDouble 0x60 // pvString }; return typeCodeLUT[scalarType]; } void Scalar::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte(getTypeCodeLUT(scalarType)); } void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { // must be done via FieldCreate throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr Scalar::build() const { return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast(shared_from_this())); } std::string BoundedString::getID() const { std::ostringstream id; id << Scalar::getID() << '(' << maxLength << ')'; return id.str(); } void BoundedString::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte(0x83); SerializeHelper::writeSize(maxLength, buffer, control); } std::size_t BoundedString::getMaximumLength() const { return maxLength; } BoundedString::BoundedString(std::size_t maxStringLength) : Scalar(pvString), maxLength(maxStringLength) { if (maxLength == 0) THROW_EXCEPTION2(std::invalid_argument, "maxLength == 0"); } BoundedString::~BoundedString() { cacheCleanup(); } static void serializeStructureField(const Structure* structure, ByteBuffer* buffer, SerializableControl* control) { // to optimize default (non-empty) IDs optimization // empty IDs are not allowed string id = structure->getID(); if (id == Structure::DEFAULT_ID) // TODO slow comparison SerializeHelper::serializeString(string(), buffer, control); else SerializeHelper::serializeString(id, buffer, control); FieldConstPtrArray const & fields = structure->getFields(); StringArray const & fieldNames = structure->getFieldNames(); std::size_t len = fields.size(); SerializeHelper::writeSize(len, buffer, control); for (std::size_t i = 0; i < len; i++) { SerializeHelper::serializeString(fieldNames[i], buffer, control); control->cachedSerialize(fields[i], buffer); } } static StructureConstPtr deserializeStructureField(const FieldCreate* fieldCreate, ByteBuffer* buffer, DeserializableControl* control) { string id = SerializeHelper::deserializeString(buffer, control); const std::size_t size = SerializeHelper::readSize(buffer, control); FieldConstPtrArray fields; fields.reserve(size); StringArray fieldNames; fieldNames.reserve(size); for (std::size_t i = 0; i < size; i++) { fieldNames.push_back(SerializeHelper::deserializeString(buffer, control)); fields.push_back(control->cachedDeserialize(buffer)); } if (id.empty()) return fieldCreate->createStructure(fieldNames, fields); else return fieldCreate->createStructure(id, fieldNames, fields); } static void serializeUnionField(const Union* punion, ByteBuffer* buffer, SerializableControl* control) { // to optimize default (non-empty) IDs optimization // empty IDs are not allowed string id = punion->getID(); if (id == Union::DEFAULT_ID) // TODO slow comparison SerializeHelper::serializeString(string(), buffer, control); else SerializeHelper::serializeString(id, buffer, control); FieldConstPtrArray const & fields = punion->getFields(); StringArray const & fieldNames = punion->getFieldNames(); std::size_t len = fields.size(); SerializeHelper::writeSize(len, buffer, control); for (std::size_t i = 0; i < len; i++) { SerializeHelper::serializeString(fieldNames[i], buffer, control); control->cachedSerialize(fields[i], buffer); } } static UnionConstPtr deserializeUnionField(const FieldCreate* fieldCreate, ByteBuffer* buffer, DeserializableControl* control) { string id = SerializeHelper::deserializeString(buffer, control); const std::size_t size = SerializeHelper::readSize(buffer, control); FieldConstPtrArray fields; fields.reserve(size); StringArray fieldNames; fieldNames.reserve(size); for (std::size_t i = 0; i < size; i++) { fieldNames.push_back(SerializeHelper::deserializeString(buffer, control)); fields.push_back(control->cachedDeserialize(buffer)); } if (id.empty()) return fieldCreate->createUnion(fieldNames, fields); else return fieldCreate->createUnion(id, fieldNames, fields); } Array::Array(Type type) : Field(type) { } Array::~Array() {} ScalarArray::ScalarArray(ScalarType elementType) : Array(scalarArray), elementType(elementType) { if(elementType<0 || elementType>MAX_SCALAR_TYPE) throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType"); } ScalarArray::~ScalarArray() { cacheCleanup(); } const string ScalarArray::getIDScalarArrayLUT() const { static const string idScalarArrayLUT[] = { "boolean[]", // pvBoolean "byte[]", // pvByte "short[]", // pvShort "int[]", // pvInt "long[]", // pvLong "ubyte[]", // pvUByte "ushort[]", // pvUShort "uint[]", // pvUInt "ulong[]", // pvULong "float[]", // pvFloat "double[]", // pvDouble "string[]" // pvString }; return idScalarArrayLUT[elementType]; } string ScalarArray::getID() const { return getIDScalarArrayLUT(); } std::ostream& ScalarArray::dump(std::ostream& o) const { return o << format::indent() << getID(); } void ScalarArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte((int8)0x08 | Scalar::getTypeCodeLUT(elementType)); } void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr ScalarArray::build() const { return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast(shared_from_this())); } BoundedScalarArray::~BoundedScalarArray() { cacheCleanup(); } BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size) : ScalarArray(elementType), size(size) { } string BoundedScalarArray::getID() const { char buffer[32]; sprintf(buffer, "%s<%zu>", ScalarTypeFunc::name(getElementType()), size); return string(buffer); } void BoundedScalarArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte((int8)0x10 | Scalar::getTypeCodeLUT(getElementType())); SerializeHelper::writeSize(size, buffer, control); } FixedScalarArray::~FixedScalarArray() { cacheCleanup(); } FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size) : ScalarArray(elementType), size(size) { } string FixedScalarArray::getID() const { char buffer[32]; sprintf(buffer, "%s[%zu]", ScalarTypeFunc::name(getElementType()), size); return string(buffer); } void FixedScalarArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte((int8)0x18 | Scalar::getTypeCodeLUT(getElementType())); SerializeHelper::writeSize(size, buffer, control); } StructureArray::StructureArray(StructureConstPtr const & structure) : Array(structureArray),pstructure(structure) { } StructureArray::~StructureArray() { cacheCleanup(); } string StructureArray::getID() const { return pstructure->getID() + "[]"; } std::ostream& StructureArray::dump(std::ostream& o) const { o << format::indent() << getID() << std::endl; { format::indent_scope s(o); o << *pstructure; } return o; } void StructureArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte((int8)0x88); control->cachedSerialize(pstructure, buffer); } void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr > > StructureArray::build() const { return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast(shared_from_this())); } UnionArray::UnionArray(UnionConstPtr const & _punion) : Array(unionArray),punion(_punion) { } UnionArray::~UnionArray() { cacheCleanup(); } string UnionArray::getID() const { return punion->getID() + "[]"; } std::ostream& UnionArray::dump(std::ostream& o) const { o << format::indent() << getID() << std::endl; { format::indent_scope s(o); o << *punion; } return o; } void UnionArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); if (punion->isVariant()) { // unrestricted/variant union buffer->putByte((int8)0x8A); } else { buffer->putByte((int8)0x89); control->cachedSerialize(punion, buffer); } } void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr > > UnionArray::build() const { return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast(shared_from_this())); } const string Structure::DEFAULT_ID = Structure::defaultId(); const string & Structure::defaultId() { static const string id = "structure"; return id; } Structure::Structure ( StringArray const & fieldNames, FieldConstPtrArray const & infields, string const & inid) : Field(structure), fieldNames(fieldNames), fields(infields), id(inid) { if(inid.empty()) { THROW_EXCEPTION2(std::invalid_argument, "Can't construct Structure, id is empty string"); } if(fieldNames.size()!=fields.size()) { THROW_EXCEPTION2(std::invalid_argument, "Can't construct Structure, fieldNames.size()!=fields.size()"); } size_t number = fields.size(); for(size_t i=0; igetID() << ' ' << fieldNames[i] << std::endl; switch(pfield->getType()) { case scalar: case scalarArray: break; case structure: { Field const *xxx = pfield.get(); Structure const *pstruct = static_cast(xxx); format::indent_scope s(o); pstruct->dumpFields(o); break; } case structureArray: { format::indent_scope s(o); Field const *xxx = pfield.get(); StructureArray const *pstructureArray = static_cast(xxx); o << *pstructureArray->getStructure(); break; } case union_: { Field const *xxx = pfield.get(); Union const *punion = static_cast(xxx); format::indent_scope s(o); punion->dumpFields(o); break; } case unionArray: { format::indent_scope s(o); Field const *xxx = pfield.get(); UnionArray const *punionArray = static_cast(xxx); o << *punionArray->getUnion(); break; } } } } void Structure::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); buffer->putByte((int8)0x80); serializeStructureField(this, buffer, control); } void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr Structure::build() const { return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast(shared_from_this())); } const string Union::DEFAULT_ID = Union::defaultId(); const string & Union::defaultId() { static const string id = "union"; return id; } const string Union::ANY_ID = Union::anyId(); const string & Union::anyId() { static const string id = "any"; return id; } Union::Union () : Field(union_), fieldNames(), fields(), id(anyId()) { } Union::Union ( StringArray const & fieldNames, FieldConstPtrArray const & infields, string const & inid) : Field(union_), fieldNames(fieldNames), fields(infields), id(inid) { if(inid.empty()) { THROW_EXCEPTION2(std::invalid_argument, "Can't construct Union, id is empty string"); } if(fieldNames.size()!=fields.size()) { THROW_EXCEPTION2(std::invalid_argument, "Can't construct Union, fieldNames.size()!=fields.size()"); } if(fields.size()==0 && inid!=ANY_ID) { THROW_EXCEPTION2(std::invalid_argument, "Can't construct Union, no fields only allowed when id = " + ANY_ID); } size_t number = fields.size(); for(size_t i=0; igetType()!=t) continue; ScalarType type; switch(fields[i]->getType()) { case scalar: type = static_cast(fields[i].get())->getScalarType(); break; case scalarArray: type = static_cast(fields[i].get())->getElementType(); break; default: continue; } if(type==s) { // exact match ret = i; break; // we're done } else if(ret==-1) { // first partial match ret = i; } } return ret; } string Union::getID() const { return id; } FieldConstPtr Union::getField(string const & fieldName) const { size_t numberFields = fields.size(); for(size_t i=0; igetID() << ' ' << fieldNames[i] << std::endl; switch(pfield->getType()) { case scalar: case scalarArray: break; case structure: { Field const *xxx = pfield.get(); Structure const *pstruct = static_cast(xxx); format::indent_scope s(o); pstruct->dumpFields(o); break; } case structureArray: { format::indent_scope s(o); o << *pfield; break; } case union_: { Field const *xxx = pfield.get(); Union const *punion = static_cast(xxx); format::indent_scope s(o); punion->dumpFields(o); break; } case unionArray: { format::indent_scope s(o); o << *pfield; break; } } } } void Union::serialize(ByteBuffer *buffer, SerializableControl *control) const { control->ensureBuffer(1); if (fields.size() == 0) { // unrestricted/variant union buffer->putByte((int8)0x82); } else { buffer->putByte((int8)0x81); serializeUnionField(this, buffer, control); } } void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control*/) { throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } std::tr1::shared_ptr Union::build() const { return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast(shared_from_this())); } FieldBuilder::FieldBuilder() :fieldCreate(getFieldCreate()) ,idSet(false) ,nestedClassToBuild(structure) ,nestedArray(false) ,createNested(true) {} FieldBuilder::FieldBuilder(const Structure* S) :fieldCreate(getFieldCreate()) ,id(S->getID()) ,idSet(!id.empty()) ,fieldNames(S->getFieldNames()) ,fields(S->getFields()) ,parentBuilder() ,nestedClassToBuild(structure) ,nestedName() ,nestedArray(false) ,createNested(false) {} FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const Structure* S) :fieldCreate(_parentBuilder->fieldCreate) ,id(S->getID()) ,idSet(!id.empty()) ,fieldNames(S->getFieldNames()) ,fields(S->getFields()) ,parentBuilder(_parentBuilder) ,nestedClassToBuild(structure) ,nestedName(name) ,nestedArray(false) ,createNested(false) {} FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const StructureArray* S) :fieldCreate(getFieldCreate()) ,id(S->getStructure()->getID()) ,idSet(!id.empty()) ,fieldNames(S->getStructure()->getFieldNames()) ,fields(S->getStructure()->getFields()) ,parentBuilder(_parentBuilder) ,nestedClassToBuild(structure) ,nestedName(name) ,nestedArray(true) ,createNested(false) {} FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const Union* S) :fieldCreate(getFieldCreate()) ,id(S->getID()) ,idSet(!id.empty()) ,fieldNames(S->getFieldNames()) ,fields(S->getFields()) ,parentBuilder(_parentBuilder) ,nestedClassToBuild(union_) ,nestedName(name) ,nestedArray(false) ,createNested(false) {} FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const UnionArray* S) :fieldCreate(getFieldCreate()) ,id(S->getUnion()->getID()) ,idSet(!id.empty()) ,fieldNames(S->getUnion()->getFieldNames()) ,fields(S->getUnion()->getFields()) ,parentBuilder(_parentBuilder) ,nestedClassToBuild(union_) ,nestedName(name) ,nestedArray(true) ,createNested(false) {} FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder, string const & _nestedName, Type _nestedClassToBuild, bool _nestedArray) :fieldCreate(_parentBuilder->fieldCreate) ,idSet(false) ,parentBuilder(_parentBuilder) ,nestedClassToBuild(_nestedClassToBuild) ,nestedName(_nestedName) ,nestedArray(_nestedArray) ,createNested(true) {} void FieldBuilder::reset() { id.erase(); idSet = false; fieldNames.clear(); fields.clear(); } FieldBuilderPtr FieldBuilder::begin() { FieldBuilderPtr ret(new FieldBuilder); return ret; } FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S) { FieldBuilderPtr ret(new FieldBuilder(S.get())); return ret; } FieldBuilderPtr FieldBuilder::setId(string const & id) { this->id = id; idSet = true; return shared_from_this(); } FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType) { return add(name, fieldCreate->createScalar(scalarType)); } FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength) { return add(name, fieldCreate->createBoundedString(maxLength)); } FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field) { const Field* cur = findField(name, field->getType()); if(!cur) { fields.push_back(field); fieldNames.push_back(name); } else if(*cur!=*field) { THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name); } // else exact duplicate is silently ignored return shared_from_this(); } FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType) { return add(name, fieldCreate->createScalarArray(scalarType)); } FieldBuilderPtr FieldBuilder::addFixedArray(string const & name, ScalarType scalarType, size_t size) { return add(name, fieldCreate->createFixedScalarArray(scalarType, size)); } FieldBuilderPtr FieldBuilder::addBoundedArray(string const & name, ScalarType scalarType, size_t size) { return add(name, fieldCreate->createBoundedScalarArray(scalarType, size)); } FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const & element) { FieldConstPtr fld; switch (element->getType()) { case structure: fld = fieldCreate->createStructureArray(static_pointer_cast(element)); break; case union_: fld = fieldCreate->createUnionArray(static_pointer_cast(element)); break; case scalar: if (std::tr1::dynamic_pointer_cast(element).get()) THROW_EXCEPTION2(std::invalid_argument, "bounded string arrays are not supported"); fld = fieldCreate->createScalarArray(static_pointer_cast(element)->getScalarType()); break; // scalarArray? default: std::ostringstream msg("unsupported array element type: "); msg << element->getType(); THROW_EXCEPTION2(std::invalid_argument, msg.str()); } return add(name, fld); } FieldConstPtr FieldBuilder::createFieldInternal(Type type) { // minor optimization if (fieldNames.size() == 0 && type == union_) return fieldCreate->createVariantUnion(); if (type == structure) { return (idSet) ? fieldCreate->createStructure(id, fieldNames, fields) : fieldCreate->createStructure(fieldNames, fields); } else if (type == union_) { return (idSet) ? fieldCreate->createUnion(id, fieldNames, fields) : fieldCreate->createUnion(fieldNames, fields); } else { std::ostringstream msg("unsupported type: "); msg << type; THROW_EXCEPTION2(std::invalid_argument, msg.str()); } } StructureConstPtr FieldBuilder::createStructure() { if (parentBuilder.get()) THROW_EXCEPTION2(std::runtime_error, "createStructure() called in nested FieldBuilder"); StructureConstPtr field(static_pointer_cast(createFieldInternal(structure))); reset(); return field; } UnionConstPtr FieldBuilder::createUnion() { if (parentBuilder.get()) THROW_EXCEPTION2(std::runtime_error, "createUnion() called in nested FieldBuilder"); UnionConstPtr field(static_pointer_cast(createFieldInternal(union_))); reset(); return field; } const Field* FieldBuilder::findField(const std::string& name, Type ftype) { // linear search on the theory that the number of fields is small for(size_t i=0; igetType()!=ftype) THROW_EXCEPTION2(std::invalid_argument, "nested field not required type: "+name); return fields[i].get(); } return 0; } FieldBuilderPtr FieldBuilder::addNestedStructure(string const & name) { const Field *cur = findField(name, structure); if(cur) { return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, static_cast(cur))); } return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false)); } FieldBuilderPtr FieldBuilder::addNestedUnion(string const & name) { const Field *cur = findField(name, union_); if(cur) { return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, static_cast(cur))); } return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, false)); } FieldBuilderPtr FieldBuilder::addNestedStructureArray(string const & name) { const Field *cur = findField(name, structureArray); if(cur) { return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, static_cast(cur))); } return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true)); } FieldBuilderPtr FieldBuilder::addNestedUnionArray(string const & name) { const Field *cur = findField(name, unionArray); if(cur) { return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, static_cast(cur))); } return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true)); } FieldBuilderPtr FieldBuilder::endNested() { if (!parentBuilder) THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields"); FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild); if(createNested) { if (nestedArray) parentBuilder->addArray(nestedName, nestedField); else parentBuilder->add(nestedName, nestedField); return parentBuilder; } else { for(size_t i=0, N = parentBuilder->fieldNames.size(); ifieldNames[i]) continue; if(nestedArray) { if(nestedClassToBuild==structure) parentBuilder->fields[i] = fieldCreate->createStructureArray(std::tr1::static_pointer_cast(nestedField)); else if(nestedClassToBuild==union_) parentBuilder->fields[i] = fieldCreate->createUnionArray(std::tr1::static_pointer_cast(nestedField)); else throw std::logic_error("bad nested class"); } else { parentBuilder->fields[i] = nestedField; } return parentBuilder; } // this only reached if bug in ctor THROW_EXCEPTION2(std::logic_error, "no nested field field?"); } } FieldBuilderPtr FieldCreate::createFieldBuilder() const { return FieldBuilderPtr(new FieldBuilder()); } FieldBuilderPtr FieldCreate::createFieldBuilder(StructureConstPtr S) const { FieldBuilderPtr ret(new FieldBuilder(S.get())); return ret; } ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const { if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) { std::ostringstream strm("Can't construct Scalar from invalid ScalarType "); strm << scalarType; THROW_EXCEPTION2(std::invalid_argument, strm.str()); } return scalars[scalarType]; } BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const { std::tr1::shared_ptr s(new BoundedString(maxLength)); Helper::cache(this, s); return s; } ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const { if(elementType<0 || elementType>MAX_SCALAR_TYPE) { std::ostringstream strm("Can't construct ScalarArray from invalid ScalarType "); strm << elementType; THROW_EXCEPTION2(std::invalid_argument, strm.str()); } return scalarArrays[elementType]; } ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType, size_t size) const { if(elementType<0 || elementType>MAX_SCALAR_TYPE) { std::ostringstream strm("Can't construct fixed ScalarArray from invalid ScalarType "); strm << elementType; THROW_EXCEPTION2(std::invalid_argument, strm.str()); } std::tr1::shared_ptr s(new FixedScalarArray(elementType, size)); Helper::cache(this, s); return s; } ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const { if(elementType<0 || elementType>MAX_SCALAR_TYPE) { std::ostringstream strm("Can't construct bounded ScalarArray from invalid ScalarType "); strm << elementType; THROW_EXCEPTION2(std::invalid_argument, strm.str()); } std::tr1::shared_ptr s(new BoundedScalarArray(elementType, size)); Helper::cache(this, s); return s; } StructureConstPtr FieldCreate::createStructure () const { StringArray fieldNames; FieldConstPtrArray fields; return createStructure(fieldNames,fields); } namespace { bool xisalnum(char c) { return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); } void validateFieldName(const std::string& n) { // enforce [A-Za-z_][A-Za-z0-9_]* if(n.size()==0) throw std::invalid_argument("zero length field names not allowed"); if(n[0]>='0' && n[0]<='9') { std::ostringstream msg; msg<<"Field name \""< sp(new Structure(fieldNames,fields)); Helper::cache(this, sp); return sp; } StructureConstPtr FieldCreate::createStructure ( string const & id, StringArray const & fieldNames, FieldConstPtrArray const & fields) const { validateFieldNames(fieldNames); std::tr1::shared_ptr sp(new Structure(fieldNames,fields,id)); Helper::cache(this, sp); return sp; } StructureArrayConstPtr FieldCreate::createStructureArray( StructureConstPtr const & structure) const { std::tr1::shared_ptr sp(new StructureArray(structure)); Helper::cache(this, sp); return sp; } UnionConstPtr FieldCreate::createUnion ( StringArray const & fieldNames,FieldConstPtrArray const & fields) const { validateFieldNames(fieldNames); std::tr1::shared_ptr sp(new Union(fieldNames,fields)); Helper::cache(this, sp); return sp; } UnionConstPtr FieldCreate::createUnion ( string const & id, StringArray const & fieldNames, FieldConstPtrArray const & fields) const { validateFieldNames(fieldNames); std::tr1::shared_ptr sp(new Union(fieldNames,fields,id)); Helper::cache(this, sp); return sp; } UnionConstPtr FieldCreate::createVariantUnion () const { return variantUnion; } UnionArrayConstPtr FieldCreate::createUnionArray( UnionConstPtr const & punion) const { std::tr1::shared_ptr sp(new UnionArray(punion)); Helper::cache(this, sp); return sp; } UnionArrayConstPtr FieldCreate::createVariantUnionArray () const { return variantUnionArray; } StructureConstPtr FieldCreate::appendField( StructureConstPtr const & structure, string const & fieldName, FieldConstPtr const & field) const { StringArray const & oldNames = structure->getFieldNames(); FieldConstPtrArray const & oldFields = structure->getFields(); size_t oldLen = oldNames.size(); StringArray newNames(oldLen+1); FieldConstPtrArray newFields(oldLen+1); for(size_t i = 0; igetID(),newNames,newFields); } StructureConstPtr FieldCreate::appendFields( StructureConstPtr const & structure, StringArray const & fieldNames, FieldConstPtrArray const & fields) const { validateFieldNames(fieldNames); StringArray const & oldNames = structure->getFieldNames(); FieldConstPtrArray const & oldFields = structure->getFields(); size_t oldLen = oldNames.size(); size_t extra = fieldNames.size(); StringArray newNames(oldLen+extra); FieldConstPtrArray newFields(oldLen+extra); for(size_t i = 0; igetID(),newNames,newFields); } static int decodeScalar(int8 code) { static const int integerLUT[] = { pvByte, // 8-bits pvShort, // 16-bits pvInt, // 32-bits pvLong, // 64-bits pvUByte, // unsigned 8-bits pvUShort, // unsigned 16-bits pvUInt, // unsigned 32-bits pvULong // unsigned 64-bits }; static const int floatLUT[] = { -1, // reserved -1, // 16-bits pvFloat, // 32-bits pvDouble, // 64-bits -1, -1, -1, -1 }; // bits 7-5 switch (code >> 5) { case 0: return pvBoolean; case 1: return integerLUT[code & 0x07]; case 2: return floatLUT[code & 0x07]; case 3: return pvString; default: return -1; } } FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl* control) const { control->ensureData(1); int8 code = buffer->getByte(); if (code == -1) return FieldConstPtr(); int typeCode = code & 0xE7; int scalarOrArray = code & 0x18; bool notArray = (scalarOrArray == 0); if (notArray) { if (typeCode < 0x80) { // Type type = Type.scalar; int scalarType = decodeScalar(code); if (scalarType == -1) throw std::invalid_argument("invalid scalar type encoding"); return scalars[scalarType]; } else if (typeCode == 0x80) { // Type type = Type.structure; return deserializeStructureField(this, buffer, control); } else if (typeCode == 0x81) { // Type type = Type.union; return deserializeUnionField(this, buffer, control); } else if (typeCode == 0x82) { // Type type = Type.union; variant union (aka any type) return variantUnion; } else if (typeCode == 0x83) { // TODO cache? // bounded string size_t size = SerializeHelper::readSize(buffer, control); std::tr1::shared_ptr sp( new BoundedString(size)); Helper::cache(this, sp); return sp; } else throw std::invalid_argument("invalid type encoding"); } else // array { bool isVariable = (scalarOrArray == 0x08); // bounded == 0x10; bool isFixed = (scalarOrArray == 0x18); size_t size = (isVariable ? 0 : SerializeHelper::readSize(buffer, control)); if (typeCode < 0x80) { // Type type = Type.scalarArray; int scalarType = decodeScalar(code); if (scalarType == -1) throw std::invalid_argument("invalid scalarArray type encoding"); if (isVariable) return scalarArrays[scalarType]; else if (isFixed) { // TODO use std::make_shared std::tr1::shared_ptr sp( new FixedScalarArray(static_cast(scalarType), size)); Helper::cache(this, sp); return sp; } else { // TODO use std::make_shared std::tr1::shared_ptr sp( new BoundedScalarArray(static_cast(scalarType), size)); Helper::cache(this, sp); return sp; } } else if (typeCode == 0x80) { // TODO fixed and bounded array support if (!isVariable) throw std::invalid_argument("fixed and bounded structure array not supported"); // Type type = Type.structureArray; StructureConstPtr elementStructure = std::tr1::static_pointer_cast(control->cachedDeserialize(buffer)); // TODO use std::make_shared std::tr1::shared_ptr sp(new StructureArray(elementStructure)); Helper::cache(this, sp); return sp; } else if (typeCode == 0x81) { // TODO fixed and bounded array support if (!isVariable) throw std::invalid_argument("fixed and bounded structure array not supported"); // Type type = Type.unionArray; UnionConstPtr elementUnion = std::tr1::static_pointer_cast(control->cachedDeserialize(buffer)); // TODO use std::make_shared std::tr1::shared_ptr sp(new UnionArray(elementUnion)); Helper::cache(this, sp); return sp; } else if (typeCode == 0x82) { // TODO fixed and bounded array support if (!isVariable) throw std::invalid_argument("fixed and bounded structure array not supported"); // Type type = Type.unionArray; variant union (aka any type) return variantUnionArray; } else throw std::invalid_argument("invalid type encoding"); } } namespace detail { struct field_factory { FieldCreatePtr fieldCreate; field_factory() :fieldCreate(new FieldCreate()) { registerRefCounter("Field", &Field::num_instances); registerRefCounter("Thread", &Thread::num_instances); } }; } static detail::field_factory* field_factory_s; static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT; static void field_factory_init(void*) { try { field_factory_s = new detail::field_factory; }catch(std::exception& e){ std::cerr<<"Error initializing getFieldCreate() : "<fieldCreate) throw std::logic_error("getFieldCreate() not initialized"); return field_factory_s->fieldCreate; } FieldCreate::FieldCreate() { for (int i = 0; i <= MAX_SCALAR_TYPE; i++) { std::tr1::shared_ptr sp(new Scalar(static_cast(i))); Helper::cache(this, sp); scalars.push_back(sp); std::tr1::shared_ptr spa(new ScalarArray(static_cast(i))); Helper::cache(this, spa); scalarArrays.push_back(spa); } std::tr1::shared_ptr su(new Union()); Helper::cache(this, su); variantUnion = su; std::tr1::shared_ptr sua(new UnionArray(variantUnion)); Helper::cache(this, sua); variantUnionArray = sua; } }} namespace std{ std::ostream& operator<<(std::ostream& o, const epics::pvData::Field *ptr) { if(ptr) return o << *ptr; return o << "nullptr"; } }