#ifndef CPSW_YAML_H #define CPSW_YAML_H #ifdef NO_YAML_SUPPORT #include namespace YAML { class Node; typedef Node *const_iterator; class Node { public: bool IsSequence() const { throw NoYAMLSupportError(); } bool IsScalar() const { throw NoYAMLSupportError(); } bool IsMap() const { throw NoYAMLSupportError(); } void SetTag(const std::string &) { throw NoYAMLSupportError(); } const_iterator begin() const { return 0; } const_iterator end() const { return 0; } bool operator!() const { return true; } }; }; template class IYamlTypeRegistry { public: virtual T makeItem(YamlState &) = 0; }; template class CYamlTypeRegistry : public IYamlTypeRegistry { public: virtual T makeItem(YamlState &) { throw NoYAMLSupportError(); } virtual void extractClassName(std::vector *svec_p, YamlState &) { throw NoYAMLSupportError(); } virtual bool extractInstantiate(YamlState &node) { throw NoYAMLSupportError(); } }; template class IYamlFactoryBase { public: IYamlFactoryBase(const char *className, IYamlTypeRegistry *r) {} virtual T makeItem(YamlState &node, IYamlTypeRegistry *) { throw NoYAMLSupportError(); } }; class CYamlSupportBase : public virtual IYamlSupportBase { public: virtual void dumpYamlPart(YAML::Node &n) const {} virtual void dumpYaml(YAML::Node &n) const {} }; template static void mustReadNode(YamlState &node, const char *fld, T *val) { throw NoYAMLSupportError(); } template static bool readNode(YamlState &node, const char *fld, T *val) { return false; } template static void writeNode(YAML::Node &node, const char *fld, const T &val) { throw NoYAMLSupportError(); } static inline void pushNode(YAML::Node &node, const char *fld, const YAML::Node &child) { throw NoYAMLSupportError(); } static inline const YAML::Node getNode(YamlState &n, const char *fld) { throw NoYAMLSupportError(); } template class CYamlFieldFactory { }; #else #include #include #include namespace YAML { // A 'Node' extension which remembers parent nodes // (which is necessary for backtracking through "<<" merge keys). // The idea is that PNodes 'live' as automatic variables on the // stack. // The 'uplink_' points to the parent node which is on the stack // of the current function or one of its callers. // The assumption is that this class is used from code which // recurses and backtracks through the tree... // PNodes observe 'copy-move' semantics. 'parent' and 'child' // information of a PNode is erased when it is copied. class PNode : public Node { private: const PNode mutable * parent_; const PNode mutable * child_; const char * key_; public: void dump() const; const char *getName() const { return key_; } // assign a new Node to this PNode while preserving parent/child/key // ('merge' operation) PNode & operator=(const Node &orig_node); void merge(const Node &orig_node); // 'extract' a Node from a PNode Node get() const; // 'assignment'; moves parent/child from the original to the destination PNode & operator=(const PNode &orig); // 'copy constructor'; moves parent/child from the original to the destination PNode(const PNode &orig); // Construct a PNode by map lookup while remembering // the parent. PNode( const PNode *parent, const char *key ); // A PNode from a Node PNode( const PNode *parent, const char *key, const Node &node); // Construct a PNode by sequence lookup while remembering // the parent. // Note that merge tag backtracking does not support // backing up through sequences! PNode( const PNode *parent, unsigned index ); ~PNode(); class MergekeyVisitor { public: virtual bool visit(PNode *merged_node) = 0; }; // starting at 'this' node visit all merge keys upstream // until the visitor's 'visit' method returns 'false', the // root is reached or 'maxlevel' parents have been scanned // for merge keys. void visitMergekeys(MergekeyVisitor *, int maxlevel = -1); // lookup a key starting at 'this' PNode and backtrack // through merge ("<<") keys. // // If there is a structure like this // // a: // <<: // b: // c: // key1: merged_value1 // key2: merged_value2 // b: // c: // key1: value1: // // then the algorithm is expected to // find // a["b"]["c"]["key1"] -> value1 // a["b"]["c"]["key2"] -> merged_value2 // // Since recursive lookups through many merge keys can become // quite expensive we provide the 'maxlevel' feature which // tells the algorithm through how many levels of parents it // should search for merge keys (0: none, <0 all levels). PNode lookup(const char *key, int maxlevel = -1) const; private: static YAML::Node backtrack_mergekeys(const YAML::PNode *, unsigned, const YAML::Node &); }; }; typedef const YAML::PNode YamlState; class CYamlSupportBase; typedef shared_ptr YamlSupportBase; class CYamlSupportBase : public virtual IYamlSupportBase { public: // subclass may implement a constructor from a YAML::Node // every class implements this and MUST chain through its // superclass first (thus, derived classes can overwrite fields) // e.g., // // void CYamlSupportSubclass::dumpYamlPart(YAML::Node &node) // { // CYamlSupportBase::dumpYamlPart( node ); // chain // // dump subclass fields into node here (may override superclass entries) // node["myField"] = myvalue; // } virtual void dumpYamlPart(YAML::Node &node) const; // When using the 'CYamlFieldFactory' then every subclass // MUST define a static _getClassName() member and let it // return a unique value; // Other factories may use other ways to determine the // name that is entered into the registry... // Copy-paste this method and modify the name. static const char *_getClassName() { return "YamlSupportBase"; } // Every subclass MUST implement the 'getClassName()' virtual // method (for polymorphic access to the class name). // Just copy-paste this one: virtual const char *getClassName() const { return _getClassName(); } // subclass MAY implement 'overrideNode()' which is executed // by dispatchMakeField() before the new entity is created. // Can be used to modify the YAML node before it is passed // to the constructor (create a new node from 'orig', modify // and return it). static void overrideNode(YamlState &orig); // insert the classname into a node; // e.g.,: // node["class"] = getClassName(); // or // node.SetTag( getClassName() ); virtual void setClassName(YAML::Node &node) const; // used by CYamlSupportBase and DevImpl to append class name and iterate // through children; subclass probably wants to leave this alone unless // you know what you are doing... virtual void dumpYaml(YAML::Node &) const; }; class CDevImpl; typedef shared_ptr DevImpl; /* Template specializations to convert YAML nodes to CPSW entries * (ByteOrder, IIntField::Mode, Field, MMIODev, etc.) */ namespace YAML { template<> struct convert { static bool decode(const Node& node, ByteOrder& rhs) { if (!node.IsScalar()) return false; std::string str = node.Scalar(); if( str.compare( "LE" ) == 0 ) rhs = LE; else if (str.compare( "BE" ) == 0 ) rhs = BE; else rhs = UNKNOWN; return true; } static Node encode(const ByteOrder &rhs) { Node node; if ( LE == rhs ) node = "LE"; else if ( BE == rhs ) node = "BE"; else node = "UNKNOWN"; return node; } }; template<> struct convert { static bool decode(const Node& node, IField::Cacheable& rhs) { if (!node.IsScalar()) return false; std::string str = node.Scalar(); if( str.compare( "NOT_CACHEABLE" ) == 0 ) rhs = IField::NOT_CACHEABLE; else if (str.compare( "WT_CACHEABLE" ) == 0 ) rhs = IField::WT_CACHEABLE; else if (str.compare( "WB_CACHEABLE" ) == 0 ) rhs = IField::WB_CACHEABLE; else rhs = IField::UNKNOWN_CACHEABLE; return true; } static Node encode(const IField::Cacheable& rhs) { Node node; switch ( rhs ) { case IField::NOT_CACHEABLE: node = "NOT_CACHEABLE"; break; case IField::WT_CACHEABLE: node = "WT_CACHEABLE"; break; case IField::WB_CACHEABLE: node = "WB_CACHEABLE"; break; default: node = "UNKNOWN_CACHEABLE"; break; } return node; } }; template<> struct convert { static bool decode(const Node& node, IIntField::Mode& rhs) { if (!node.IsScalar()) return false; std::string str = node.Scalar(); if( str.compare( "RO" ) == 0 ) rhs = IIntField::RO; else if (str.compare( "WO" ) == 0 ) #if 0 // without caching and bit-level access at the SRP protocol level we cannot // support write-only yet. rhs = IIntField::WO; #else rhs = IIntField::RW; #endif else if (str.compare( "RW" ) == 0 ) rhs = IIntField::RW; else return false; return true; } static Node encode(const IIntField::Mode &rhs) { Node node; switch ( rhs ) { case IIntField::RO: node = "RO"; break; case IIntField::RW: node = "RW"; break; case IIntField::WO: node = "WO"; break; default: break; } return node; } }; #if 0 template<> struct convert { static bool decode(const PNode &node, MutableEnum &rhs) { if ( ! node.IsSequence() ) { return false; } MutableEnum e = IMutableEnum::create( node ); rhs = e; return true; } }; template<> struct convert { static bool decode(const Node &node, IIntField::Builder &rhs) { IIntField::Builder bldr = IIntField::IBuilder::create(); std::string name = node["name"].as(); bldr->name( node["name"].as().c_str() ); if( node["sizeBits"] ) { bldr->sizeBits( node["sizeBits"].as() ); } if( node["isSigned"] ) { bldr->isSigned( node["isSigned"].as() ); } if( node["lsBit"] ) { bldr->lsBit( node["lsBit"].as() ); } if( node["mode"] ) { bldr->mode( node["mode"].as() ); } if( node["wordSwap"] ) { bldr->wordSwap( node["wordSwap"].as() ); } if( node["enums"] ) { bldr->setEnum( node["enums"].as() ); } rhs = bldr; return true; } }; template<> struct convert { static bool decode(const Node& node, IntField& rhs) { // if(!node.IsMap() || node.size() >= 3) { // return false; // } IIntField::Builder bldr( node.as() ); rhs = bldr->build(); return true; } }; template<> struct convert { static bool decode(const Node& node, SequenceCommand& rhs) { // if(!node.IsMap() || node.size() >= 3) { // return false; // } std::string name; std::vector fields; std::vector values; //name = "C_"; //name = name + node["name"].as(); name = node["name"].as(); if( node["sequence"] ) { const YAML::Node& seq = node["sequence"]; for( unsigned i = 0; i < seq.size(); i++ ) { fields.push_back( seq[i]["entry"].as() ); values.push_back( seq[i]["value"].as() ); } } else { return false; } rhs = ISequenceCommand::create( name.c_str(), fields, values ); return true; } }; template<> struct convert { static bool decode(const Node& node, MMIODev& rhs) { std::string name = node["name"].as(); uint64_t size = node["size"].as(); rhs = IMMIODev::create( name.c_str(), size ); Field f; if ( node["MMIODev"] ) { const YAML::Node& mmio = node["MMIODev"]; for( unsigned i = 0; i < mmio.size(); i++ ) { uint64_t offset = mmio[i]["offset"].as(); unsigned nelms = mmio[i]["nelms"] ? mmio[i]["nelms"].as() : 1; uint64_t stride = mmio[i]["stride"] ? mmio[i]["stride"].as() : IMMIODev::STRIDE_AUTO; ByteOrder byteOrder = mmio[i]["ByteOrder"] ? mmio[i]["ByteOrder"].as() : UNKNOWN; IField::Cacheable cacheable = mmio[i]["cacheable"] ? \ mmio[i]["cacheable"].as() : \ IField::UNKNOWN_CACHEABLE; f = mmio[i].as(); f->setCacheable( cacheable ); rhs->addAtAddress( f, offset, nelms, stride, byteOrder ); } } // This device contains iField if ( node["IntField"] ) { const YAML::Node& iField = node["IntField"]; for( unsigned i = 0; i < iField.size(); i++ ) { uint64_t offset = iField[i]["offset"].as(); unsigned nelms = iField[i]["nelms"] ? iField[i]["nelms"].as() : 1; uint64_t stride = iField[i]["stride"] ? iField[i]["stride"].as() : IMMIODev::STRIDE_AUTO; ByteOrder byteOrder = iField[i]["ByteOrder"] ? iField[i]["ByteOrder"].as() : UNKNOWN; std::string desc = iField[i]["description"].as(); IField::Cacheable cacheable = iField[i]["cacheable"] ? \ iField[i]["cacheable"].as() : \ IField::UNKNOWN_CACHEABLE; f = iField[i].as(); f->setDescription( desc ); f->setCacheable( cacheable ); rhs->addAtAddress( f, offset, nelms, stride, byteOrder ); } } if ( node["Commands"] ) { const YAML::Node& commands = node["Commands"]; for( unsigned i = 0; i < commands.size(); i++ ) { f = commands[i].as(); rhs->addAtAddress( f, 0 ); } } // This device contains other mmio return true; } }; #endif template<> struct convert { static bool decode(const Node& node, INetIODev::ProtocolVersion& rhs) { if (!node.IsScalar()) return false; std::string str = node.Scalar(); if ( str.compare( "SRP_UDP_V1" ) == 0 ) rhs = INetIODev::SRP_UDP_V1; else if (str.compare( "SRP_UDP_V2" ) == 0 ) rhs = INetIODev::SRP_UDP_V2; else if (str.compare( "SRP_UDP_V3" ) == 0 ) rhs = INetIODev::SRP_UDP_V3; else if (str.compare( "SRP_UDP_NONE" ) == 0 ) rhs = INetIODev::SRP_UDP_NONE; else return false; return true; } static Node encode(const INetIODev::ProtocolVersion &rhs) { Node node; switch( rhs ) { case INetIODev::SRP_UDP_V1: node = "SRP_UDP_V1"; break; case INetIODev::SRP_UDP_V2: node = "SRP_UDP_V2"; break; case INetIODev::SRP_UDP_V3: node = "SRP_UDP_V3"; break; default: node = "SRP_UDP_NONE"; break; } return node; } }; #if 0 template<> struct convert { static bool decode(const Node& node, INetIODev::PortBuilder& rhs) { rhs = INetIODev::createPortBuilder(); { const YAML::Node &nn( getNode(node, "SRP") ); if( nn ) { { const YAML::Node &n( getNode(nn, "ProtocolVersion") ); if( n ) rhs->setSRPVersion( n.as() ); } { const YAML::Node &n( getNode(nn, "TimeoutUS") ); if( n ) rhs->setSRPTimeoutUS( n.as() ); } { const YAML::Node &n( getNode(nn, "DynTimeout") ); if( n ) rhs->useSRPDynTimeout( n.as() ); } { const YAML::Node &n( getNode(nn, "RetryCount") ); if( n ) rhs->setSRPRetryCount( n.as() ); } } } { const YAML::Node &nn( getNode(node, "udp") ); if ( nn ) { { const YAML::Node &n( getNode(nn, "port") ); if( n ) rhs->setUdpPort( n.as() ); } { const YAML::Node &n( getNode(nn, "outQueueDepth") ); if( n ) rhs->setUdpOutQueueDepth( n.as() ); } { const YAML::Node &n( getNode(nn, "numRxThreads") ); if( n ) rhs->setUdpNumRxThreads( n.as() ); } { const YAML::Node &n( getNode(nn, "pollSecs") ); if( n ) rhs->setUdpPollSecs( n.as() ); } } } { const YAML::Node &nn( getNode(node, "RSSI" ) ); if ( nn ) rhs->useRssi( nn.as() ); } { const YAML::Node &nn( getNode(node, "depack") ); if (nn ) { rhs->useDepack( true ); { const YAML::Node &n( getNode(nn, "outQueueDepth") ); if( n ) rhs->setDepackOutQueueDepth( n.as() ); } { const YAML::Node &n( getNode(nn, "ldFrameWinSize") ); if( n ) rhs->setDepackLdFrameWinSize( n.as() ); } { const YAML::Node &n( getNode(nn, "ldFragWinSize") ); if( n ) rhs->setDepackLdFragWinSize( n.as() ); } } } { const YAML::Node &nn( getNode(node, "SRPMux") ); if (nn ) { rhs->useSRPMux( true ); { const YAML::Node &n( getNode(nn, "VirtualChannel") ); if( n ) rhs->setSRPMuxVirtualChannel( n.as() ); } } } { const YAML::Node &nn( getNode(node, "TDestMux") ); if (nn ) { rhs->useTDestMux( true ); { const YAML::Node &n( getNode(nn, "TDEST") ); if( n ) rhs->setTDestMuxTDEST( n.as() ); } { const YAML::Node &n( getNode(nn, "StripHeader") ); if( n ) rhs->setTDestMuxStripHeader( n.as() ); } { const YAML::Node &n( getNode(nn, "outQueueDepth") ); if( n ) rhs->setTDestMuxOutQueueDepth( n.as() ); } } } return true; } }; template<> struct convert { static bool decode(const Node& node, NetIODev& rhs) { std::string name = node["name"].as(); std::string ipAddr = node["ipAddr"].as(); rhs = INetIODev::create( name.c_str(), ipAddr.c_str() ); INetIODev::PortBuilder bldr; Field f; if ( node["MMIODev"] ) { const YAML::Node& mmio = node["MMIODev"]; for( unsigned i = 0; i < mmio.size(); i++ ) { f = mmio[i].as(); bldr = mmio[i].as(); rhs->addAtAddress( f, bldr ); } } if ( node["StreamDev"] ) { const YAML::Node& stream = node["StreamDev"]; for( unsigned i = 0; i < stream.size(); i++ ) { f = IField::create( stream[i]["name"].as().c_str() ); bldr = stream[i].as(); rhs->addAtAddress( f, bldr ); } } return true; } }; #endif } // helpers to read map entries template static void mustReadNode(YamlState &node, const char *fld, T *val) { const YAML::Node &n( node.lookup(fld) ); if ( ! n ) { throw NotFoundError( std::string("property '") + std::string(fld) + std::string("'") ); } else { *val = n.as(); } } template static bool readNode(YamlState &node, const char *fld, T *val) { const YAML::Node &n( node.lookup(fld) ); if ( n ) { *val = n.as(); return true; } return false; } template static void writeNode(YAML::Node &node, const char *fld, const T &val) { node[fld] = val; } void pushNode(YAML::Node &node, const char *fld, const YAML::Node &child); // YAML Emitter overloads YAML::Emitter& operator << (YAML::Emitter& out, const ScalVal_RO& s); YAML::Emitter& operator << (YAML::Emitter& out, const Hub& h); template class IYamlFactoryBase; template class IYamlTypeRegistry { public: virtual T makeItem(YamlState &) = 0; virtual void extractClassName(std::vector *svec_p, YamlState &) = 0; virtual void addFactory(const char *className, IYamlFactoryBase *f) = 0; virtual void delFactory(const char *className) = 0; virtual void dumpClasses() = 0; }; class IRegistry { public: virtual void addItem(const char *name, void *item) = 0; virtual void delItem(const char *name) = 0; virtual void *getItem(const char *name) = 0; virtual void dumpItems() = 0; virtual ~IRegistry() {} static IRegistry *create(); }; template class IYamlFactoryBase { private: IYamlTypeRegistry *registry_ ; const char *className_; public: IYamlFactoryBase(const char *className, IYamlTypeRegistry *r) : registry_(r), className_(className) { r->addFactory( className, this ); } virtual T makeItem(YamlState &node, IYamlTypeRegistry *) = 0; virtual ~IYamlFactoryBase() { registry_->delFactory( className_ ); } }; template class CYamlTypeRegistry : public IYamlTypeRegistry { private: IRegistry *registry_; CYamlTypeRegistry(const CYamlTypeRegistry&); CYamlTypeRegistry operator=(const CYamlTypeRegistry &); public: CYamlTypeRegistry() : registry_( IRegistry::create() ) { } virtual void addFactory(const char *className, IYamlFactoryBase *f) { registry_->addItem(className, f); } virtual void delFactory(const char *className) { registry_->delItem(className); } virtual void extractClassName(std::vector *svec_p, YamlState &n); virtual bool extractInstantiate(YamlState &n); virtual T makeItem(YamlState &n) { std::vector str_vec; std::string str_no_factory = ""; if ( ! extractInstantiate( n ) ) return T(); extractClassName( &str_vec, n ); if ( str_vec.size() == 0 ) throw NotFoundError("CYamlTypeRegistry: node w/o class info"); for( std::vector::iterator it = str_vec.begin(); it != str_vec.end(); ++it ) { IYamlFactoryBase *factory = static_cast *>( registry_->getItem( it->c_str() ) ); if ( factory ) return factory->makeItem( n, this ); if ( 0 != str_no_factory.size() ) str_no_factory += ", "; str_no_factory += (*it); } throw NotFoundError(std::string("No factory for '") + str_no_factory + std::string("' found")); } virtual void dumpClasses() { registry_->dumpItems(); } virtual ~CYamlTypeRegistry() { delete registry_; } }; class CYamlFieldFactoryBase : public IYamlFactoryBase { public: static IYamlTypeRegistry *getFieldRegistry(); protected: CYamlFieldFactoryBase(const char *typeLabel) : IYamlFactoryBase( typeLabel, getFieldRegistry() ) { } virtual void addChildren(CEntryImpl &, YamlState &, IYamlTypeRegistry *); virtual void addChildren(CDevImpl &, YamlState &, IYamlTypeRegistry *); public: static Dev dispatchMakeField(const YAML::Node &node, const char *root_name); static YAML::Node loadPreprocessedYaml (std::istream &, const char *yaml_dir = 0); static YAML::Node loadPreprocessedYaml (const char *char_stream, const char *yaml_dir = 0); static YAML::Node loadPreprocessedYamlFile(const char *file_name, const char *yaml_dir = 0); static void dumpClasses() { getFieldRegistry()->dumpClasses(); } }; template class CYamlFieldFactory : public CYamlFieldFactoryBase { public: CYamlFieldFactory() : CYamlFieldFactoryBase(T::element_type::_getClassName()) { } virtual Field makeItem(YamlState & state, IYamlTypeRegistry *registry) { T::element_type::overrideNode(state); T fld( CShObj::create(state) ); addChildren( *fld, state, registry ); return fld; } }; #endif // For adding YAML support to a subclass of CYamlSupportBase: // // - add a constructor that takes a YAML::Node & argument (YAML -> c++ class) // The factory's makeItem() then uses this constructor. Alternatively, // makeItem() may use another constructor and build the item using // information from the node. // - add a virtual 'void dumpYamlPart(YAML::Node&)' member (c++ class -> YAML) // This method MUST chain through the corresponding superclass member. // - copy/paste virtual 'const char *getClassName()' method (from CEntryImpl) // - add a 'static const char * _getClassName()' member and return a unique name // - expand 'DECLARE_YAML_FIELD_FACTORY( shared_pointer_type )' macro ONCE from code which // is guaranteed to be linked by the application (can be tricky when using static // linking). Built-in classes do this in 'cpsw_yaml.cc'. // #define DECLARE_YAML_FIELD_FACTORY(FieldType) CYamlFieldFactory FieldType##factory_ #endif