diff --git a/dram/.cproject b/dram/.cproject index a6a2ab6a..b444b756 100644 --- a/dram/.cproject +++ b/dram/.cproject @@ -1,80 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -86,57 +14,77 @@ - - - - - - - - @@ -155,11 +103,16 @@ + + + + + @@ -184,6 +137,9 @@ + + + @@ -193,9 +149,18 @@ + + + + + + + + + diff --git a/dram/.gitignore b/dram/.gitignore index 8ebfee1c..910cfef8 100644 --- a/dram/.gitignore +++ b/dram/.gitignore @@ -1,3 +1,5 @@ /simulation /unit_test /unit_test +/build-simulation +/simulation-build diff --git a/dram/.settings/language.settings.xml b/dram/.settings/language.settings.xml index c75ff474..127cd8ac 100644 --- a/dram/.settings/language.settings.xml +++ b/dram/.settings/language.settings.xml @@ -1,20 +1,10 @@ - + - - - - - - - - - - - + diff --git a/dram/src/common/DebugManager.cpp b/dram/src/common/DebugManager.cpp new file mode 100644 index 00000000..be552906 --- /dev/null +++ b/dram/src/common/DebugManager.cpp @@ -0,0 +1,50 @@ +/* + * DebugManager.cpp + * + * Created on: Mar 20, 2014 + * Author: jonny + */ + +#include "DebugManager.h" +#include + +using namespace std; + +DebugManager& DebugManager::getInstance() +{ + static DebugManager manager; + return manager; +} + +void DebugManager::printDebug(Importance importancy, Sender sender, sc_time time, std::string message) +{ + cout << "[" << importancyToString(importancy) << "]"; + if(printTime) std::cout << " at " << time; + if(printLocation) std::cout << " in " << senderToString(sender); + cout << ": " << message << endl; +} + +string DebugManager::importancyToString(Importance importancy) +{ + //if((unsigned int)importancy == Importancy::Warning) + // return "Warning"; + + switch(importancy) + { + case Importance::Info: return "[Info]"; + case Importance::Warning: return "[Warning]"; + case Importance::Error: return "[Error]"; + } + return "unknown importance"; +} + +string DebugManager::senderToString(Sender sender) +{ + + /*switch(sender) + { + case Sender::Core: return "Core"; + case Sender::Scheduler: return "Scheduler"; + }*/ + return "Core"; +} diff --git a/dram/src/common/DebugManager.h b/dram/src/common/DebugManager.h new file mode 100644 index 00000000..eaac6141 --- /dev/null +++ b/dram/src/common/DebugManager.h @@ -0,0 +1,35 @@ +/* + * DebugManager.h + * + * Created on: Mar 20, 2014 + * Author: jonny + */ + +#ifndef DEBUGMANAGER_H_ +#define DEBUGMANAGER_H_ + +#include + +enum class Importance {Warning, Error, Info}; + +class DebugManager +{ +public: + static DebugManager& getInstance(); + + enum Sender {Core, Scheduler}; + + bool printTime; + bool printLocation; + + void printDebug(Importance i, Sender s, sc_time time, std::string message); + +private: + DebugManager() : printTime(true), printLocation(true) {}; + DebugManager(const DebugManager&); + std::string senderToString(Sender sender); + std::string importancyToString(Importance importancy); + +}; + +#endif /* DEBUGMANAGER_H_ */ diff --git a/dram/src/common/dramExtension.cpp b/dram/src/common/dramExtension.cpp new file mode 100644 index 00000000..ccc331a9 --- /dev/null +++ b/dram/src/common/dramExtension.cpp @@ -0,0 +1,94 @@ +#include "dramExtension.h" +#include + +using namespace tlm; + +tlm_extension_base* DramExtension::clone() const +{ + return new DramExtension(thread, bank, row, column); +} + +void DramExtension::copy_from(const tlm_extension_base &ext) +{ + const DramExtension &cpyFrom = static_cast(ext); + thread = cpyFrom.thread; + bank = cpyFrom.bank; + row = cpyFrom.row; + column = cpyFrom.column; +} + +/* Static methods + * + */ +const DramExtension& DramExtension::getExtension(const tlm_generic_payload *payload) +{ + DramExtension *result = NULL; + payload->get_extension(result); + assert(result != NULL); + return *result; +} + +const DramExtension& DramExtension::getExtension(const tlm_generic_payload &payload) +{ + return DramExtension::getExtension(&payload); +} + +bool operator ==(const Thread& lhs, const Thread& rhs) +{ + return lhs.ID() == rhs.ID(); +} + +bool operator !=(const Thread& lhs, const Thread& rhs) +{ + return !(lhs == rhs); +} + +bool operator ==(const Channel& lhs, const Channel& rhs) +{ + return lhs.ID() == rhs.ID(); +} + +bool operator !=(const Channel& lhs, const Channel& rhs) +{ + return !(lhs == rhs); +} + +bool operator ==(const Bank& lhs, const Bank& rhs) +{ + return lhs.ID() == rhs.ID(); +} + +bool operator !=(const Bank& lhs, const Bank& rhs) +{ + return !(lhs == rhs); +} + +bool operator <(const Bank& lhs, const Bank& rhs) +{ + return lhs.ID() < rhs.ID(); +} + + +const Row Row::NO_ROW; + +bool operator ==(const Row& lhs, const Row& rhs) +{ + if(lhs.isNoRow != rhs.isNoRow) + return false; + return lhs.ID() == rhs.ID(); +} + +bool operator !=(const Row& lhs, const Row& rhs) +{ + return !(lhs == rhs); +} + +bool operator ==(const Column& lhs, const Column& rhs) +{ + return lhs.ID() == rhs.ID(); +} + +bool operator !=(const Column& lhs, const Column& rhs) +{ + return !(lhs == rhs); +} diff --git a/dram/src/common/dramExtension.h b/dram/src/common/dramExtension.h new file mode 100644 index 00000000..435212b1 --- /dev/null +++ b/dram/src/common/dramExtension.h @@ -0,0 +1,116 @@ +/* + * DramExtension.h + * + * Created on: Mar 9, 2014 + * Author: gernhard + */ + +#ifndef DRAMEXTENSION_H_ +#define DRAMEXTENSION_H_ + +#include +#include + +class Thread +{ +public : + explicit Thread(unsigned int id) : id(id) {} + + unsigned int ID() const { return id;} +private: + unsigned int id; +}; + + +class Channel +{ +public : + explicit Channel(unsigned int id) : id(id) {} + unsigned int ID() const { return id;} +private: + unsigned int id; +}; + + +class Bank +{ +public : + explicit Bank(unsigned int id) : id(id) {} + unsigned int ID() const { return id;} +private: + unsigned int id; +}; + + +class Row +{ +public : + static const Row NO_ROW; + + Row() : id(0), isNoRow(true) {} + explicit Row(unsigned int id) : id(id), isNoRow(false) {} + + unsigned int ID() const { return id;} +private: + unsigned int id; + bool isNoRow; + + friend bool operator==(const Row &lhs, const Row &rhs); +}; + +class Column +{ +public : + explicit Column(unsigned int id) : id(id) {} + + unsigned int ID() const { return id;} +private: + unsigned int id; +}; + +bool operator==(const Thread &lhs, const Thread &rhs); +bool operator!=(const Thread &lhs, const Thread &rhs); +bool operator==(const Channel &lhs, const Channel &rhs); +bool operator!=(const Channel &lhs, const Channel &rhs); +bool operator==(const Bank &lhs, const Bank &rhs); +bool operator!=(const Bank &lhs, const Bank &rhs); +bool operator<(const Bank &lhs, const Bank &rhs); +bool operator==(const Row &lhs, const Row &rhs); +bool operator!=(const Row &lhs, const Row &rhs); +bool operator==(const Column &lhs, const Column &rhs); +bool operator!=(const Column &lhs, const Column &rhs); + +class DramExtension : public tlm::tlm_extension +{ +private: + Thread thread; + Channel channel; + Bank bank; + Row row; + Column column; + +public: + + DramExtension():thread(0),channel(0),bank(0),row(0),column(0){} + DramExtension(const Thread& thread, const Bank& bank, const Row& row, const Column& column) : + thread(thread),channel(0),bank(bank),row(row),column(column){} + DramExtension(const Thread& thread,const Channel& channel, const Bank& bank, const Row& row, const Column& column) : + thread(thread),channel(channel),bank(bank),row(row),column(column){} + + + ~DramExtension(){} + virtual tlm::tlm_extension_base* clone() const; + virtual void copy_from(const tlm_extension_base &ext); + + const Thread& getThread() const{return thread;} + const Channel& getChannel() const{return channel;} + const Bank& getBank() const{return bank;} + const Row& getRow() const{return row;} + const Column& getColumn() const{return column;} + + + static const DramExtension& getExtension(const tlm::tlm_generic_payload *payload); + static const DramExtension& getExtension(const tlm::tlm_generic_payload &payload); +}; + +#endif /* DRAMEXTENSION_H_ */ diff --git a/dram/src/common/protocol.h b/dram/src/common/protocol.h new file mode 100755 index 00000000..2bb20e8a --- /dev/null +++ b/dram/src/common/protocol.h @@ -0,0 +1,50 @@ +#ifndef EXTENDED_PHASE_DRAM +#define EXTENDED_PHASE_DRAM +// DRAM Control Phases + +DECLARE_EXTENDED_PHASE(BEGIN_PRE); +DECLARE_EXTENDED_PHASE(END_PRE); + +DECLARE_EXTENDED_PHASE(BEGIN_ACT); +DECLARE_EXTENDED_PHASE(END_ACT); + +DECLARE_EXTENDED_PHASE(BEGIN_REFA); +DECLARE_EXTENDED_PHASE(END_REFA); + +DECLARE_EXTENDED_PHASE(BEGIN_REFB); +DECLARE_EXTENDED_PHASE(END_REFB); + +// Phases for Read and Write + +DECLARE_EXTENDED_PHASE(BEGIN_WR); +DECLARE_EXTENDED_PHASE(END_WR); + +DECLARE_EXTENDED_PHASE(BEGIN_RD); +DECLARE_EXTENDED_PHASE(END_RD); + +DECLARE_EXTENDED_PHASE(BEGIN_WRA); +DECLARE_EXTENDED_PHASE(END_WRA); + +DECLARE_EXTENDED_PHASE(BEGIN_RDA); +DECLARE_EXTENDED_PHASE(END_RDA); + +// Phases for Power Down + +DECLARE_EXTENDED_PHASE(BEGIN_PDNP); +DECLARE_EXTENDED_PHASE(END_PDNP); + +DECLARE_EXTENDED_PHASE(BEGIN_PDNA); +DECLARE_EXTENDED_PHASE(END_PDNA); + +DECLARE_EXTENDED_PHASE(BEGIN_SREF); +DECLARE_EXTENDED_PHASE(END_SREF); + +// Internal Phases +DECLARE_EXTENDED_PHASE(START_PROCESSING); +DECLARE_EXTENDED_PHASE(AUTO_PRECHARGE); + +//Triggers +DECLARE_EXTENDED_PHASE(REFRESH_TRIGGER); + +#endif + diff --git a/dram/src/common/third_party/tinystr.cpp b/dram/src/common/third_party/tinystr.cpp new file mode 100755 index 00000000..06657682 --- /dev/null +++ b/dram/src/common/third_party/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/dram/src/common/third_party/tinystr.h b/dram/src/common/third_party/tinystr.h new file mode 100755 index 00000000..89cca334 --- /dev/null +++ b/dram/src/common/third_party/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/dram/src/common/third_party/tinyxml.cpp b/dram/src/common/third_party/tinyxml.cpp new file mode 100755 index 00000000..9c161dfc --- /dev/null +++ b/dram/src/common/third_party/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/dram/src/common/third_party/tinyxml.h b/dram/src/common/third_party/tinyxml.h new file mode 100755 index 00000000..a3589e5b --- /dev/null +++ b/dram/src/common/third_party/tinyxml.h @@ -0,0 +1,1805 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i 1 warning from 1st checker of path +// Version 3, 17 July 2008 Support compilation under SystemC 2.1.v1 +// Version 4, 12 Aug 2008 Add header #include +// Version 5, 08 Sep 2008 Fix bugs in message text + +// TLM-2.0 Base Protocol Compliance Checker + +/* +Instantiate this checker module in-line between initiator and target, initiator and interconnect, +or interconnect and target by binding the target_socket and initiator_socket +Binding two checkers either side of an interconnect component, or interleaving a series of +checkers with interconnect components, will enable some deeper checks as against having just +a single checker + +For example + + Initiator *initiator; + Bus *bus; + Memory *memory; + ... + initiator->socket.bind(bus->target_socket); + bus->initiator_socket.bind(memory->socket); + +might become + + tlm_utils::tlm2_base_protocol_checker<32> *checker1; + tlm_utils::tlm2_base_protocol_checker<32> *checker2; + ... + initiator->socket.bind(checker1->target_socket); + checker1->initiator_socket.bind(bus->target_socket); + bus->initiator_socket.bind(checker2->target_socket); + checker2->initiator_socket.bind(memory->socket); + + +GENERAL FEATURES OF THE BASE PROTOCOL CHECKER + +The checks are relatively expensive, hence by default the number of checks is limited. +A maximum number can be set explicitly by calling set_num_checks(max) +Checking can be deactivated at any time by calling set_num_checks(0) +All checkers decrement a single global count, because having some checkers running and +others not can cause bogus violation reports +It is not permitted to turn checks on by calling set_num_checks() once checking has been +deactivated, because this could cause bogus violation reports + +The DMI and debug checks are unaffected by the num_checks count (because they are cheap) + +The error messages contain User Manual references + +The checker is designed to be used with a transaction pool: otherwise it could consume +a lot of memory. The checker keeps a local copy of each transaction object +Failures are reported with a severity of SC_ERROR. The actions may be overridden by calling: + sc_report_handler::set_actions("tlm2_protocol_checker", ...); + +SPECIFIC CHECKS + +nb_transport: phase sequence BEGIN_REQ -> END_REQ -> BEGIN_RESP -> END_RESP +Must not have two outstanding requests or responses (exclusion rules) +Must not have decreasing timing annotations on calls to or returns from nb_transport_fw/bw +Phase extensions permitted and ignored +Must not call b_transport during nb_transport phase sequence and vice-versa + +nb_transport: memory manager must be set +nb_transport: reference count must be non-zero +First checker in BEGIN_REQ path should see ref_count == 1 (warning only) +An interconnect component that sets a memory manager should also clear it +An interconnect component that sets extensions with no memory manager should also clear them +(Does not bother with these memory manager checks for DMI and debug) + +Transaction object must be properly initialized +Many generic payload attributes must not be modified during the transaction lifetime +Transaction object must not be re-allocated for a new transaction while still in use +DMI descriptor must be properly initialized +Debug transaction must be properly initialized +Debug byte count must be less than data_length + +Checks that require multiple checkers to be instantiated along a transaction path: +The BEGIN_RESP path must be the reverse of the BEGIN_REQ path +Transaction object must not be sent with BEGIN_REQ while participating in a previous response +Interconnect component must not set response status attribute to TLM_OK_RESPONSE +Interconnect component must not modify data array on the response path +*/ + + +// ******************** PREAMBLE ******************** + + +#ifndef __tlm2_base_protocol_checker__ +#define __tlm2_base_protocol_checker__ + +#include "systemc" +using std::cout; +using std::endl; +using std::dec; +using std::hex; + +#include +#include +#include + + +namespace tlm_utils { + + +// Number of checks remaining +const sc_dt::uint64 default_num_checks = 100000; +static sc_dt::uint64 num_checks = default_num_checks; + + +// Types used when building a trace of the transaction path +typedef unsigned char uchar_t; +typedef std::deque deque_t; + +struct path_t { + path_t () { response_in_progress = false; ok_response = false; resp_data_ptr = 0; } + + bool response_in_progress; + bool ok_response; + deque_t path; + uchar_t* resp_data_ptr; // Copy of data on response path +}; + +// Global variable used for checks involving multiple checkers along a transaction path +static std::map shared_map; + + +// ******************** CLASS DEFINITION ******************** + + +template +class tlm2_base_protocol_checker + +: public sc_core::sc_module +, public tlm::tlm_fw_transport_if +, public tlm::tlm_bw_transport_if +{ +public: + + // Instantiate and bind checker inline between an existing pair of initiator and target sockets + + tlm::tlm_target_socket target_socket; + tlm::tlm_initiator_socket initiator_socket; + + SC_CTOR(tlm2_base_protocol_checker) + : m_request_in_progress(0), m_response_in_progress(0) + { + target_socket .bind( *this ); + initiator_socket.bind( *this ); + } + + + // Access methods for num_checks count + + static void set_num_checks(sc_dt::uint64 n) { + if (num_checks == 0) + SC_REPORT_FATAL("tlm2_protocol_checker", "Method set_num_checks called after checking has stopped due to maximum number of checks being reached"); + num_checks = n; + } + + static sc_dt::uint64 get_num_checks() { return num_checks; } + + + // TLM-2.0 interface methods for initiator and target sockets, instrumented with checks + + virtual tlm::tlm_sync_enum nb_transport_fw( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) + { + tlm::tlm_phase& start_phase = phase; + + if (num_checks) + nb_transport_fw_pre_checks( trans, phase, delay ); + + tlm::tlm_sync_enum status; + status = initiator_socket->nb_transport_fw( trans, phase, delay ); + + if (num_checks) + nb_transport_fw_post_checks( trans, start_phase, phase, delay, status ); + + return status; + } + + virtual tlm::tlm_sync_enum nb_transport_bw( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) + { + if (num_checks) + nb_transport_bw_pre_checks( trans, phase, delay ); + + tlm::tlm_sync_enum status; + status = target_socket->nb_transport_bw( trans, phase, delay ); + + if (num_checks) + nb_transport_bw_post_checks( trans, phase, delay, status ); + + return status; + } + + virtual void b_transport( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay ) + { + if (num_checks) + b_transport_pre_checks( trans, delay ); + + initiator_socket->b_transport( trans, delay ); + + if (num_checks) + b_transport_post_checks( trans, delay ); + } + + virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans, + tlm::tlm_dmi& dmi_data) + { + get_direct_mem_ptr_pre_checks( trans, dmi_data ); + + bool status; + status = initiator_socket->get_direct_mem_ptr( trans, dmi_data ); + return status; + } + + virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range, + sc_dt::uint64 end_range) + { + target_socket->invalidate_direct_mem_ptr(start_range, end_range); + } + + virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans) + { + transport_dbg_pre_checks( trans ); + + unsigned int count; + count = initiator_socket->transport_dbg( trans ); + + transport_dbg_post_checks( trans, count ); + return count; + } + + +private: + void b_transport_pre_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay); + + void b_transport_post_checks( tlm::tlm_generic_payload& trans, sc_core::sc_time& delay); + + void nb_transport_fw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay); + + void nb_transport_fw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase, + sc_core::sc_time& delay, tlm::tlm_sync_enum status); + + void nb_transport_bw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay); + + void nb_transport_bw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + tlm::tlm_sync_enum status); + + void nb_transport_response_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + const char* txt2, const char* txt3, const char* txt4); + + void check_initial_state( tlm::tlm_generic_payload& trans, const char* txt2 ); + void check_trans_not_modified( tlm::tlm_generic_payload& trans, const char* txt2 ); + void check_response_path( tlm::tlm_generic_payload& trans, const char* txt2 ); + + void get_direct_mem_ptr_pre_checks( tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ); + + void transport_dbg_pre_checks( tlm::tlm_generic_payload& trans ); + + void transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count ); + + void tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning = false ); + +private: + + struct state_t { + state_t() { b_call = 0; ph = tlm::UNINITIALIZED_PHASE; gp = 0; } + + bool has_mm; + unsigned int b_call; // Number of b_transport calls in progress + tlm::tlm_phase ph; + sc_core::sc_time time; // Current time + annotated delay + tlm::tlm_generic_payload* gp; // Points to new data and byte enable buffers + uchar_t* data_ptr; // Stores original pointers + uchar_t* byte_enable_ptr; + }; + + // Transaction state for the specific hop where this checker is inlined + std::map m_map; + + // Flags for exclusion rules + tlm::tlm_generic_payload* m_request_in_progress; + tlm::tlm_generic_payload* m_response_in_progress; + + std::ostringstream txt; + +}; + + + +// ******************** MEMBER FUNCTION DEFINITIONS ******************** + + +#define BOILERPLATE \ +template \ +void tlm2_base_protocol_checker:: + + +BOILERPLATE +b_transport_pre_checks( + tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) +{ + ++ m_map[&trans].b_call; + + if ( trans.has_mm() && trans.get_ref_count() == 0) + { + txt << "Transaction passed to b_transport with memory manager and reference count of 0"; + tlm2error(trans, "6.5 s)"); + } + check_initial_state(trans, "b_transport"); + +#if !(defined SYSTEMC_VERSION & SYSTEMC_VERSION <= 20050714) + if (sc_core::sc_get_current_process_handle().proc_kind() == sc_core::SC_METHOD_PROC_) + { + txt << "b_transport called from method process"; + tlm2error(trans, "4.1.1.4 b)"); + } +#endif + + if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4) + { + txt << "b_transport called during a sequence of nb_transport calls"; + tlm2error(trans, "7.2.5 k)"); + } +} + + +BOILERPLATE +b_transport_post_checks( + tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) +{ + check_response_path(trans, "b_transport"); + check_trans_not_modified(trans, "b_transport"); + -- m_map[&trans].b_call; +} + + +BOILERPLATE +nb_transport_fw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) +{ + if ( !trans.has_mm() ) + { + txt << "Transaction passed to nb_transport_fw with no memory manager set"; + tlm2error(trans, "6.5 h)"); + } + if ( trans.get_ref_count() == 0) + { + txt << "Transaction passed to nb_transport_fw with reference count of 0"; + tlm2error(trans, "6.5 s)"); + } + + switch (phase) + { + case tlm::BEGIN_REQ: + check_initial_state(trans, "nb_transport_fw"); + + if (m_map[&trans].ph > 0 && m_map[&trans].ph < 4) // END_RESP -> BEGIN_REQ is legal + { + txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw"; + tlm2error(trans, "7.2.3 i)"); + } + + if (m_request_in_progress) + { + txt << "Transaction violates BEGIN_REQ exclusion rule, detected in nb_transport_fw"; + tlm2error(trans, "7.2.4 b)"); + } + m_request_in_progress = &trans; + + if (m_map[&trans].b_call) + { + txt << "nb_transport_fw called during a b_transport call"; + tlm2error(trans, "7.2.5 k)"); + } + break; + + case tlm::END_REQ: + case tlm::BEGIN_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on forward path, detected in nb_transport_fw"; + tlm2error(trans, "7.2.3 d)"); + break; + + case tlm::END_RESP: + if (m_map[&trans].ph != tlm::BEGIN_RESP) + { + txt << "Phase " << phase << " sent out-of-sequence on forward path, detected in nb_transport_fw"; + tlm2error(trans, "7.2.3 i)"); + } + m_response_in_progress = 0; + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << "nb_transport_fw called with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "7.2.5 e)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; +} + + +BOILERPLATE +nb_transport_fw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& start_phase, tlm::tlm_phase& phase, + sc_core::sc_time& delay, tlm::tlm_sync_enum status) +{ + if (status == tlm::TLM_UPDATED) + { + nb_transport_response_checks( + trans, phase, delay, "(forward) return", "Return from nb_transport_fw", "nb_transport_fw"); + } + else if (status == tlm::TLM_COMPLETED) + { + if (start_phase == tlm::BEGIN_REQ) + check_response_path(trans, "nb_transport_fw"); + m_request_in_progress = 0; + m_map[&trans].ph = tlm::UNINITIALIZED_PHASE; + } + + // Transaction object should not be re-allocated, even during the END_RESP phase + //if (phase != tlm::END_RESP) + { + std::ostringstream txt; + txt << "nb_transport_fw, phase = " << phase; + check_trans_not_modified(trans, txt.str().c_str()); + } +} + + +BOILERPLATE +nb_transport_bw_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) +{ + if ( !trans.has_mm() ) + { + txt << "Transaction passed to nb_transport_bw with no memory manager set"; + tlm2error(trans, "6.5 h)"); + } + if ( trans.get_ref_count() == 0) + { + txt << "Transaction passed to nb_transport_bw with reference count of 0"; + tlm2error(trans, "6.5 s)"); + } + nb_transport_response_checks( + trans, phase, delay, "backward", "nb_transport_bw called", "nb_transport_bw"); +} + + +BOILERPLATE +nb_transport_bw_post_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + tlm::tlm_sync_enum status) +{ + if (status == tlm::TLM_UPDATED) + { + switch (phase) + { + case tlm::BEGIN_REQ: + txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "7.2.3 i)"); + break; + + case tlm::END_REQ: + case tlm::BEGIN_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "7.2.3 d)"); + break; + + case tlm::END_RESP: + if (m_map[&trans].ph != tlm::BEGIN_RESP) + { + txt << "Phase " << phase << " sent out-of-sequence on (backward) return path, detected in nb_transport_bw"; + tlm2error(trans, "7.2.3 i)"); + } + + m_response_in_progress = 0; + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << "Return from nb_transport_bw with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "7.2.5 e)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; + } + else if (status == tlm::TLM_COMPLETED) + { + m_response_in_progress = 0; + m_map[&trans].ph = tlm::UNINITIALIZED_PHASE; + } + + // Transaction object should not be re-allocated, even during the END_RESP phase + //if (phase != tlm::END_RESP) + { + std::ostringstream txt; + txt << "nb_transport_bw, phase = " << phase; + check_trans_not_modified(trans, txt.str().c_str()); + } +} + + +BOILERPLATE +nb_transport_response_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay, + const char* txt2, const char* txt3, const char* txt4) +{ + if (trans.is_response_ok()) + if (shared_map[&trans].response_in_progress && !shared_map[&trans].ok_response) + { + txt << "Interconnect component sets response status attribute to TLM_OK_RESPONSE" + << ", detected in " << txt4; + tlm2error(trans, "6.7)"); + + } + + switch (phase) + { + case tlm::BEGIN_REQ: + case tlm::END_RESP: + case tlm::UNINITIALIZED_PHASE: + txt << "Phase " << phase << " sent on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "7.2.3 d)"); + break; + + case tlm::END_REQ: + if (m_map[&trans].ph != tlm::BEGIN_REQ) + { + txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "7.2.3 i)"); + } + + m_request_in_progress = 0; + break; + + case tlm::BEGIN_RESP: + if (m_map[&trans].ph != tlm::BEGIN_REQ && m_map[&trans].ph != tlm::END_REQ) + { + txt << "Phase " << phase << " sent out-of-sequence on " << txt2 << " path" + << ", detected in " << txt4; + tlm2error(trans, "7.2.3 i)"); + } + + if (&trans == m_request_in_progress) + m_request_in_progress = 0; + + if (m_response_in_progress) + { + txt << "Transaction violates BEGIN_RESP exclusion rule" + << ", detected in " << txt4; + tlm2error(trans, "7.2.4 c)"); + } + m_response_in_progress = &trans; + + check_response_path(trans, txt4); + break; + } + + if (phase < 5) // Ignore extended phases + m_map[&trans].ph = phase; + + if (sc_core::sc_time_stamp() + delay < m_map[&trans].time) + { + txt << txt3 << " with decreasing timing annotation:" + << " delay = " << delay + << ", sc_time_stamp() + delay from previous call = " << m_map[&trans].time; + tlm2error(trans, "7.2.5 e)"); + } + m_map[&trans].time = sc_core::sc_time_stamp() + delay; +} + + +BOILERPLATE +check_initial_state( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + if (num_checks > 0) + { + --num_checks; + if (num_checks == 0) + SC_REPORT_INFO("tlm2_protocol_checker", "Checkers deactivated after executing the set number of checks"); + } + + if ( trans.has_mm() && trans.get_ref_count() > 1 && shared_map[&trans].path.empty() ) + { + txt << "New transaction passed to " << txt2 << " with reference count = " + << trans.get_ref_count(); + tlm2error(trans, "6.5 s)", true); + } + if (trans.get_data_ptr() == 0) + { + txt << "Transaction not properly initialized: data_ptr == 0, detected in " << txt2; + tlm2error(trans, "6.10 e)"); + } + if (trans.get_data_length() == 0) + { + txt << "Transaction not properly initialized: data_langth == 0, detected in " << txt2; + tlm2error(trans, "6.11 d)"); + } + if (trans.get_byte_enable_ptr() != 0 && trans.get_byte_enable_length() == 0) + { + txt << "Transaction not properly initialized: " + << "byte_enable_ptr != 0 and byte_enable_length == 0, detected in " << txt2; + tlm2error(trans, "6.13 f)"); + } + if (trans.get_streaming_width() == 0) + { + txt << "Transaction not properly initialized: streaming_width == 0, detected in " << txt2; + tlm2error(trans, "6.14 f)"); + } + if (trans.is_dmi_allowed()) + { + txt << "Transaction not properly initialized: dmi_allowed == true, detected in " << txt2; + tlm2error(trans, "6.15)"); + } + if (trans.get_response_status() != tlm::TLM_INCOMPLETE_RESPONSE) + { + txt << "Transaction not properly initialized: response_status != TLM_INCOMPLETE_RESPONSE, detected in " << txt2; + tlm2error(trans, "6.16 e)"); + } + + // Setup clones of transaction and buffers in map + tlm::tlm_generic_payload* gp = m_map[&trans].gp; + if (gp == 0) + gp = new tlm::tlm_generic_payload; // Memory leak: transactions are never cleared from map + else + { + delete [] gp->get_data_ptr(); + gp->free_all_extensions(); + } + gp->set_data_ptr( new uchar_t[trans.get_data_length()] ); + m_map[&trans].data_ptr = trans.get_data_ptr(); + + if (gp->get_byte_enable_ptr()) + delete [] gp->get_byte_enable_ptr(); + if (trans.get_byte_enable_ptr()) + gp->set_byte_enable_ptr( new uchar_t[trans.get_byte_enable_length()] ); + else + gp->set_byte_enable_ptr(0); + m_map[&trans].byte_enable_ptr = trans.get_byte_enable_ptr(); + + gp->deep_copy_from(trans); + m_map[&trans].gp = gp; + m_map[&trans].time = sc_core::SC_ZERO_TIME; + m_map[&trans].has_mm = trans.has_mm(); + + // Store request path checker sequence + if (shared_map[&trans].resp_data_ptr) + { + delete [] shared_map[&trans].resp_data_ptr; + shared_map[&trans].resp_data_ptr = 0; + } + if (shared_map[&trans].response_in_progress) + { + txt << "Transaction object sent with BEGIN_REQ while still being used on a previous response path, detected in " << txt2; + tlm2error(trans, "6.5"); + } + shared_map[&trans].ok_response = false; + shared_map[&trans].path.push_back(this); +} + + +BOILERPLATE +check_trans_not_modified( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + tlm::tlm_generic_payload* init = m_map[&trans].gp; + + if (trans.get_command() != init->get_command()) + { + txt << "Command attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_data_ptr() != m_map[&trans].data_ptr) + { + txt << "Data pointer attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_data_length() != init->get_data_length()) + { + txt << "Data length attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_command() == tlm::TLM_WRITE_COMMAND) + for (unsigned int i = 0; i < init->get_data_length(); i++) + if (trans.get_data_ptr()[i] != init->get_data_ptr()[i]) + { + txt << "Data array modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_byte_enable_ptr() != m_map[&trans].byte_enable_ptr) + { + txt << "Byte enable pointer attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_byte_enable_length() != init->get_byte_enable_length()) + { + txt << "Byte enable length attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_byte_enable_ptr()) + for (unsigned int i = 0; i < init->get_byte_enable_length(); i++) + if (trans.get_byte_enable_ptr()[i] != init->get_byte_enable_ptr()[i]) + { + txt << "Byte enable array modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if (trans.get_streaming_width() != init->get_streaming_width()) + { + txt << "Streaming width attribute modified during transaction lifetime, detected in " << txt2; + tlm2error(trans, "6.7)"); + } + if ( !m_map[&trans].has_mm ) + { + if (trans.has_mm()) + { + txt << "Interconnect component sets a memory manager, but does not clear it on return, detected in " << txt2; + tlm2error(trans, "6.5y)"); + } + for (unsigned int i = 0; i < tlm::max_num_extensions(); i++) + if (trans.get_extension(i)) + if ( !m_map[&trans].gp->get_extension(i) ) + { + txt << "Extension set (index = " << i << ") without also being deleted in the absence of a memory manager, detected in " << txt2; + tlm2error(trans, "6.5 y)"); + } + } + + uchar_t* resp_data_ptr = shared_map[&trans].resp_data_ptr; + if (resp_data_ptr) + for (unsigned int i = 0; i < trans.get_data_length(); i++) + if (trans.get_data_ptr()[i] != resp_data_ptr[i]) + { + txt << "Transaction data array modified in interconnect component on response path, detected in " << txt2; + tlm2error(trans, "6.7)"); + } +} + + +BOILERPLATE +check_response_path( + tlm::tlm_generic_payload& trans, const char* txt2 ) +{ + if ( !shared_map[&trans].path.empty() ) + { + if ( this != shared_map[&trans].path.back() ) + { + txt << "BEGIN_RESP path is not the reverse of the BEGIN_REQ path."; + txt << "\nBEGIN_REQ path includes these checkers: -> "; + deque_t path = shared_map[&trans].path; + for (deque_t::iterator i = path.begin(); i < path.end(); i++) + txt << (*i)->name() << " -> "; + txt << "\nDetected in " << txt2; + tlm2error(trans, "7.2.5 m-n)"); + } + shared_map[&trans].path.pop_back(); + shared_map[&trans].response_in_progress = !shared_map[&trans].path.empty(); + shared_map[&trans].ok_response = trans.is_response_ok(); + + // Create a copy of the data array for comparison on the response path + if ( !shared_map[&trans].resp_data_ptr ) + { + shared_map[&trans].resp_data_ptr = new u_char[trans.get_data_length()]; + memcpy(shared_map[&trans].resp_data_ptr, trans.get_data_ptr(), trans.get_data_length()); + } + } +} + + +BOILERPLATE +get_direct_mem_ptr_pre_checks( + tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data ) +{ + if (dmi_data.get_dmi_ptr() != 0) + { + txt << "DMI descriptor not properly initialized: dmi_ptr != 0"; + tlm2error(trans, "4.2.5 f)"); + } + if (!dmi_data.is_none_allowed()) + { + txt << "DMI descriptor not properly initialized: granted_access != DMI_ACCESS_NONE"; + tlm2error(trans, "4.2.5 a)"); + } + if (dmi_data.get_start_address() != 0) + { + txt << "DMI descriptor not properly initialized: start_address != 0"; + tlm2error(trans, "4.2.5 s)"); + } + if (dmi_data.get_end_address() != (sc_dt::uint64)(-1)) + { + txt << "DMI descriptor not properly initialized: end_address != 0"; + tlm2error(trans, "4.2.5 s)"); + } + if (dmi_data.get_read_latency() != sc_core::SC_ZERO_TIME) + { + txt << "DMI descriptor not properly initialized: read_latency != SC_ZERO_TIME"; + tlm2error(trans, "4.2.5 aa)"); + } + if (dmi_data.get_write_latency() != sc_core::SC_ZERO_TIME) + { + txt << "DMI descriptor not properly initialized: write_latency != SC_ZERO_TIME"; + tlm2error(trans, "4.2.5 aa)"); + } +} + + +BOILERPLATE +transport_dbg_pre_checks( tlm::tlm_generic_payload& trans ) +{ + if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0) + { + txt << "Debug transaction has data_ptr == 0"; + tlm2error(trans, "4.3.4 j)"); + } +} + + +BOILERPLATE +transport_dbg_post_checks( tlm::tlm_generic_payload& trans, unsigned int count ) +{ + if (trans.get_data_length() > 0 && trans.get_data_ptr() == 0) + { + txt << "Debug transaction has data_ptr == 0"; + tlm2error(trans, "4.3.4 j)"); + } + if (count > trans.get_data_length()) + { + txt << "Count returned from transport_dbg is greater than data_length"; + tlm2error(trans, "4.3.4 o)"); + } +} + + +BOILERPLATE +tlm2error( tlm::tlm_generic_payload& trans, const char* ref, bool warning ) +{ + txt << "\n\nRefer to TLM-2.0 User Manual, version JA22, clause " << ref; + txt << "\n\nChecker instance: " << this->name(); + txt << "\n\nTransaction details:"; + txt << "\n has_mm = " << dec << trans.has_mm() << " (bool)"; + txt << "\n ref_count = " << dec << trans.get_ref_count() << " (int)"; + txt << "\n\n command = " << + (trans.get_command() == tlm::TLM_READ_COMMAND ? "TLM_READ_COMMAND" + :trans.get_command() == tlm::TLM_WRITE_COMMAND ? "TLM_WRITE_COMMAND" + : "TLM_IGNORE_COMMAND"); + txt << "\n address = " << hex << trans.get_address() << " (hex)"; + txt << "\n data_ptr = " << hex + << reinterpret_cast(trans.get_data_ptr()) << " (hex)"; + txt << "\n data_length = " << hex << trans.get_data_length() << " (hex)"; + txt << "\n streaming_width = " << hex << trans.get_streaming_width() << " (hex)"; + txt << "\n byte_enable_ptr = " << hex + << reinterpret_cast(trans.get_byte_enable_ptr()) << " (hex)"; + txt << "\n byte_enable_length = " << hex << trans.get_byte_enable_length() << " (hex)"; + txt << "\n dmi_allowed = " << dec << trans.is_dmi_allowed() << " (bool)"; + txt << "\n response_status = " << trans.get_response_string(); + + bool extensions_present = false; + for (unsigned int i = 0; i < tlm::max_num_extensions(); i++) + { + tlm::tlm_extension_base* ext = trans.get_extension(i); + if (ext) + { + if (!extensions_present) + txt << "\n\n extensions:"; + txt << "\n index = " << i << " type = " << typeid(*ext).name(); + extensions_present = true; + } + } + + txt << "\n\n"; + if (warning) + SC_REPORT_WARNING("tlm2_protocol_checker", txt.str().c_str()); + else + SC_REPORT_ERROR("tlm2_protocol_checker", txt.str().c_str()); +} + + + +} // namespace tlm_utils + +#endif // __tlm2_base_protocol_checker__ diff --git a/dram/src/common/tlmDBPhaseRecorder.cpp b/dram/src/common/tlmDBPhaseRecorder.cpp new file mode 100644 index 00000000..9cc4cc0c --- /dev/null +++ b/dram/src/common/tlmDBPhaseRecorder.cpp @@ -0,0 +1,242 @@ +#include "tlmDBPhaseRecorder.h" +#include "protocol.h" +#include "dramExtension.h" +#include "xmlAddressdecoder.h" +#include + +using namespace std; + +tlmDBPhaseRecorder::tlmDBPhaseRecorder(string name, string pathToConfigs) : + transactionIDCounter(1), PicosecondsPerNanosecond(1e3), pathToConfigs(pathToConfigs) +{ + openDB(name.c_str()); + createTables(); + cout << "Created tables in file " << name << std::endl; + setUpTransactionTerminatingPhases(); + cout << "Set up terminating phases " << name << std::endl; + prepareSqlStatements(); + cout << "Prepared statements " << name << std::endl; + sqlite3_exec(db, "BEGIN", 0, 0, 0); + cout << "Started new transaction " << name << std::endl; +} + +tlmDBPhaseRecorder::~tlmDBPhaseRecorder() +{ + insertGeneralInfo(); + sqlite3_exec(db, "COMMIT", 0, 0, 0); + cout << "Number of transactions written to DB: " << transactionIDCounter - 1 << endl; + cout << "tlmPhaseRecorder:\tEnd Recording" << endl; + sqlite3_close(db); +} + +void tlmDBPhaseRecorder::recordPhase(tlm::tlm_generic_payload& trans, tlm::tlm_phase phase, + sc_time time) +{ + time /= PicosecondsPerNanosecond; + + if (currentTransactionsInSystem.count(&trans) == 0) + introduceNewTransactionToSystem(time, trans); + + unsigned int id = currentTransactionsInSystem[&trans]; + + string phaseName = phaseToString(phase); + string phaseBeginPrefix = "BEGIN_"; + string phaseEndPrefix = "END_"; + + if (phaseName.find(phaseBeginPrefix) != string::npos) + insertPhaseInDB(phaseName, time, id); + else + updatePhaseEndInDB(phaseName, time, id); + + bool phaseTerminatesTransaction = count(transactionTerminatingPhases.begin(), + transactionTerminatingPhases.end(), phase) == 1; + if (phaseTerminatesTransaction) + removeTransactionFromSystem(time, id, trans); + + recordingEndTime = time; +} + +void tlmDBPhaseRecorder::recordDebugMessage(std::string message, sc_time time) +{ + time /= PicosecondsPerNanosecond; + insertDebugMessageInDB(message, time); +} + +void tlmDBPhaseRecorder::createTables() +{ + string initial = getFileContents(pathToConfigs + string("/createTraceDB.sql")); + executeSqlCommand(initial); +} + +void tlmDBPhaseRecorder::prepareSqlStatements() +{ + insertTransactionString = + "INSERT INTO Transactions VALUES (:id,:rangeID,:address,:thread,:channel,:bank,:row,:column,:command)"; + insertRangeString = "INSERT INTO Ranges VALUES (:id,:begin,:end)"; + updateRangeString = "UPDATE Ranges SET End = :end WHERE ID = :id"; + insertPhaseString = + "INSERT INTO Phases (PhaseName,PhaseBegin,PhaseEnd,Transact) VALUES (:name,:begin,:end,:transaction)"; + updatePhaseString = + "UPDATE Phases SET PhaseEnd = :end WHERE Transact = :trans AND PhaseName = :name"; + insertGeneralInfoString = + "INSERT INTO GeneralInfo (NumberOfTransactions,TraceEnd,NumberOfBanks,description,clk,UnitOfTime) Values (:numberOfTransactions,:end,:numberOfBanks,:description,:clk,:unitOfTime)"; + insertDebugMessageString = "INSERT INTO DebugMessages (Time,Message) Values (:time,:message)"; + + sqlite3_prepare(db, insertTransactionString.c_str(), -1, &insertTransactionStatement, 0); + sqlite3_prepare(db, insertRangeString.c_str(), -1, &insertRangeStatement, 0); + sqlite3_prepare(db, updateRangeString.c_str(), -1, &updateRangeStatement, 0); + sqlite3_prepare(db, insertPhaseString.c_str(), -1, &insertPhaseStatement, 0); + sqlite3_prepare(db, updatePhaseString.c_str(), -1, &updatePhaseStatement, 0); + sqlite3_prepare(db, insertGeneralInfoString.c_str(), -1, &insertGeneralInfoStatement, 0); + sqlite3_prepare(db, insertDebugMessageString.c_str(), -1, &insertDebugMessageStatement, 0); +} + +void tlmDBPhaseRecorder::setUpTransactionTerminatingPhases() +{ + transactionTerminatingPhases.push_back(tlm::END_RESP); + transactionTerminatingPhases.push_back(static_cast(END_REFA)); + transactionTerminatingPhases.push_back(static_cast(END_REFB)); + transactionTerminatingPhases.push_back(static_cast(END_PDNP)); + transactionTerminatingPhases.push_back(static_cast(END_PDNA)); + transactionTerminatingPhases.push_back(static_cast(END_SREF)); +} + +void tlmDBPhaseRecorder::openDB(std::string name) +{ + if (sqlite3_open(name.c_str(), &db)) + { + cout << "ERROR Cannot open DB" << endl; + sqlite3_close(db); + } +} + +void tlmDBPhaseRecorder::insertDebugMessageInDB(string message, const sc_time& time) +{ + sqlite3_bind_int64(insertDebugMessageStatement, 1, time.value()); + sqlite3_bind_text(insertDebugMessageStatement, 2, message.c_str(), message.length(), 0); + executeSqlStatement(insertDebugMessageStatement); +} + +void tlmDBPhaseRecorder::insertGeneralInfo() +{ + sqlite3_bind_int64(insertGeneralInfoStatement, 1, transactionIDCounter - 1); + sqlite3_bind_int64(insertGeneralInfoStatement, 2, recordingEndTime.value()); + sqlite3_bind_int(insertGeneralInfoStatement, 3, xmlAddressDecoder::getInstance().getNumberOfBanks()); + sqlite3_bind_text(insertGeneralInfoStatement, 4, "", 0, NULL); + sqlite3_bind_int(insertGeneralInfoStatement, 5, 6); + sqlite3_bind_text(insertGeneralInfoStatement, 6, "NS", 2, NULL); + executeSqlStatement(insertGeneralInfoStatement); +} +void tlmDBPhaseRecorder::insertTransactionInDB(unsigned int id, tlm::tlm_generic_payload& trans) +{ + sqlite3_bind_int(insertTransactionStatement, 1, id); + sqlite3_bind_int(insertTransactionStatement, 2, id); + sqlite3_bind_int(insertTransactionStatement, 3, trans.get_address()); + sqlite3_bind_text(insertTransactionStatement, 9, + trans.get_command() == tlm::TLM_READ_COMMAND ? "R" : "W", 1, 0); + + const DramExtension& extension = DramExtension::getExtension(trans); + sqlite3_bind_int(insertTransactionStatement, 4, extension.getThread().ID()); + sqlite3_bind_int(insertTransactionStatement, 5, extension.getChannel().ID()); + sqlite3_bind_int(insertTransactionStatement, 6, extension.getBank().ID()); + sqlite3_bind_int(insertTransactionStatement, 7, extension.getRow().ID()); + sqlite3_bind_int(insertTransactionStatement, 8, extension.getColumn().ID()); + + executeSqlStatement(insertTransactionStatement); +} +void tlmDBPhaseRecorder::insertRangeInDB(unsigned int id, const sc_time& time) +{ + sqlite3_bind_int(insertRangeStatement, 1, id); + sqlite3_bind_int64(insertRangeStatement, 2, time.value()); + sqlite3_bind_int64(insertRangeStatement, 3, time.value()); + executeSqlStatement(insertRangeStatement); +} +void tlmDBPhaseRecorder::insertPhaseInDB(string phaseName, const sc_time& time, unsigned int id) +{ + string phaseBeginPrefix = "BEGIN_"; + phaseName.erase(0, phaseBeginPrefix.length()); + sqlite3_bind_text(insertPhaseStatement, 1, phaseName.c_str(), phaseName.length(), 0); + sqlite3_bind_int64(insertPhaseStatement, 2, time.value()); + sqlite3_bind_int64(insertPhaseStatement, 3, time.value()); + sqlite3_bind_int(insertPhaseStatement, 4, id); + executeSqlStatement(insertPhaseStatement); +} + +void tlmDBPhaseRecorder::updatePhaseEndInDB(string phaseName, const sc_time& time, unsigned int id) +{ + string phaseEndPrefix = "END_"; + phaseName.erase(0, phaseEndPrefix.length()); + sqlite3_bind_int64(updatePhaseStatement, 1, time.value()); + sqlite3_bind_int(updatePhaseStatement, 2, id); + sqlite3_bind_text(updatePhaseStatement, 3, phaseName.c_str(), phaseName.length(), 0); + executeSqlStatement(updatePhaseStatement); +} + +void tlmDBPhaseRecorder::introduceNewTransactionToSystem(const sc_time& time, + tlm::tlm_generic_payload& trans) +{ + unsigned int id = transactionIDCounter++; + currentTransactionsInSystem[&trans] = id; + insertTransactionInDB(id, trans); + insertRangeInDB(id, time); + if (id % transactionCommitRate == 0) + { + sqlite3_exec(db, "COMMIT", 0, 0, 0); + cout << "Committing trasaction nr. " << id << " (and previous) to database" << endl; + sqlite3_exec(db, "BEGIN", 0, 0, 0); + } +} +void tlmDBPhaseRecorder::removeTransactionFromSystem(const sc_time& time, unsigned int id, + tlm::tlm_generic_payload& trans) +{ + currentTransactionsInSystem.erase(&trans); + sqlite3_bind_int64(updateRangeStatement, 1, time.value()); + sqlite3_bind_int(updateRangeStatement, 2, id); + executeSqlStatement(updateRangeStatement); +} + +void tlmDBPhaseRecorder::executeSqlStatement(sqlite3_stmt* statement) +{ + if (sqlite3_step(statement) != SQLITE_DONE) + { + cout << "Could not execute statement" << endl; + } + sqlite3_reset(statement); +} +void tlmDBPhaseRecorder::executeSqlCommand(string command) +{ + cout << "executing sql-script" << endl; + + char * errMsg = 0; + int rc = sqlite3_exec(db, command.c_str(), NULL, 0, &errMsg); + if (rc != SQLITE_OK) + { + cout << errMsg << endl; + SC_REPORT_FATAL("SQLITE Error", errMsg); + sqlite3_free(errMsg); + } + + cout << "done executing script" << endl; +} +string tlmDBPhaseRecorder::getFileContents(string filename) +{ + ifstream in(filename.c_str(), ios::in | ios::binary); + if (in) + { + string contents; + in.seekg(0, ios::end); + contents.resize(in.tellg()); + in.seekg(0, ios::beg); + in.read(&contents[0], contents.size()); + in.close(); + return (contents); + } + throw(errno); +} +string tlmDBPhaseRecorder::phaseToString(tlm::tlm_phase phase) +{ + ostringstream oss; + oss << phase; + string str = oss.str(); + return str; +} diff --git a/dram/src/common/tlmDBPhaseRecorder.h b/dram/src/common/tlmDBPhaseRecorder.h new file mode 100755 index 00000000..689b7b0b --- /dev/null +++ b/dram/src/common/tlmDBPhaseRecorder.h @@ -0,0 +1,64 @@ +#ifndef TLMPHASERECORDER_H +#define TLMPHASERECORDER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xmlAddressdecoder.h" + +using namespace std; + +class tlmDBPhaseRecorder +{ +public: + + tlmDBPhaseRecorder(std::string name, std::string pathToConfigs); + ~tlmDBPhaseRecorder(); + + void recordPhase(tlm::tlm_generic_payload &trans, tlm::tlm_phase phase, sc_time time); + void recordDebugMessage(std::string message, sc_time time); + +private: + std::string phaseToString(tlm::tlm_phase phase); + std::string getFileContents(std::string filename); + + void executeSqlCommand(std::string command); + void executeSqlStatement(sqlite3_stmt* statement); + void prepareSqlStatements(); + void openDB(std::string name); + void setUpTransactionTerminatingPhases(); + + void introduceNewTransactionToSystem(const sc_time& time, tlm::tlm_generic_payload& trans); + void removeTransactionFromSystem(const sc_time& time, unsigned int transactionID, tlm::tlm_generic_payload& trans); + + void createTables(); + void insertGeneralInfo(); + void insertTransactionInDB(unsigned int transactionID, tlm::tlm_generic_payload& trans); + void insertRangeInDB(unsigned int transactionID, const sc_time& time); + void insertPhaseInDB(string phaseName, const sc_time& time, unsigned int transactionID); + void updatePhaseEndInDB(string phaseName, const sc_time& time, unsigned int transactionID); + void insertDebugMessageInDB(string message, const sc_time& time); + + static const int transactionCommitRate = 10000; + map currentTransactionsInSystem; + unsigned int transactionIDCounter; + sc_time recordingEndTime; + std::vector transactionTerminatingPhases; + sqlite3 *db; + sqlite3_stmt *insertTransactionStatement, *insertRangeStatement, *updateRangeStatement, + *insertPhaseStatement, *updatePhaseStatement, *insertGeneralInfoStatement, *insertDebugMessageStatement; + std::string insertTransactionString, insertRangeString, updateRangeString, insertPhaseString, updatePhaseString, insertGeneralInfoString, + insertDebugMessageString; + + double PicosecondsPerNanosecond; + std::string pathToConfigs; +}; + +#endif diff --git a/dram/src/common/xmlAddressdecoder.cpp b/dram/src/common/xmlAddressdecoder.cpp new file mode 100644 index 00000000..be6fd638 --- /dev/null +++ b/dram/src/common/xmlAddressdecoder.cpp @@ -0,0 +1,131 @@ +#include "xmlAddressdecoder.h" +#include + +using namespace std; + +string xmlAddressDecoder::URI = ""; + +xmlAddressDecoder::xmlAddressDecoder(string URI) +{ + doc = new TiXmlDocument(URI.c_str()); + if (doc->LoadFile()) + { + dramconfig = doc->FirstChildElement("dramconfig"); + addressmap = dramconfig->FirstChildElement("addressmap"); + + unsigned int from; + unsigned int to; + + // get channel: + TiXmlElement* channel = addressmap->FirstChildElement("channel"); + + from = getAttribute(channel, "from"); + to = getAttribute(channel, "to"); + + channelShift = from; + channelMask = pow(2.0, to + 1.0) - pow(2.0, from + 0.0); + channelSize = pow(2.0, to - from + 1.0); + + // get row: + TiXmlElement* row = addressmap->FirstChildElement("row"); + + from = getAttribute(row, "from"); + to = getAttribute(row, "to"); + + rowShift = from; + rowMask = pow(2.0, to + 1.0) - pow(2.0, from + 0.0); + rowSize = pow(2.0, to - from + 1.0); + + // get bank: + TiXmlElement* bank = addressmap->FirstChildElement("bank"); + + from = getAttribute(bank, "from"); + to = getAttribute(bank, "to"); + + bankShift = from; + bankMask = pow(2.0, to + 1.0) - pow(2.0, from + 0.0); + bankSize = pow(2.0, to - from + 1.0); + + // get colum: + TiXmlElement* colum = addressmap->FirstChildElement("colum"); + + from = getAttribute(colum, "from"); + to = getAttribute(colum, "to"); + + columShift = from; + columMask = pow(2.0, to + 1.0) - pow(2.0, from + 0.0); + columSize = pow(2.0, to - from + 1.0); + + // get bytes: + TiXmlElement* bytes = addressmap->FirstChildElement("bytes"); + + from = getAttribute(bytes, "from"); + to = getAttribute(bytes, "to"); + + bytesShift = from; + bytesMask = pow(2.0, to + 1.0) - pow(2.0, from + 0.0); + bytesSize = pow(2.0, to - from + 1.0); + } + else + { + SC_REPORT_ERROR("xmlAddressDecoder can not find ", URI.c_str()); + } +} + +xmlAddressDecoder& xmlAddressDecoder::getInstance() +{ + static xmlAddressDecoder decoder(xmlAddressDecoder::URI); + return decoder; +} + +xmlAddressDecoder::~xmlAddressDecoder() +{ + delete doc; +} + +void xmlAddressDecoder::getNode(unsigned int addr, node * n) +{ + n->channel = (addr & channelMask) >> channelShift; + n->row = (addr & rowMask) >> rowShift; + n->bank = (addr & bankMask) >> bankShift; + n->colum = (addr & columMask) >> columShift; +} + +void xmlAddressDecoder::getBRC(unsigned int addr, unsigned int &bank, unsigned int &row, + unsigned int &colum) +{ + row = (addr & rowMask) >> rowShift; + bank = (addr & bankMask) >> bankShift; + colum = (addr & columMask) >> columShift; +} + +void xmlAddressDecoder::getCBRC(unsigned int addr, unsigned int &channel, unsigned int &bank, + unsigned int &row, unsigned int &colum) +{ + channel = (addr & channelMask) >> channelShift; + getBRC(addr, bank, row, colum); +} +void xmlAddressDecoder::getC(unsigned int addr, unsigned int &channel) +{ + channel = (addr & channelMask) >> channelShift; +} + +unsigned int xmlAddressDecoder::getNumberOfBanks() +{ + return bankSize; +} + +unsigned int xmlAddressDecoder::getNumberOfRowsPerBank() +{ + return rowSize; +} + +unsigned int xmlAddressDecoder::getNumberOfColumsPerRow() +{ + return columSize; +} + +unsigned int xmlAddressDecoder::getNumberOfBytesPerColumn() +{ + return bytesSize; +} diff --git a/dram/src/common/xmlAddressdecoder.h b/dram/src/common/xmlAddressdecoder.h new file mode 100755 index 00000000..cb4b0291 --- /dev/null +++ b/dram/src/common/xmlAddressdecoder.h @@ -0,0 +1,87 @@ +#ifndef _XMLADDRESSDECODER_H +#define _XMLADDRESSDECODER_H +// Copyright (C) 2011 University of Kaiserslautern +// Microelectronic System Design Research Group +// +// de.uni-kl.eit.ems.vp +// +// Matthias Jung 2012 +// +/// \file +/// \brief XML address decoder +/// \author Matthias Jung +/// \date 02.07.2012 +// + + +#include +#include +#include +#include +#include + +#include "third_party/tinyxml.h" + +struct node +{ + unsigned int channel; + unsigned int row; + unsigned int bank; + unsigned int colum; + tlm::tlm_command command; + tlm::tlm_phase phase; +}; + +class xmlAddressDecoder +{ + +public: + static std::string URI; + static xmlAddressDecoder& getInstance(); + + void getNode(unsigned int addr, node * n); + void getBRC(unsigned int addr, unsigned int &bank, unsigned int &row, unsigned int &colum); + void getCBRC(unsigned int addr, unsigned int &channel, unsigned int &bank, unsigned int &row, unsigned int &colum); + void getC(unsigned int addr, unsigned int &channel); + unsigned int getNumberOfBanks(); + unsigned int getNumberOfRowsPerBank(); + unsigned int getNumberOfColumsPerRow(); + unsigned int getNumberOfBytesPerColumn(); + + private: + xmlAddressDecoder(std::string URI); + ~xmlAddressDecoder(); + + unsigned int channelMask; + unsigned int rowMask; + unsigned int bankMask; + unsigned int columMask; + unsigned int bytesMask; + + unsigned int channelShift; + unsigned int rowShift; + unsigned int bankShift; + unsigned int columShift; + unsigned int bytesShift; + + unsigned int channelSize; + unsigned int bankSize; + unsigned int rowSize; + unsigned int columSize; + unsigned int bytesSize; + + TiXmlDocument * doc; + TiXmlElement * dramconfig; + TiXmlElement * addressmap; + + template + T getAttribute(TiXmlElement * element, const std::string s) + { + T d; + element->QueryValueAttribute(s, &d); + return d; + } +}; + + +#endif diff --git a/dram/src/common/xmlConfig.h b/dram/src/common/xmlConfig.h new file mode 100755 index 00000000..b1533af7 --- /dev/null +++ b/dram/src/common/xmlConfig.h @@ -0,0 +1,99 @@ +#ifndef CONFIH_H +#define CONFIH_H + +#include +#include +#include "third_party/tinyxml.h" + + +using namespace std; + +class xmlConfig +{ + public: + + sc_time clk; + sc_time tRRD; + sc_time tRC; + sc_time tRCD; + sc_time tBL; + sc_time tRL; + sc_time tWL; + sc_time tWTR; + sc_time tRP; + sc_time tRAS; + sc_time tWR; + sc_time tREF; + sc_time tRFC; + sc_time tXP; + sc_time tCKE; + sc_time tXSR; + sc_time tCKESR; + sc_time tREFA; + sc_time tREFB; + sc_time tPDNTO; + + double IDD0; + double IDD2N; + double IDD3N; + double IDD4R; + double IDD4W; + double IDD5; + double IDD6; + double IDD5B1; + double IDD2P; + double IDD3P; + double VDD; + + sc_time ccPreprocessingTime; + + public: + xmlConfig() + { + //clk = sc_time(6.0, SC_NS); // 166MHz + clk = sc_time(6.0, SC_NS); // 166MHz + + // Timings: + // WC timings for 200MHz + // before thermal: + tRRD = 2 * clk; // 2 * clk; // 4 * clk;//1 * clk; //2 * clk; + tRCD = 3 * clk; // 2 * clk; // 10 * clk;//3 * clk; //4 * clk; + tRL = 3 * clk; // 3 * clk; // 10 * clk;//3 * clk; //3 * clk; + tBL = 1 * clk; // 1 * clk; // 1 * clk;//1 * clk; //1 * clk; + tWL = 1 * clk; // 1 * clk; // 9 * clk;//1 * clk; //1 * clk; + tWTR = 3 * clk; // 3 * clk; // 4 * clk;//3 * clk; //3 * clk; + tRP = 3 * clk; // 3 * clk; // 10 * clk;//3 * clk; //4 * clk; // sadri changed from 2 to 3 = 18ns/6ns + tRAS = 6 * clk; // 5 * clk; // 18 * clk;//8 * clk; //9 * clk; + tWR = 2 * clk; // 2 * clk; // 10 * clk;//2 * clk; //3 * clk; + tRFC = 18 * clk; // 22 * clk; // 15 * clk; // 110* clk;//18 * clk; // sadri changed from 15 to 22 = 130ns/6ns + tXP = 2 * clk; + tCKE = 3 * clk; + tXSR = tRFC + 2 * clk; + tCKESR = 3 * clk; + tPDNTO = 0 * clk; + tRC = tRP + tRAS; + tREF = sc_time(64, SC_MS); + tREFA = tRP + tRFC; + tREFB = tRP + tRC; // refresh for one bank + + // Power realted currents and voltages: + // 166MHz thermal non minimal timings (200MHz) + // 166MHz with minimal timing + IDD0 = 37.85 / 1000.0; // 47.3 / 1000.0; //64 / 1000.0; + IDD2N = 4.36 / 1000.0; // 4.36 / 1000.0; //4.4 / 1000.0; + IDD3N = 5.60 / 1000.0; // 6.07 / 1000.0; //5.9 / 1000.0; + IDD4R = 94.64 / 1000.0; // 94.65 / 1000.0; //94 / 1000.0; + IDD4W = 88.65 / 1000.0; // 88.66 / 1000.0; //88 / 1000.0; + IDD5 = 136.20 / 1000.0; // 162.56 / 1000.0; //163 / 1000.0; + IDD5B1 = 37.32 / 1000.0; // + IDD2P = 2.4 / 1000.0; // + IDD3P = 3.6 / 1000.0; // + IDD6 = 3.4 / 1000.0; // + + VDD = 1.2; + + ccPreprocessingTime = 1 * clk; + } +}; + +#endif diff --git a/dram/src/core/BankStates.h b/dram/src/core/BankStates.h index 824a694d..7b741796 100644 --- a/dram/src/core/BankStates.h +++ b/dram/src/core/BankStates.h @@ -8,7 +8,7 @@ #ifndef BANKSTATES_H_ #define BANKSTATES_H_ #include -#include "common/dramExtension.h" +#include "../common/dramExtension.h" namespace core { diff --git a/dram/src/core/CommandBus.cpp b/dram/src/core/CommandBus.cpp index 57b27e8e..c91c668d 100644 --- a/dram/src/core/CommandBus.cpp +++ b/dram/src/core/CommandBus.cpp @@ -12,32 +12,12 @@ namespace core { void CommandBus::schedule(ScheduledCommand& command) { - + std::cout<::iterator it = state.pendingBusCommands.begin(); @@ -81,55 +63,8 @@ void CommandBus::cleanUpBus(sc_time currentTime) state.pendingBusCommands.lower_bound(currentTime)); } -void CommandBus::changeControllerState(const ScheduledCommand& command) -{ - switch (command.getCommand()) - { - case Refresh: - refresh(command); - break; - case Activate: - activate(command); - break; - case Precharge: - precharge(command); - default: - break; - } -} -void CommandBus::refresh(const ScheduledCommand& command) -{ - if (config.RefreshBankwise) - { - state.bankStates.closeRowBuffer(command.getBank()); - } - else - { - state.bankStates.closeAllRowBuffers(); - } -} - -void CommandBus::precharge(const ScheduledCommand& command) -{ - if (command.getCommand() == Precharge) - { - state.bankStates.closeRowBuffer(command.getBank()); - } - else if (command.getCommand() == PrechargeAll) - { - state.bankStates.closeAllRowBuffers(); - } -} - -void CommandBus::activate(const ScheduledCommand& command) -{ - if (command.getCommand() == Activate) - { - state.bankStates.openRowInRowBuffer(command.getBank(), command.getRow()); - } -} } /* namespace controller */ diff --git a/dram/src/core/Controller.cpp b/dram/src/core/Controller.cpp index caac221e..0c98720a 100644 --- a/dram/src/core/Controller.cpp +++ b/dram/src/core/Controller.cpp @@ -16,7 +16,7 @@ namespace core { DramController::DramController(IWrapperConnector& wrapperConnector): config(),state(config.numberOfBanks, config.nActivate), savedState(config.numberOfBanks, config.nActivate), commandSequenceGenerator(state), commandChecker(), commandSequenceScheduler( - bus, commandChecker), bus(wrapperConnector, config, state), refreshManager(bus, config.Timings.refreshTimings[0]) + bus, commandChecker), bus(wrapperConnector, config, state), refreshManager(bus, config.Timings.refreshTimings) { commandChecker[Activate] = new ActivateChecker(config, state); diff --git a/dram/src/core/Controller.h b/dram/src/core/Controller.h index ad7eaec1..9316403b 100644 --- a/dram/src/core/Controller.h +++ b/dram/src/core/Controller.h @@ -14,7 +14,7 @@ #include "CommandBus.h" #include "Configuration.h" #include "powerdown/PowerDownManager.h" -#include "refresh/RefreshManager.h" +#include "refresh/BankwiseRefreshManager.h" #include "scheduling/CommandSequenceGenerator.h" #include "scheduling/checker/ICommandChecker.h" #include "scheduling/CommandSequenceScheduler.h" @@ -47,7 +47,7 @@ private: std::map commandChecker; CommandSequenceScheduler commandSequenceScheduler; CommandBus bus; - RefreshManager refreshManager; + BankwiseRefreshManager refreshManager; //std::vector allCommandChecker; //PowerDownManager powerDownManager; diff --git a/dram/src/core/ControllerState.cpp b/dram/src/core/ControllerState.cpp index 77876747..e019677d 100644 --- a/dram/src/core/ControllerState.cpp +++ b/dram/src/core/ControllerState.cpp @@ -40,4 +40,40 @@ const ScheduledCommand ControllerState::getLastCommand(Bank bank) return lastCommand; } +void ControllerState::change(const ScheduledCommand& scheduledCommand) +{ + pendingBusCommands.insert(scheduledCommand.getStart()); + lastCommandsOnBus[scheduledCommand.getCommand()][scheduledCommand.getBank()] = scheduledCommand; + lastCommandsOnBus[scheduledCommand.getCommand()][scheduledCommand.getBank()].invalidateTransaction(); + + //TODO do in refresh manager + /*if(scheduledCommand.getCommand() == Refresh) + { + for(unsigned int i = 0; i < bankStates.getNumberOfBanks(); ++i) + { + lastCommandsOnBus[scheduledCommand.getCommand()][Bank(i)] = scheduledCommand; + } + }*/ + + + + switch (scheduledCommand.getCommand()) + { + case Refresh: + bankStates.closeRowBuffer(scheduledCommand.getBank()); + break; + case Activate: + bankStates.openRowInRowBuffer(scheduledCommand.getBank(), scheduledCommand.getRow()); + nActivateWindow.put(scheduledCommand.getStart()); + break; + case Precharge: + bankStates.closeRowBuffer(scheduledCommand.getBank()); + break; + default: + break; + } + +} + } /* namespace controller */ + diff --git a/dram/src/core/ControllerState.h b/dram/src/core/ControllerState.h index c12d1b01..d37b627e 100644 --- a/dram/src/core/ControllerState.h +++ b/dram/src/core/ControllerState.h @@ -27,6 +27,8 @@ public: const ScheduledCommand getLastCommand(Command command); const ScheduledCommand getLastCommand(Bank bank); + void change(const ScheduledCommand& scheduledCommand); + BankStates bankStates; //used by the various checkers diff --git a/dram/src/core/refresh/BankwiseRefreshManager.h b/dram/src/core/refresh/BankwiseRefreshManager.h index 1b089484..a6c56a7a 100644 --- a/dram/src/core/refresh/BankwiseRefreshManager.h +++ b/dram/src/core/refresh/BankwiseRefreshManager.h @@ -9,7 +9,7 @@ #define BANKWISEREFRESHMANAGER_H_ #include "IRefreshManager.h" -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" #include "RefreshManager.h" namespace core { diff --git a/dram/src/core/refresh/IRefreshManager.h b/dram/src/core/refresh/IRefreshManager.h index 63c662e9..083a8af4 100644 --- a/dram/src/core/refresh/IRefreshManager.h +++ b/dram/src/core/refresh/IRefreshManager.h @@ -21,7 +21,14 @@ class IRefreshManager public: virtual ~IRefreshManager() {} + /* + *Checks whether a schedule collides with the next planned refresh + */ virtual bool hasCollision(const CommandSchedule& schedule) = 0; + + /* + *Schedules the next planned refresh if it's start time is not in the past + */ virtual void scheduleRefresh(sc_time time) = 0; }; diff --git a/dram/src/core/refresh/RefreshManager.cpp b/dram/src/core/refresh/RefreshManager.cpp index 46f28298..aeafe791 100644 --- a/dram/src/core/refresh/RefreshManager.cpp +++ b/dram/src/core/refresh/RefreshManager.cpp @@ -12,18 +12,14 @@ namespace core { -RefreshManager::RefreshManager(CommandBus& bus, const RefreshTiming& refreshTiming) : bus(bus), - refreshTiming(refreshTiming) +RefreshManager::RefreshManager(CommandBus& bus, const RefreshTiming& refreshTiming + ) : bus(bus), refreshTiming(refreshTiming) { setupTransaction(refreshTransaction, Bank(0)); nextPlannedRefresh = new ScheduledCommand(refreshTransaction, Refresh, SC_ZERO_TIME, refreshTiming.tRFC); - sc_assert(refreshTiming.tRFC > SC_ZERO_TIME); - sc_assert(nextPlannedRefresh->getExecutionTime()>SC_ZERO_TIME); - planNextRefresh(*nextPlannedRefresh); } - RefreshManager::RefreshManager(CommandBus& bus, const RefreshTiming& refreshTiming, Bank bank) : bus(bus), refreshTiming(refreshTiming) { @@ -58,7 +54,7 @@ bool RefreshManager::hasCollision(const CommandSchedule& schedule) void RefreshManager::scheduleRefresh(sc_time time) { - if (time <= nextPlannedRefresh->getStart()) + if (time == nextPlannedRefresh->getStart()) scheduleRefresh(*nextPlannedRefresh); } diff --git a/dram/src/core/refresh/RefreshManager.h b/dram/src/core/refresh/RefreshManager.h index c406f99d..7084b2c5 100644 --- a/dram/src/core/refresh/RefreshManager.h +++ b/dram/src/core/refresh/RefreshManager.h @@ -9,7 +9,7 @@ #define REFRESHMANAGER_H_ #include "IRefreshManager.h" -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" namespace core { diff --git a/dram/src/core/scheduling/CommandSchedule.h b/dram/src/core/scheduling/CommandSchedule.h index 3611372a..48b6be69 100644 --- a/dram/src/core/scheduling/CommandSchedule.h +++ b/dram/src/core/scheduling/CommandSchedule.h @@ -9,7 +9,7 @@ #define COMMANDSCHEDULE_H_ #include -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" #include "ScheduledCommand.h" namespace core { diff --git a/dram/src/core/scheduling/CommandSequenceGenerator.cpp b/dram/src/core/scheduling/CommandSequenceGenerator.cpp index b8508306..ababbbfd 100644 --- a/dram/src/core/scheduling/CommandSequenceGenerator.cpp +++ b/dram/src/core/scheduling/CommandSequenceGenerator.cpp @@ -6,7 +6,7 @@ */ #include "CommandSequenceGenerator.h" -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" using namespace std; diff --git a/dram/src/core/scheduling/ScheduledCommand.h b/dram/src/core/scheduling/ScheduledCommand.h index 8b0e2530..1cfee85d 100644 --- a/dram/src/core/scheduling/ScheduledCommand.h +++ b/dram/src/core/scheduling/ScheduledCommand.h @@ -11,7 +11,7 @@ #include #include #include "../Command.h" -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" namespace core { diff --git a/dram/src/core/scheduling/checker/ActivateChecker.cpp b/dram/src/core/scheduling/checker/ActivateChecker.cpp index 05c1f89a..e519c6af 100644 --- a/dram/src/core/scheduling/checker/ActivateChecker.cpp +++ b/dram/src/core/scheduling/checker/ActivateChecker.cpp @@ -21,7 +21,7 @@ void ActivateChecker::delayToSatisfyConstraints(ScheduledCommand& command) const if(lastCommandOnBank.isValidCommand()) //implies precharge to activate (tRP) and refresh to activate (tFRC) - sc_assert(command.getStart() >= state.getLastCommand(command.getBank())); + sc_assert(command.getStart() >= state.getLastCommand(command.getBank()).getStart()); satisfy_activateToActivate_sameBank(command); satisfy_activateToActivate_differentBank(command); diff --git a/dram/src/core/scheduling/checker/PrechargeChecker.cpp b/dram/src/core/scheduling/checker/PrechargeChecker.cpp index 8442eafc..412cc805 100644 --- a/dram/src/core/scheduling/checker/PrechargeChecker.cpp +++ b/dram/src/core/scheduling/checker/PrechargeChecker.cpp @@ -11,10 +11,10 @@ namespace core { void PrechargeChecker::delayToSatisfyConstraints(ScheduledCommand& command) const { - sc_assert(command.getCommand() == Precharge || command == PrechargeAll); + sc_assert(command.getCommand() == Precharge || command.getCommand() == PrechargeAll); ScheduledCommand lastCommandOnBank = state.getLastCommand(command.getBank()); if(lastCommandOnBank.isValidCommand()) - sc_assert(command.getStart() >= state.getLastCommand(command.getBank())); + sc_assert(command.getStart() >= state.getLastCommand(command.getBank()).getStart()); } sc_time PrechargeChecker::getExecutionTime(const tlm::tlm_generic_payload& payload, diff --git a/dram/src/core/utils/Utils.h b/dram/src/core/utils/Utils.h index 3c6c85db..c9850e78 100644 --- a/dram/src/core/utils/Utils.h +++ b/dram/src/core/utils/Utils.h @@ -10,7 +10,7 @@ #include #include -#include "common/dramExtension.h" +#include "../../common/dramExtension.h" unsigned int getStartAddress(Bank bank); unsigned int getBurstLengthInBytes(const tlm::tlm_generic_payload& payload, unsigned int buswidth); diff --git a/dram/src/scheduler/Fr_Fcfs.cpp b/dram/src/scheduler/Fr_Fcfs.cpp index a514f3bb..5d432f11 100644 --- a/dram/src/scheduler/Fr_Fcfs.cpp +++ b/dram/src/scheduler/Fr_Fcfs.cpp @@ -1,6 +1,6 @@ //#include "Fr_Fcfs.h" //#include "assert.h" -//#include "common/dramExtension.h" +//#include "../common/dramExtension.h" // //using namespace tlm; //using namespace std; diff --git a/dram/src/scheduler/Scheduler.h b/dram/src/scheduler/Scheduler.h index 5df85107..54f3d386 100644 --- a/dram/src/scheduler/Scheduler.h +++ b/dram/src/scheduler/Scheduler.h @@ -1,7 +1,7 @@ #ifndef SCHEDULER_H #define SCHEDULER_H #include -#include "common/dramExtension.h" +#include "../common/dramExtension.h" namespace scheduler { diff --git a/dram/src/simulation/MemoryManager.cpp b/dram/src/simulation/MemoryManager.cpp new file mode 100644 index 00000000..35cf28b0 --- /dev/null +++ b/dram/src/simulation/MemoryManager.cpp @@ -0,0 +1,50 @@ +/* + * MemoryManager.cpp + * + * Created on: Mar 16, 2014 + * Author: robert + */ + +#include "MemoryManager.h" +#include + +using namespace std; + +MemoryManager::MemoryManager(): numberOfAllocations(0), numberOfFrees(0) +{ + +} + +MemoryManager::~MemoryManager() +{ + for(gp* payload: freePayloads) + { + delete payload; + numberOfFrees++; + } + cout << "Memory Manager: Number of allocated payloads: " << numberOfAllocations << std::endl; + cout << "Memory Manager: Number of freed payloads: " << numberOfFrees << std::endl; + +} + +gp* MemoryManager::allocate() +{ + //if(freePayloads.empty()) + //{ + //numberOfAllocations++; + return new gp(this); + //} + //else + //{ +// gp* result = freePayloads.back(); +// freePayloads.pop_back(); +// return result; +// } +} + +void MemoryManager::free(gp* payload) +{ + payload->reset(); //clears all extensions + freePayloads.push_back(payload); +} + diff --git a/dram/src/simulation/MemoryManager.h b/dram/src/simulation/MemoryManager.h new file mode 100644 index 00000000..cbbb2e61 --- /dev/null +++ b/dram/src/simulation/MemoryManager.h @@ -0,0 +1,29 @@ +/* + * MemoryManager.h + * + * Created on: Mar 16, 2014 + * Author: robert + */ + +#ifndef MEMORYMANAGER_H_ +#define MEMORYMANAGER_H_ + +#include +#include +typedef tlm::tlm_generic_payload gp; + +class MemoryManager : public tlm::tlm_mm_interface +{ +public: + MemoryManager(); + virtual ~MemoryManager(); + virtual gp* allocate(); + virtual void free(gp* payload); + +private: + unsigned int numberOfAllocations; + unsigned int numberOfFrees; + std::vector freePayloads; +}; + +#endif /* MEMORYMANAGER_H_ */ diff --git a/dram/src/simulation/arbiter.h b/dram/src/simulation/arbiter.h new file mode 100644 index 00000000..d56b534d --- /dev/null +++ b/dram/src/simulation/arbiter.h @@ -0,0 +1,145 @@ +/* + * arbiter.h + * + * Created on: Mar 16, 2014 + * Author: robert + */ + +#ifndef ARBITER_H_ +#define ARBITER_H_ + + + +#include +#include +#include +#include +#include +#include +#include "../common/xmlAddressdecoder.h" +#include "../common/dramExtension.h" +#include + + +using namespace std; +using namespace tlm; + +template +struct Arbiter: public sc_module +{ +public: + tlm_utils::simple_initiator_socket iSocket; + tlm_utils::simple_target_socket_tagged tSockets[NUMBER_OF_THREADS]; + + SC_CTOR(Arbiter) : + payloadEventQueue(this, &Arbiter::peqCallback), channelIsFree(true) + { + iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw); + + for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i) + { + tSockets[i].register_nb_transport_fw(this, &Arbiter::nb_transport_fw, i); + } + } + +private: + tlm_utils::peq_with_cb_and_phase payloadEventQueue; + bool channelIsFree; + deque backpressure; + + + // Initiated by schedulerWrapper + tlm_sync_enum nb_transport_bw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay) + { + payloadEventQueue.notify(payload, phase, bwDelay); + return TLM_ACCEPTED; + } + + // Initiated by .stl players + tlm_sync_enum nb_transport_fw(int socketId, tlm_generic_payload& payload, tlm_phase& phase, + sc_time& fwDelay) + { + if(phase == BEGIN_REQ) + { + appendDramExtension(socketId, payload); + payload.acquire(); + } + else if(phase == END_RESP) + payload.release(); + + payloadEventQueue.notify(payload, phase, fwDelay); + return TLM_ACCEPTED; + } + + void peqCallback(tlm_generic_payload& payload, const tlm_phase& phase) + { + //Phases initiated by .stl players + if (phase == BEGIN_REQ) + { + if(channelIsFree) + { + channelIsFree = false; + sendToChannel(payload, phase, SC_ZERO_TIME ); + } + else + { + backpressure.push_back(&payload); + } + } + + else if (phase == END_RESP) + { + sendToChannel(payload, phase, SC_ZERO_TIME ); + } + + //Phases initiated by dram backend + else if (phase == END_REQ) + { + channelIsFree = true; + sendToTraceplayer(DramExtension::getExtension(payload).getThread().ID(), payload, phase, SC_ZERO_TIME); + + if(!backpressure.empty()) + { + tlm_generic_payload* payloadToSend = backpressure.front(); + backpressure.pop_front(); + sendToChannel(*payloadToSend, BEGIN_REQ, SC_ZERO_TIME ); + channelIsFree = false; + } + } + else if (phase == BEGIN_RESP) + { + sendToTraceplayer(DramExtension::getExtension(payload).getThread().ID(), payload, phase, SC_ZERO_TIME); + } + + else + { + SC_REPORT_FATAL(0, "Payload event queue in arbiter was triggered with unknown phase"); + } + } + + void sendToChannel(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) + { + tlm_phase TPhase = phase; + sc_time TDelay = delay; + iSocket->nb_transport_fw(payload, TPhase, TDelay); + } + + void sendToTraceplayer(unsigned int id, tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) + { + tlm_phase TPhase = phase; + sc_time TDelay = delay; + tSockets[id]->nb_transport_bw(payload, TPhase, TDelay); + } + + void appendDramExtension(int socketId, tlm_generic_payload& payload) + { + node n; + xmlAddressDecoder::getInstance().getNode(static_cast(payload.get_address()), &n); + DramExtension* extension = new DramExtension(Thread(socketId), Channel(n.channel), Bank(n.bank), Row(n.row), Column(n.colum)); + payload.set_auto_extension(extension); + } +}; + + + +#endif /* ARBITER_H_ */ diff --git a/dram/src/tlm/controllerwrapper.h b/dram/src/simulation/controllerwrapper.h similarity index 98% rename from dram/src/tlm/controllerwrapper.h rename to dram/src/simulation/controllerwrapper.h index 8249a715..5649ba52 100644 --- a/dram/src/tlm/controllerwrapper.h +++ b/dram/src/simulation/controllerwrapper.h @@ -16,8 +16,8 @@ #include #include -#include "common/protocol.h" -#include "common/tlmDBPhaseRecorder.h" +#include "../common/protocol.h" +#include "../common/tlmDBPhaseRecorder.h" #include "../core/IWrapperConnector.h" #include "../core/Controller.h" #include "../scheduler/Scheduler.h" diff --git a/dram/src/simulation/dram.h b/dram/src/simulation/dram.h new file mode 100644 index 00000000..e2ad424b --- /dev/null +++ b/dram/src/simulation/dram.h @@ -0,0 +1,219 @@ +/* + * dram.h + * + * Created on: Mar 16, 2014 + * Author: robert + */ + +#ifndef DRAM_H_ +#define DRAM_H_ + + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/protocol.h" +#include "../common/xmlConfig.h" + +using namespace sc_core; +using namespace sc_dt; +using namespace std; +using namespace tlm; + +xmlConfig xc; + +template +struct Dram: sc_module +{ + tlm_utils::simple_target_socket tSocket; + sc_event target_done_event; + tlm_utils::peq_with_cb_and_phase m_peq; + + SC_CTOR(Dram) : tSocket("socket") ,m_peq(this, &Dram::peq_cb) + { + tSocket.register_nb_transport_fw(this, &Dram::nb_transport_fw); + } + + ~Dram() + { + + } + + // TLM-2 non-blocking transport method + virtual tlm::tlm_sync_enum nb_transport_fw( tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_time& delay ) + { + // Queue the transaction until the annotated time has elapsed + m_peq.notify( trans, phase, delay); + return tlm::TLM_ACCEPTED; + } + + void peq_cb(tlm::tlm_generic_payload& trans, const tlm::tlm_phase& phase) + { + if(phase == BEGIN_PRE || phase == AUTO_PRECHARGE) + { + + if(phase == BEGIN_PRE) + { + send_end_pre(trans); + } + } + else if(phase == BEGIN_ACT) + { + send_end_act(trans); + } + else if(phase == BEGIN_WR) + { + send_end_wr(trans,true); + } + else if(phase == BEGIN_RD) + { + send_end_rd(trans,true); + } + else if(phase == BEGIN_WRA) + { + send_end_wr(trans,false); + } + else if(phase == BEGIN_RDA) + { + send_end_rd(trans,false); + } + else if(phase == BEGIN_REFA) + { + send_end_refa(trans); + } + else if(phase == BEGIN_REFB) + { + send_end_refb(trans); + } + else if(phase == BEGIN_PDNP) + { + + } + else if(phase == END_PDNP) + { + + } + else if(phase == BEGIN_PDNA) + { + + } + else if(phase == END_PDNA) + { + + } + else if(phase == BEGIN_SREF) + { + + } + else if(phase == END_SREF) + { + + } + else // case tlm::BEGIN_REQ,END_REQ... + { + SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by target (2)"); + } + } + + + void send_end_refa(tlm::tlm_generic_payload& trans) + { + tlm::tlm_phase bw_phase; + sc_time delay; + + bw_phase = END_REFA; + delay = xc.tREFA; + + tSocket->nb_transport_bw( trans, bw_phase, delay ); + + } + + void send_end_refb(tlm::tlm_generic_payload& trans) + { + tlm::tlm_phase bw_phase; + sc_time delay; + + bw_phase = END_REFB; + delay = xc.tREFB; + + tSocket->nb_transport_bw( trans, bw_phase, delay ); + + } + + void send_end_rd(tlm::tlm_generic_payload& trans, bool open_page_policy_f) + { + tlm::tlm_phase bw_phase; + sc_time delay = SC_ZERO_TIME; + + + unsigned int BL = 2; + + if(open_page_policy_f == true) + { + bw_phase = END_RD; + delay = xc.tRL + xc.clk * BL; + } + else + { + bw_phase = END_RDA; + delay = xc.tRL + xc.clk * BL; + } + + tSocket->nb_transport_bw( trans, bw_phase, delay ); + } + + void send_end_wr(tlm::tlm_generic_payload& trans, bool open_page_policy_f) + { + + tlm::tlm_phase bw_phase; + sc_time delay = SC_ZERO_TIME; + + unsigned int BL = 2; + + if(open_page_policy_f == true) + { + bw_phase = END_WR; + delay = xc.tWL + xc.clk * (BL -1); + } + else + { + bw_phase = END_WRA; + delay = xc.tWL + xc.clk * (BL -1) + xc.tWR; + } + + // Send end of WR + tSocket->nb_transport_bw( trans, bw_phase, delay ); + } + + void send_end_pre(tlm::tlm_generic_payload& trans) + { + tlm::tlm_phase bw_phase; + sc_time delay; + + bw_phase = END_PRE; + delay = xc.tRP; + + tSocket->nb_transport_bw( trans, bw_phase, delay ); + } + void send_end_act(tlm::tlm_generic_payload& trans) + { + tlm::tlm_phase bw_phase; + sc_time delay; + + bw_phase = END_ACT; + delay = xc.tRCD; + + tSocket->nb_transport_bw( trans, bw_phase, delay ); + } +}; + + + + + +#endif /* DRAM_H_ */ diff --git a/dram/src/simulation/main.cpp b/dram/src/simulation/main.cpp new file mode 100644 index 00000000..0027d130 --- /dev/null +++ b/dram/src/simulation/main.cpp @@ -0,0 +1,77 @@ +/* + * main.cpp + * + * Created on: Mar 16, 2014 + * Author: robert + */ + + + +#include +#include +#include +#include +#include +#include "../common/tlmDBPhaseRecorder.h" +#include "../common/DebugManager.h" +#include "../common/xmlAddressdecoder.h" +#include "controllerwrapper.h" +#include "dram.h" +#include "arbiter.h" +#include "traceplayer.h" +#include +#include + +using namespace std; + +int sc_main(int argc, char **argv) { + + DebugManager::getInstance().printDebug(Importance::Warning, DebugManager::Sender::Core, SC_ZERO_TIME, "hhh"); + + string executableName(argv[0]); + string pathOfExecutable = executableName.substr(0,executableName.find_last_of('/')); + //string pathToStaticFolder = pathOfExecutable + string("../static"); + string pathToStaticFolder = string("/home/jonny/git/dram/dram/static"); + xmlAddressDecoder::URI = pathToStaticFolder + string("/addressConfig.xml"); + + tlmDBPhaseRecorder *recorder = new tlmDBPhaseRecorder("tpr.tdb", pathToStaticFolder); + + //TracePlayer<> player("player","/home/robert/traces/trace.stl"); + //TracePlayer<> player("player",string("/home/robert/common/common/static/test2.stl")); + //TracePlayer<> player("player",string("/home/jonny/traces/mediabench-h263encode_32.stl")); + TracePlayer<> player("player",string("/home/jonny/traces/mediabench-h263decode_32.stl")); + + cout << "Toplevel: build player" << std::endl; + Dram<> dram("dram"); + cout << "Toplevel: build dram" << std::endl; + Arbiter<> arbiter("arbiter"); + cout << "Toplevel: build arbiter" << std::endl; + ControllerWrapper<> controller("controller",*recorder); + cout << "Toplevel: build controller" << std::endl; + + cout << "Toplevel: binding sockets" << std::endl; + player.iSocket.bind(arbiter.tSockets[0]); + arbiter.iSocket.bind(controller.tSocket); + controller.iSocket.bind(dram.tSocket); + + cout << "Toplevel: simulation start" << std::endl; + + clock_t begin = clock(); + sc_start(); + + clock_t end = clock(); + + double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; + + cout << "Simulation took " << elapsed_secs << " seconds. You better optimize your model ! " << endl; + + delete recorder; + + string testingScript = pathToStaticFolder + string("/tests.py"); + string runTestCommand = string("python ") + testingScript + string(" tpr.tdb"); + //system(runTestCommand.c_str()); + + string run_tpr = "/home/jonny/git/analyzer/build-traceAnalyzer-Desktop-Debug/traceAnalyzer tpr.tdb"; + system(run_tpr.c_str()); + return 0; +} diff --git a/dram/src/simulation/traceplayer.h b/dram/src/simulation/traceplayer.h new file mode 100644 index 00000000..2ff4d4a3 --- /dev/null +++ b/dram/src/simulation/traceplayer.h @@ -0,0 +1,160 @@ +/* + * traceplayer.h + * + * Created on: Mar 16, 2014 + * Author: robert + */ + +#ifndef TRACEPLAYER_H_ +#define TRACEPLAYER_H_ + + + +#include +#include +#include +#include +#include +#include +#include +#include "MemoryManager.h" + +using namespace std; +using namespace tlm; + +template +struct TracePlayer: public sc_module +{ +public: + tlm_utils::simple_initiator_socket iSocket; + unsigned int transactionsSend; + + TracePlayer(sc_module_name name, string pathToTrace) : + payloadEventQueue(this, &TracePlayer::peqCallback), file(pathToTrace), numberOfPendingTransactions(0), transactionsSend(0) + { + if (!file.is_open()) + SC_REPORT_FATAL(0, (string("Could not open trace ") + pathToTrace).c_str()); + + if(!file) + { + SC_REPORT_FATAL(0, "trace is empty! Simulation stops"); + sc_stop(); + } + + iSocket.register_nb_transport_bw(this, &TracePlayer::nb_transport_bw); + scheduleNextPayload(); + } + +private: + tlm_utils::peq_with_cb_and_phase payloadEventQueue; + MemoryManager memoryManager; + ifstream file; + unsigned int numberOfPendingTransactions; + + void scheduleNextPayload() + { + if(file) + { + string time,command,address; + file >> time >> command >>address; + + //if there is a newline at the end of the .stl + if(time.empty() || command.empty() || address.empty()) + return; + + gp* payload = memoryManager.allocate(); + long parsedAdress = std::stoi(address.c_str(),0,16); + payload->set_address(parsedAdress); + + if(command == "read") + { + payload->set_command(TLM_READ_COMMAND); + } + else if(command == "write") + { + payload->set_command(TLM_WRITE_COMMAND); + } + else + { + SC_REPORT_FATAL(0, (string("Corrupted tracefile, command ") + command + string(" unknown")).c_str()); + } + + + payload->set_data_length(BUSWIDTH/8); + payload->set_response_status(TLM_INCOMPLETE_RESPONSE); + payload->set_dmi_allowed(false); + payload->set_byte_enable_length(0); + payload->set_streaming_width(0); + + sc_time sendingTime = sc_time(std::stoi(time.c_str()), SC_NS); + if(sendingTime <= sc_time_stamp()) + { + payloadEventQueue.notify(*payload,BEGIN_REQ,SC_ZERO_TIME); + } + else + { + payloadEventQueue.notify(*payload,BEGIN_REQ,sendingTime - sc_time_stamp()); + } + numberOfPendingTransactions++; + + } + } + + tlm_sync_enum nb_transport_bw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay) + { + payloadEventQueue.notify(payload, phase, bwDelay); + return TLM_ACCEPTED; + } + + + void peqCallback(tlm_generic_payload& payload, const tlm_phase& phase) + { + if (phase == BEGIN_REQ) + { + payload.acquire(); + sendToTarget(payload,phase,SC_ZERO_TIME); + //cout << "Sending transaction number: " << transactionsSend << std::endl; + transactionsSend++; + //cout << "Traceplayer: sending transaction at " << sc_time_stamp() << std::endl; + } + + else if (phase == END_REQ) + { + scheduleNextPayload(); + } + else if (phase == BEGIN_RESP) + { + payload.release(); + sendToTarget(payload,END_RESP,SC_ZERO_TIME); + numberOfPendingTransactions--; + //cout << numberOfPendingTransactions << std::endl; + + if(numberOfPendingTransactions == 0) + payloadEventQueue.notify(payload, END_RESP, SC_ZERO_TIME); + } + + //kleiner hack + else if (phase == END_RESP) + { + cout << "simulation stop at " << sc_time_stamp() << std::endl; + sc_stop(); + } + + else + { + SC_REPORT_FATAL(0, "TracePlayer PEQ was triggered with unknown phase"); + } + } + + void sendToTarget(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) + { + tlm_phase TPhase = phase; + sc_time TDelay = delay; + iSocket->nb_transport_fw(payload, TPhase, TDelay); + } +}; + + + + +#endif /* TRACEPLAYER_H_ */ diff --git a/dram/static/.gitignore b/dram/static/.gitignore new file mode 100644 index 00000000..6b13107c --- /dev/null +++ b/dram/static/.gitignore @@ -0,0 +1,3 @@ +*.stl +*.pyc +sampleStlGeneration.py diff --git a/dram/static/addressConfig.xml b/dram/static/addressConfig.xml new file mode 100755 index 00000000..1dc6c4eb --- /dev/null +++ b/dram/static/addressConfig.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/dram/static/createTraceDB.sql b/dram/static/createTraceDB.sql new file mode 100644 index 00000000..6acb961b --- /dev/null +++ b/dram/static/createTraceDB.sql @@ -0,0 +1,55 @@ +DROP TABLE IF EXISTS Phases; +DROP TABLE IF EXISTS GeneralInfo; +DROP TABLE IF EXISTS Comments; +DROP TABLE IF EXISTS ranges; +DROP TABLE IF EXISTS Transactions; +DROP TABLE IF EXISTS DebugMessages; + +CREATE TABLE Phases( + ID INTEGER PRIMARY KEY, + PhaseName TEXT, + PhaseBegin INTEGER, + PhaseEnd INTEGER, + Transact INTEGER +); + +CREATE TABLE GeneralInfo( + NumberOfTransactions INTEGER, + TraceEnd INTEGER, + NumberOfBanks INTEGER, + Description TEXT, + clk INTEGER, + UnitOfTime TEXT +); + + +CREATE TABLE Comments( + Time INTEGER, + Text TEXT +); + +CREATE TABLE DebugMessages( + Time INTEGER, + Message TEXT +); + +-- use SQLITE R* TREE Module to make queries on timespans effecient (see http://www.sqlite.org/rtree.html) +CREATE VIRTUAL TABLE ranges USING rtree( + id, + begin, end +); + +CREATE TABLE Transactions( + ID INTEGER PRIMARY KEY, + Range INTEGER, + Address INTEGER, + TThread INTEGER, + TChannel INTEGER, + TBank INTEGER, + TRow INTEGER, + TColumn INTEGER, + Command TEXT); + +CREATE INDEX ranges_index ON Transactions(Range); +CREATE INDEX "phasesTransactions" ON "Phases" ("Transact" ASC); +CREATE INDEX "messageTimes" ON "DebugMessages" ("Time" ASC) \ No newline at end of file diff --git a/dram/static/metrics.py b/dram/static/metrics.py new file mode 100644 index 00000000..aa5b7a52 --- /dev/null +++ b/dram/static/metrics.py @@ -0,0 +1,25 @@ +import sys +import sqlite3 + +def the_answer_to_life_the_universe_and_everything(connection): + return 42 + +def number_of_precharges(connection): + return 21 + +def calculateMetrics(pathToTrace): + connection = sqlite3.connect(pathToTrace) + metrics = [the_answer_to_life_the_universe_and_everything, number_of_precharges] + result = [] + + for metric in metrics: + result.append((metric.__name__.replace("_"," "), metric(connection))) + + connection.close() + print(result) + return result + +if __name__ == "__main__": + path = sys.argv[1] + calculateMetrics(path) + \ No newline at end of file diff --git a/dram/static/stlGenerator.py b/dram/static/stlGenerator.py new file mode 100644 index 00000000..3b78064e --- /dev/null +++ b/dram/static/stlGenerator.py @@ -0,0 +1,120 @@ +import xml.etree.ElementTree as ET +import re +import sys +import os + + +class DramConfigReader: + maxValues = {} + startBits = {} + endBits = {} + + def __extractAdressEncoding(self, root, elementName): + rowElement = root[0].find(elementName) + rowBitFrom, rowBitsTo = int( + rowElement.attrib['from']), int(rowElement.attrib['to']) + rowBitRange = rowBitsTo - rowBitFrom + 1 + maxRow = 2 ** rowBitRange - 1 + #print('{0}: range {1}..{2}'.format(elementName, 0, maxRow)) + self.startBits[elementName] = rowBitFrom + self.endBits[elementName] = rowBitsTo + self.maxValues[elementName] = maxRow + + def __init__(self): + root = ET.parse( + os.path.dirname(os.path.realpath(sys.argv[0])) + '/addressConfig.xml').getroot() + self.__extractAdressEncoding(root, 'channel') + self.__extractAdressEncoding(root, 'bank') + self.__extractAdressEncoding(root, 'row') + self.__extractAdressEncoding(root, 'colum') + + +class StlReader: + __dramConfigReader = DramConfigReader() + + def onesMask(self, numberOfOnes): + result = 0 + for i in range(numberOfOnes): + result = result | 1 << i + return result + + def parseAttributeFromAddress(self, address, element): + return address >> self.__dramConfigReader.startBits[element] & self.onesMask(self.__dramConfigReader.endBits[element] - self.__dramConfigReader.startBits[element] + 1) + + def formatStlLine(self, line): + try: + found = re.search('0x[0-9,a-f]+', line).group(0) + address = int(found, 16) + decodedAddress = '[Channel: {0} Bank: {1} Row:{2} Column: {3}]'.format(self.parseAttributeFromAddress(address, 'channel'), + self.parseAttributeFromAddress(address, 'bank'), self.parseAttributeFromAddress(address, 'row'), self.parseAttributeFromAddress(address, 'colum')) + return line.replace("\n", " ") + decodedAddress + except AttributeError: + return '' + + def printStlPretty(self, filename): + f = open(filename) + for line in f.readlines(): + print(self.formatStlLine(line)) + + +class StlGenerator: + __actions = [] + __time = 0 + __dramConfigReader = DramConfigReader() + + def clear(self): + self.__actions = [] + self.__time = 0 + + def setTime(self, time): + self.__time = time + + def addAction(self, bank, row, channel=0, RD_WR='read'): + tupel = (self.__time, RD_WR, self.__generateAdress(channel, bank, row)) + self.__actions.append(tupel) + + def setTimeAndAddAction(self, time, bank, row, channel=0, RD_WR='read'): + self.setTime(self, time) + self.addAction(self, bank, row, channel, RD_WR) + + def addLoad(self, banks, rows, channel=0, RD_WR='read'): + for bank in banks: + for row in rows: + self.addAction(bank, row, channel, RD_WR) + + def setTimeAndAddLoad(self, time, banks, rows, channel=0, RD_WR='read'): + self.setTime(self, time) + self.addLoad(banks, rows, channel, RD_WR) + + def generateStl(self, filename): + f = open(filename, 'w') + tmp = [] + for tupel in self.__actions: + tmp.append('{0}: {1} {2:#x}'.format(tupel[0], tupel[1], tupel[2])) + result = '\n'.join(tmp) + f.write(result) + f.close() + print( + '<---------------------- Generated stl {0} ---------------------->'.format(filename)) + print(result) + print( + '<---------------------- End generated stl ---------------------->'.format(filename)) + + def __generateAdress(self, channel, bank, row): + if(channel > self.__dramConfigReader.maxValues['channel']): + raise(BaseException('Channel argument out of range')) + if(bank > self.__dramConfigReader.maxValues['bank']): + raise(BaseException('Bank argument out of range')) + if(row > self.__dramConfigReader.maxValues['row']): + raise(BaseException('Row argument out of range')) + return (channel << self.__dramConfigReader.startBits['channel']) | (bank << self.__dramConfigReader.startBits['bank']) | (row << self.__dramConfigReader.startBits['row']) + + def __init__(self): + pass + + +if __name__ == '__main__': + if(len(sys.argv) > 1): + stlReader = StlReader() + for line in sys.argv[1:]: + stlReader.printStlPretty(line) diff --git a/dram/static/tests.py b/dram/static/tests.py new file mode 100644 index 00000000..8d415dbc --- /dev/null +++ b/dram/static/tests.py @@ -0,0 +1,281 @@ +import sys +import sqlite3 +import math + +class DramConfig(object): + """Holds the timing constraints in the standard and the configuration of the DRAM""" + + unitOfTime = "ns" + nActivateWindow = 0 + burstLengtht = 2 + clk = numberOfBanks = 0 + tRP = tRAS = tRC = tRRD = tRCD = tTAW = tRL = tWL = tWTR = 0 + + def clkAlign(self, value): + return math.ceil(value/self.clk)*self.clk + + def parseFromXml(self): + self.clk = 6 + self.numberOfBanks = 8 + self.nActivateWindow = 2 + self.burstLength = 2 + self.tRP = 3*self.clk + self.tRAS = 6*self.clk + self.tRRD = 2*self.clk + self.tRC = self.tRP + self.tRAS + self.tRCD = 3*self.clk + self.tRL = 3*self.clk + self.tWL = 1*self.clk + self.tWTR = 3*self.clk + self.tTAW = self.clkAlign(50) + + def __init__(self): + self.parseFromXml() + +dramconfig = DramConfig() + +# ----------- test utils --------------------------------------- + + +tests = [] + +def test(function): + tests.append(function) + return function + +class TestResult(object): + passed = True + message = '' + def __init__(self, passed = True, message = ''): + self.passed = passed + self.message = message + +def TestSuceeded(): + return TestResult() + +def TestFailed(message): + return TestResult(False,message); + +def formatTime(time): + return ('{0} {1}'.format(time, dramconfig.unitOfTime)) + +# ----------- command bus checks --------------------------------------- + +@test +def commands_are_clockaligned(connection): + """Checks that all commands on the command bus are aligned to the system clock""" + passedTest = True + cursor = connection.cursor() + cursor.execute("SELECT ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin") + lastRow = cursor.fetchone() + + for currentRow in cursor: + phaseBegin = currentRow[1] + phaseEnd = currentRow[2] + if(phaseBegin % dramconfig.clk != 0): + return TestFailed("Command with PhaseID {0} starts at {1} and is not aligned to system clock ({2})".format( + currentRow[0], formatTime(phaseBegin), formatTime(dramconfig.clk))) + if(phaseEnd % dramconfig.clk != 0): + return TestFailed("Command with PhaseID {0} end at {1} and is not aligned to system clock ({2})".format( + currentRow[0], formatTime(phaseEnd), formatTime(dramconfig.clk))) + return TestSuceeded() + +@test +def commandbus_slots_are_used_once(connection): + """Checks that no two phases on the command bus start at the same time""" + cursor = connection.cursor() + cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName NOT IN ('REQ', 'RESP') ORDER BY PhaseBegin") + lastRow = cursor.fetchone() + + for currentRow in cursor: + if(lastRow[1] != currentRow[1]): + lastRow = currentRow + else: + return TestFailed("Commandbus slot is used twice by commands with PhaseID {0} and {1}".format(lastRow[0], currentRow[0])) + return TestSuceeded() + +# ----------- precharge checks --------------------------------------- + + +# ----------- activate checks --------------------------------------- + +@test +def precharge_before_activate(connection): + """Checks that all activate commands are preceeded by a precharge or a refresh or a powerdown""" + cursor = connection.cursor() + query = """SELECT Phases.ID, PhaseName, PhaseBegin FROM Transactions INNER JOIN Phases ON Phases.Transact = Transactions.ID + WHERE (TBank = :bank AND PhaseName IN ('ACT','PRE','REFB')) OR PhaseName IN ('REFA','SREF','PDNP','PDNA') ORDER BY PhaseBegin""" + + for bankNumber in range(dramconfig.numberOfBanks): + cursor.execute(query,{"bank": bankNumber}) + lastRow = cursor.fetchone() + + for currentRow in cursor: + if(lastRow[1] != currentRow[1] or currentRow[1] != 'ACT'): + lastRow = currentRow + else: + return TestFailed("No precharge between activates with PhaseID {0} and {1}".format(lastRow[0], currentRow[0])) + return TestSuceeded() + +@test +def activate_to_activate(connection): + """Checks minimal time between two activates (JEDEC 229, P. 27)""" + cursor = connection.cursor() + cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") + lastRow = cursor.fetchone() + + for currentRow in cursor: + timeBetweenActivates = currentRow[1] - lastRow[1]; + if(timeBetweenActivates < dramconfig.tRRD): + return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}".format(currentRow[0], lastRow[0],formatTime(timeBetweenActivates), dramconfig.tRRD)) + else: + lastRow = currentRow + return TestSuceeded() + + +@test +def activate_to_activate_on_same_bank(connection): + """Checks minimal time between two activates on the same bank (JEDEC 229, P. 27)""" + cursor = connection.cursor() + query = "SELECT Phases.ID,PhaseBegin from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName = 'ACT' AND TBANK = :bank ORDER BY PhaseBegin" + + for bankNumber in range(dramconfig.numberOfBanks): + cursor.execute(query,{"bank": bankNumber}) + lastRow = cursor.fetchone() + + for currentRow in cursor: + timeBetweenActivates = currentRow[1] - lastRow[1]; + if(timeBetweenActivates < dramconfig.tRC): + return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}, since they are on the same bank({4})". + format(currentRow[0], lastRow[0],formatTime(timeBetweenActivates), dramconfig.tRC)) + else: + lastRow = currentRow + + return TestSuceeded() + +@test +def n_activate_window(connection): + """Checks n-Activate constraint (JEDEC 229, P. 27)""" + cursor = connection.cursor() + cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") + activateWindow = [] + + for currentRow in cursor: + activateWindow.append(currentRow[1]) + if(len(activateWindow) > dramconfig.nActivateWindow + 1): + activateWindow.pop(0) + if(activateWindow[dramconfig.nActivateWindow] - activateWindow[0] < dramconfig.tTAW): + return TestFailed("Activate with PhaseID {0} and the {1} preceeding activates violate the '{1} activate window' constraint." + " No more than {1} activates should be in rolling time window of {2}".format(currentRow[0], dramconfig.nActivateWindow,formatTime(dramconfig.tTAW))) + return TestSuceeded() + + +# ----------- read checks --------------------------------------- + +@test +def activate_to_read(connection): + """Checks minimal time bewteen activate and following read (JEDEC 229, P. 29)""" + cursor = connection.cursor() + query = "SELECT Phases.ID,PhaseBegin,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('ACT','RD') AND TBANK = :bank ORDER BY PhaseBegin" + + for bankNumber in range(dramconfig.numberOfBanks): + + cursor.execute(query,{"bank": bankNumber}) + lastRow = cursor.fetchone() + + for currentRow in cursor: + if(currentRow[2] == "RD" and lastRow[2] == "ACT"): + actToReadTime = currentRow[1] - lastRow[1]; + if(actToReadTime < dramconfig.tRCD): + return TestFailed("Read with PhaseID {0} starts {1} after activate {2}. Minimum activate to read time is {3}". + format(currentRow[0],formatTime(actToReadTime),lastRow[0], formatTime(dramconfig.tRCD))) + lastRow = currentRow + + return TestSuceeded() + +@test +def read_to_read(connection): + """Checks minimal time between two reads(JEDEC 229, P. 29)""" + cursor = connection.cursor() + cursor.execute("SELECT Phases.ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName = 'RD' ORDER BY PhaseBegin") + lastRow = cursor.fetchone() + + for currentRow in cursor: + if(currentRow[1] < lastRow[2]): + timeBetweenReads = currentRow[1] - lastRow[1]; + clocksBetweenReads = round(timeBetweenReads/dramconfig.clk) + if(clocksBetweenReads % 2 == 1): + return TestFailed("Read with PhaseID {0} interrupts read {1}. They are {2} clocks ({3}) apart. Numbers of clock between interrupting reads must be even.". + format(currentRow[0], lastRow[0], clocksBetweenReads, formatTime(timeBetweenReads))) + lastRow = currentRow + + return TestSuceeded() + +@test +def write_to_read(connection): + """Checks minimal time between write and following read (JEDEC 229, P. 34)""" + cursor = connection.cursor() + query = "SELECT Phases.ID,PhaseBegin,PhaseEnd,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('RD','WR') AND TBANK = :bank ORDER BY PhaseBegin" + + for bankNumber in range(dramconfig.numberOfBanks): + + cursor.execute(query,{"bank": bankNumber}) + lastRow = cursor.fetchone() + + for currentRow in cursor: + if(currentRow[3] == "RD" and lastRow[3] == "WR"): + writeEndToReadBegin = currentRow[1] - lastRow[2]; + if(writeEndToReadBegin < dramconfig.tWTR ): + return TestFailed("Read with PhaseID {0} starts {1} after end of write {2}. Minimum time between end of write and start of read is {3}". + format(currentRow[0],formatTime(writeEndToReadBegin),lastRow[0], formatTime(dramconfig.tWTR ))) + lastRow = currentRow + + return TestSuceeded() + + + + + + + + + + +# -------------------------- interface methods -------------------- + +def runTests(pathToTrace): + connection = sqlite3.connect(pathToTrace) + + testResults = [] + numberOfFailedTest = 0 + print("================================") + print("RUNNING TEST ON {0}".format(pathToTrace)) + + print("-----------------------------\n") + + for test in tests: + testResult = test(connection) + testName = test.__name__.replace("_"," ") + testResults.append((testName, testResult.passed,testResult.message)) + + if(testResult.passed): + print("{0} passed".format(testName)) + else: + print(">>>>>>{0} failed. Message: {1}".format(testName, testResult.message)) + numberOfFailedTest = numberOfFailedTest + 1 + + print("\n-----------------------------") + + if(numberOfFailedTest == 0): + print("All tests passed") + else: + print("{0} of {1} tests passed".format(len(tests) - numberOfFailedTest,len(tests))) + + print("================================") + connection.close() + return testResults + +if __name__ == "__main__": + path = sys.argv[1] + runTests(path) + \ No newline at end of file diff --git a/dram/testing/BankwiseRefreshManager_test.cpp b/dram/testing/BankwiseRefreshManager_test.cpp index 7a1c05f2..a50d7bcf 100644 --- a/dram/testing/BankwiseRefreshManager_test.cpp +++ b/dram/testing/BankwiseRefreshManager_test.cpp @@ -9,7 +9,7 @@ #include #include #include "testUtils.h" -#include "common/dramExtension.h" +#include "../common/dramExtension.h" using namespace testing; diff --git a/dram/testing/Controller_test.cpp b/dram/testing/Controller_test.cpp index 7c79c499..38c510c3 100644 --- a/dram/testing/Controller_test.cpp +++ b/dram/testing/Controller_test.cpp @@ -14,7 +14,7 @@ #include #include "tlm/IControllerWrapper.h" #include "testUtils.h" -#include "common/dramExtension.h" +#include "../common/dramExtension.h" #include "core/Controller.h" #include