diff options
author | Stefan Radomski <github@mintwerk.de> | 2017-01-08 21:59:18 (GMT) |
---|---|---|
committer | Stefan Radomski <github@mintwerk.de> | 2017-01-08 21:59:18 (GMT) |
commit | 030f3b483f54dbef6e164194a1771ef5b346312b (patch) | |
tree | 3f5b949b5ffed83d0b41a95d9fd3cfafd17cab2d /src | |
parent | 1ab8b9a0dcaa131b8cccc735a1794ce39b351715 (diff) | |
download | uscxml-030f3b483f54dbef6e164194a1771ef5b346312b.zip uscxml-030f3b483f54dbef6e164194a1771ef5b346312b.tar.gz uscxml-030f3b483f54dbef6e164194a1771ef5b346312b.tar.bz2 |
Support for caching values on filesystem
Use USCXML_NOCACHE_FILES=YES to prevent, I will make this a build flag
Diffstat (limited to 'src')
-rw-r--r-- | src/bindings/swig/python/uscxml.i | 4 | ||||
-rw-r--r-- | src/uscxml/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/uscxml/debug/DebugSession.cpp | 147 | ||||
-rw-r--r-- | src/uscxml/debug/DebugSession.h | 13 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.cpp | 2 | ||||
-rw-r--r-- | src/uscxml/debug/DebuggerServlet.h | 2 | ||||
-rw-r--r-- | src/uscxml/debug/InterpreterIssue.cpp | 4 | ||||
-rw-r--r-- | src/uscxml/interpreter/BasicEventQueue.cpp | 18 | ||||
-rw-r--r-- | src/uscxml/interpreter/FastMicroStep.cpp | 188 | ||||
-rw-r--r-- | src/uscxml/interpreter/FastMicroStep.h | 33 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterImpl.cpp | 39 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterImpl.h | 7 | ||||
-rw-r--r-- | src/uscxml/interpreter/MicroStepImpl.h | 3 | ||||
-rw-r--r-- | src/uscxml/messages/Data.cpp | 2 | ||||
-rw-r--r-- | src/uscxml/messages/Data.h | 40 | ||||
-rw-r--r-- | src/uscxml/util/Base64.hpp | 4 | ||||
-rw-r--r-- | src/uscxml/util/Convenience.cpp | 32 | ||||
-rw-r--r-- | src/uscxml/util/Convenience.h | 2 | ||||
-rw-r--r-- | src/uscxml/util/URL.cpp | 168 | ||||
-rw-r--r-- | src/uscxml/util/URL.h | 16 | ||||
-rw-r--r-- | src/uscxml/util/URL.mm | 62 |
21 files changed, 618 insertions, 178 deletions
diff --git a/src/bindings/swig/python/uscxml.i b/src/bindings/swig/python/uscxml.i index c501223..ee920e9 100644 --- a/src/bindings/swig/python/uscxml.i +++ b/src/bindings/swig/python/uscxml.i @@ -150,11 +150,9 @@ using namespace XERCESC_NS; %include "../wrapped/WrappedIOProcessor.h" %include "../wrapped/WrappedInterpreterMonitor.h" - -/*%template(IssueList) std::list<uscxml::InterpreterIssue>; +%template(IssueList) std::list<uscxml::InterpreterIssue>; %template(DataList) std::list<uscxml::Data>; %template(StringList) std::list<std::string>; -*/ %template(DataMap) std::map<std::string, uscxml::Data>; %template(StringSet) std::set<std::string>; %template(StringVector) std::vector<std::string>; diff --git a/src/uscxml/CMakeLists.txt b/src/uscxml/CMakeLists.txt index 3497b18..459fe23 100644 --- a/src/uscxml/CMakeLists.txt +++ b/src/uscxml/CMakeLists.txt @@ -32,6 +32,15 @@ file(GLOB_RECURSE USCXML_MISC util/*.c util/*.h ) + +# Add URL.mm for MacOS / iOS +if(APPLE OR IOS) + file(GLOB USCXML_APPLE util/*.mm) + list (APPEND USCXML_MISC ${USCXML_APPLE}) + find_library(FOUNDATION_FRAMEWORK Foundation) + list (APPEND USCXML_CORE_LIBS ${FOUNDATION_FRAMEWORK}) +endif() + source_group("Interpreter" FILES ${USCXML_MISC}) list (APPEND USCXML_FILES ${USCXML_MISC}) @@ -84,4 +93,5 @@ SET(USCXML_DATAMODELS ${USCXML_DATAMODELS} PARENT_SCOPE) # set(USCXML_OPT_LIBS ${USCXML_OPT_LIBS} PARENT_SCOPE) set(USCXML_FILES ${USCXML_FILES} PARENT_SCOPE) set(USCXML_TRANSFORM_FILES ${USCXML_TRANSFORM_FILES} PARENT_SCOPE) +set(USCXML_CORE_LIBS ${USCXML_CORE_LIBS} PARENT_SCOPE) # SET(PLUMA ${PLUMA} PARENT_SCOPE)
\ No newline at end of file diff --git a/src/uscxml/debug/DebugSession.cpp b/src/uscxml/debug/DebugSession.cpp index 9fa09eb..f6b3ae1 100644 --- a/src/uscxml/debug/DebugSession.cpp +++ b/src/uscxml/debug/DebugSession.cpp @@ -228,11 +228,13 @@ Data DebugSession::debugStop(const Data& data) { if (_isRunning && _interpreterThread != NULL) { _isRunning = false; + + // unblock + _resumeCond.notify_all(); + _interpreterThread->join(); delete(_interpreterThread); } - // unblock - _resumeCond.notify_all(); _skipTo = Breakpoint(); replyData.compound["status"] = Data("success", Data::VERBATIM); @@ -377,6 +379,7 @@ Data DebugSession::enableAllBreakPoints() { return replyData; } + Data DebugSession::disableAllBreakPoints() { Data replyData; @@ -386,6 +389,38 @@ Data DebugSession::disableAllBreakPoints() { return replyData; } +Data DebugSession::getIssues() { + Data replyData; + + std::list<InterpreterIssue> issues = _interpreter.validate(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + for (auto issue : issues) { + Data issueData; + + issueData.compound["message"] = Data(issue.message, Data::VERBATIM); + issueData.compound["xPath"] = Data(issue.xPath, Data::VERBATIM); + issueData.compound["specRef"] = Data(issue.specRef, Data::VERBATIM); + + switch (issue.severity) { + case InterpreterIssue::USCXML_ISSUE_FATAL: + issueData.compound["severity"] = Data("FATAL", Data::VERBATIM); + break; + case InterpreterIssue::USCXML_ISSUE_WARNING: + issueData.compound["severity"] = Data("WARN", Data::VERBATIM); + break; + case InterpreterIssue::USCXML_ISSUE_INFO: + issueData.compound["severity"] = Data("INFO", Data::VERBATIM); + break; + default: + break; + } + + replyData.compound["issues"].array.push_back(issueData); + } + + return replyData; +} + Data DebugSession::debugEval(const Data& data) { Data replyData; @@ -416,69 +451,69 @@ Data DebugSession::debugEval(const Data& data) { } std::shared_ptr<LoggerImpl> DebugSession::create() { - return shared_from_this(); + return shared_from_this(); } void DebugSession::log(LogSeverity severity, const Event& event) { - Data d; - d.compound["data"] = event.data; - d.compound["name"] = Data(event.name); - d.compound["origin"] = Data(event.origin); - d.compound["origintype"] = Data(event.origintype); - - switch (event.eventType) { - case Event::Type::INTERNAL: - d.compound["eventType"] = Data("INTERNAL"); - break; - case Event::Type::EXTERNAL: - d.compound["eventType"] = Data("EXTERNAL"); - break; - case Event::Type::PLATFORM: - d.compound["eventType"] = Data("PLATFORM"); - break; - default: - break; - } - if (!event.hideSendId) - d.compound["sendid"] = Data(event.sendid); - if (event.invokeid.size() > 0) - d.compound["invokeid"] = Data(event.invokeid); - - // handle params - Data& params = d.compound["params"]; - bool convertedToArray = false; - for (auto param : event.params) { - if (params.compound.find(param.first) != d.compound.end()) { - // no such key, add as literal data - d.compound[param.first] = param.second; - } else if (params.compound[param.first].array.size() > 0 && convertedToArray) { - // key is already an array - params.compound[param.first].array.push_back(param.second); - } else { - // key already given as literal data, move to array - Data& existingParam = params.compound[param.first]; - params.compound[param.first].array.push_back(existingParam); - params.compound[param.first].array.push_back(param.second); - params.compound[param.first].compound.clear(); - convertedToArray = true; - } - } - - // handle namelist - Data& namelist = d.compound["namelist"]; - for (auto name : event.namelist) { - namelist.compound[name.first] = name.second; - } - - _debugger->pushData(shared_from_this(), d); + Data d; + d.compound["data"] = event.data; + d.compound["name"] = Data(event.name); + d.compound["origin"] = Data(event.origin); + d.compound["origintype"] = Data(event.origintype); + + switch (event.eventType) { + case Event::Type::INTERNAL: + d.compound["eventType"] = Data("INTERNAL"); + break; + case Event::Type::EXTERNAL: + d.compound["eventType"] = Data("EXTERNAL"); + break; + case Event::Type::PLATFORM: + d.compound["eventType"] = Data("PLATFORM"); + break; + default: + break; + } + if (!event.hideSendId) + d.compound["sendid"] = Data(event.sendid); + if (event.invokeid.size() > 0) + d.compound["invokeid"] = Data(event.invokeid); + + // handle params + Data& params = d.compound["params"]; + bool convertedToArray = false; + for (auto param : event.params) { + if (params.compound.find(param.first) != d.compound.end()) { + // no such key, add as literal data + d.compound[param.first] = param.second; + } else if (params.compound[param.first].array.size() > 0 && convertedToArray) { + // key is already an array + params.compound[param.first].array.push_back(param.second); + } else { + // key already given as literal data, move to array + Data& existingParam = params.compound[param.first]; + params.compound[param.first].array.push_back(existingParam); + params.compound[param.first].array.push_back(param.second); + params.compound[param.first].compound.clear(); + convertedToArray = true; + } + } + + // handle namelist + Data& namelist = d.compound["namelist"]; + for (auto name : event.namelist) { + namelist.compound[name.first] = name.second; + } + + _debugger->pushData(shared_from_this(), d); } void DebugSession::log(LogSeverity severity, const Data& data) { - _debugger->pushData(shared_from_this(), data); + _debugger->pushData(shared_from_this(), data); } void DebugSession::log(LogSeverity severity, const std::string& message) { - _debugger->pushData(shared_from_this(), Data(message)); + _debugger->pushData(shared_from_this(), Data(message)); } } diff --git a/src/uscxml/debug/DebugSession.h b/src/uscxml/debug/DebugSession.h index 9224163..e258568 100644 --- a/src/uscxml/debug/DebugSession.h +++ b/src/uscxml/debug/DebugSession.h @@ -66,6 +66,7 @@ public: Data disableBreakPoint(const Data& data); Data enableAllBreakPoints(); Data disableAllBreakPoints(); + Data getIssues(); Data debugEval(const Data& data); void setDebugger(Debugger* debugger) { @@ -80,12 +81,12 @@ public: _markedForDeletion = mark; } - // Logger - virtual std::shared_ptr<LoggerImpl> create(); - - virtual void log(LogSeverity severity, const Event& event); - virtual void log(LogSeverity severity, const Data& data); - virtual void log(LogSeverity severity, const std::string& message); + // Logger + virtual std::shared_ptr<LoggerImpl> create(); + + virtual void log(LogSeverity severity, const Event& event); + virtual void log(LogSeverity severity, const Data& data); + virtual void log(LogSeverity severity, const std::string& message); protected: void breakExecution(Data replyData); diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp index 016d67c..44255c0 100644 --- a/src/uscxml/debug/DebuggerServlet.cpp +++ b/src/uscxml/debug/DebuggerServlet.cpp @@ -134,6 +134,8 @@ bool DebuggerServlet::requestFromHTTP(const HTTPServer::Request& request) { } else if (boost::starts_with(request.data.at("path").atom, "/debug/disconnect")) { processDisconnect(request); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/issues")) { + replyData = session->getIssues(); } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/enable/all")) { replyData = session->enableAllBreakPoints(); } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/disable/all")) { diff --git a/src/uscxml/debug/DebuggerServlet.h b/src/uscxml/debug/DebuggerServlet.h index 3b117bf..4cd04bb 100644 --- a/src/uscxml/debug/DebuggerServlet.h +++ b/src/uscxml/debug/DebuggerServlet.h @@ -50,6 +50,8 @@ public: void processConnect(const HTTPServer::Request& request); void processListSessions(const HTTPServer::Request& request); + void processIssues(const HTTPServer::Request& request); + // void processDebugPrepare(const HTTPServer::Request& request); // void processDebugAttach(const HTTPServer::Request& request); // void processDebugStart(const HTTPServer::Request& request); diff --git a/src/uscxml/debug/InterpreterIssue.cpp b/src/uscxml/debug/InterpreterIssue.cpp index 90e06b4..4ee4442 100644 --- a/src/uscxml/debug/InterpreterIssue.cpp +++ b/src/uscxml/debug/InterpreterIssue.cpp @@ -385,7 +385,7 @@ std::list<InterpreterIssue> InterpreterIssue::forInterpreter(InterpreterImpl* in // check whether state is reachable if (!DOMUtils::isMember(state, reachable) && areFromSameMachine(state, interpreter->_scxml)) { - issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_FATAL)); + issues.push_back(InterpreterIssue("State with id '" + stateId + "' is unreachable", state, InterpreterIssue::USCXML_ISSUE_WARNING)); } // check for uniqueness of id attribute @@ -1044,4 +1044,4 @@ std::ostream& operator<< (std::ostream& os, const InterpreterIssue& issue) { } -}
\ No newline at end of file +} diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp index 2f8bd48..519754e 100644 --- a/src/uscxml/interpreter/BasicEventQueue.cpp +++ b/src/uscxml/interpreter/BasicEventQueue.cpp @@ -35,17 +35,17 @@ Event BasicEventQueue::dequeue(size_t blockMs) { std::lock_guard<std::recursive_mutex> lock(_mutex); if (blockMs > 0) { - using namespace std::chrono; + using namespace std::chrono; - // TODO: do read http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm - system_clock::time_point now = system_clock::now(); - system_clock::time_point endTime = now + milliseconds(blockMs); + // TODO: do read http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm + system_clock::time_point now = system_clock::now(); + system_clock::time_point endTime = now + milliseconds(blockMs); + + // now + milliseconds(blockMs) may not have fitted into a duration type - limit to maximum duration + if (blockMs > system_clock::duration::max().count() - duration_cast<milliseconds>(now.time_since_epoch()).count()) { + endTime = system_clock::time_point::max(); + } - // now + milliseconds(blockMs) may not have fitted into a duration type - limit to maximum duration - if (blockMs > system_clock::duration::max().count() - duration_cast<milliseconds>(now.time_since_epoch()).count()) { - endTime = system_clock::time_point::max(); - } - // block for given milliseconds or until queue is non-empty while (endTime > std::chrono::system_clock::now() && _queue.empty()) { _cond.wait_until(_mutex, endTime); diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index ed11fca..e41e9f7 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -22,6 +22,7 @@ #include "FastMicroStep.h" #include "uscxml/util/DOM.h" #include "uscxml/util/String.h" +#include "uscxml/util/Base64.hpp" #include "uscxml/util/Predicates.h" #include "uscxml/util/Convenience.h" #include "uscxml/interpreter/InterpreterMonitor.h" @@ -102,6 +103,8 @@ void FastMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { everything else */ + // TODO: We can do this in one iteration + DOMElement* child = element->getFirstElementChild(); while(child) { resortStates(child, xmlPrefix); @@ -194,9 +197,11 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _xmlPrefix = std::string(_xmlPrefix) + ":"; } + bool withCache = !envVarIsTrue("USCXML_NOCACHE_FILES"); + resortStates(_scxml, _xmlPrefix); -// assert(false); -// throw NULL; + + Data& cache = _callbacks->getCache().compound["FastMicroStep"]; /** -- All things states -- */ @@ -243,7 +248,20 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _states[0]->data = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); } + auto currState = cache.compound["states"].array.begin(); + auto endState = cache.compound["states"].array.end(); + for (i = 0; i < _states.size(); i++) { + Data* cachedState = NULL; + if (withCache) { + if (currState != endState) { + cachedState = &(*currState); + currState++; + } else { + cache.compound["states"].array.push_back(Data()); + cachedState = &(*cache.compound["states"].array.rbegin()); + } + } // collect states with an id attribute if (HAS_ATTR(_states[i]->element, "id")) { _stateIds[ATTR(_states[i]->element, "id")] = i; @@ -273,7 +291,6 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } - // set the states type if (false) { } else if (iequals(TAGNAME(_states[i]->element), _xmlPrefix.str() + "initial")) { @@ -297,17 +314,22 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } // establish the states' completion - std::list<DOMElement*> completion = getCompletion(_states[i]->element); - for (j = 0; j < _states.size(); j++) { - if (!completion.empty() && _states[j]->element == completion.front()) { - _states[i]->completion[j] = true; - completion.pop_front(); - } else { - _states[i]->completion[j] = false; + if (withCache && cachedState->compound.find("completion") != cachedState->compound.end()) { + _states[i]->completion = fromBase64(cachedState->compound["completion"]); + } else { + std::list<DOMElement*> completion = getCompletion(_states[i]->element); + for (j = 0; j < _states.size(); j++) { + if (!completion.empty() && _states[j]->element == completion.front()) { + _states[i]->completion[j] = true; + completion.pop_front(); + } else { + _states[i]->completion[j] = false; + } } + assert(completion.size() == 0); + if (withCache) + cachedState->compound["completion"] = Data(toBase64(_states[i]->completion)); } - assert(completion.size() == 0); - // this is set when establishing the completion if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) { _states[i]->type |= USCXML_STATE_HAS_HISTORY; @@ -354,46 +376,78 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } assert(tmp.size() == 0); + auto currTrans = cache.compound["transitions"].array.begin(); + auto endTrans = cache.compound["transitions"].array.end(); + for (i = 0; i < _transitions.size(); i++) { + Data* cachedTrans = NULL; + if (withCache) { + if (currTrans != endTrans) { + cachedTrans = &(*currTrans); + currTrans++; + } else { + cache.compound["transitions"].array.push_back(Data()); + cachedTrans = &(*cache.compound["transitions"].array.rbegin()); + } + } // establish the transitions' exit set assert(_transitions[i]->element != NULL); -// std::cout << "i: " << i << std::endl << std::flush; - std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); - _cache.exitSet[_transitions[i]->element] = exitList; - - for (j = 0; j < _states.size(); j++) { - if (!exitList.empty() && _states[j]->element == exitList.front()) { - _transitions[i]->exitSet[j] = true; - exitList.pop_front(); - } else { - _transitions[i]->exitSet[j] = false; + + if (withCache && cachedTrans->compound.find("exitset") != cachedTrans->compound.end()) { + _transitions[i]->exitSet = fromBase64(cachedTrans->compound["exitset"]); + } else { + std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); + + for (j = 0; j < _states.size(); j++) { + if (!exitList.empty() && _states[j]->element == exitList.front()) { + _transitions[i]->exitSet[j] = true; + exitList.pop_front(); + } else { + _transitions[i]->exitSet[j] = false; + } } + assert(exitList.size() == 0); + + if (withCache) + cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet)); } - assert(exitList.size() == 0); // establish the transitions' conflict set - for (j = i; j < _transitions.size(); j++) { - if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { - _transitions[i]->conflicts[j] = true; - } else { - _transitions[i]->conflicts[j] = false; + if (withCache && cachedTrans->compound.find("conflicts") != cachedTrans->compound.end()) { + _transitions[i]->conflicts = fromBase64(cachedTrans->compound["conflicts"]); + } else { + for (j = i; j < _transitions.size(); j++) { + if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { + _transitions[i]->conflicts[j] = true; + } else { + _transitions[i]->conflicts[j] = false; + } + // std::cout << "."; } -// std::cout << "."; - } - // conflicts matrix is symmetric - for (j = 0; j < i; j++) { - _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; - } + // conflicts matrix is symmetric + for (j = 0; j < i; j++) { + _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; + } + if (withCache) + cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts)); + } // establish the transitions' target set - std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); - for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { - if (_stateIds.find(*tIter) != _stateIds.end()) { - _transitions[i]->target[_stateIds[*tIter]] = true; + if (withCache && cachedTrans->compound.find("target") != cachedTrans->compound.end()) { + _transitions[i]->target = fromBase64(cachedTrans->compound["target"]); + } else { + std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); + for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { + if (_stateIds.find(*tIter) != _stateIds.end()) { + _transitions[i]->target[_stateIds[*tIter]] = true; + } } + if (withCache) + cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target)); + } // the transition's source @@ -422,7 +476,6 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _transitions[i]->type |= USCXML_TRANS_INITIAL; } - // the transitions event and condition _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, "event") ? ATTR(_transitions[i]->element, "event") : ""); @@ -433,10 +486,39 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { if (_transitions[i]->element->getChildElementCount() > 0) { _transitions[i]->onTrans = _transitions[i]->element; } - } _cache.exitSet.clear(); _isInitialized = true; + +} + +std::string FastMicroStep::toBase64(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset) { + std::vector<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type> bytes(bitset.num_blocks() + 1); + boost::to_block_range(bitset, bytes.begin()); + + std::string encoded; + encoded.resize(sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) * bytes.size()); + bytes[bytes.size() - 1] = static_cast<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type>(bitset.size()); + + static_assert(sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) >= sizeof(size_t), "Block type too small to encode dynamic_bitset length"); + + memcpy(&encoded[0], &bytes[0], sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) * bytes.size()); + return base64Encode(encoded.c_str(), encoded.size(), true); +} + +boost::dynamic_bitset<BITSET_BLOCKTYPE> FastMicroStep::fromBase64(const std::string& encoded) { + + std::string decoded = base64Decode(encoded); + assert(decoded.size() % sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) == 0); + std::vector<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type> bytes(decoded.size() / sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type)); + + memcpy(&bytes[0], &decoded[0], decoded.size()); + + boost::dynamic_bitset<BITSET_BLOCKTYPE> bitset; + bitset.init_from_block_range(bytes.begin(), --bytes.end()); + bitset.resize(bytes[bytes.size() - 1]); + + return bitset; } void FastMicroStep::markAsCancelled() { @@ -451,13 +533,13 @@ InterpreterState FastMicroStep::step(size_t blockMs) { size_t i, j, k; - boost::dynamic_bitset<> exitSet(_states.size(), false); - boost::dynamic_bitset<> entrySet(_states.size(), false); - boost::dynamic_bitset<> targetSet(_states.size(), false); - boost::dynamic_bitset<> tmpStates(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> entrySet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> targetSet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> tmpStates(_states.size(), false); - boost::dynamic_bitset<> conflicts(_transitions.size(), false); - boost::dynamic_bitset<> transSet(_transitions.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts(_transitions.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> transSet(_transitions.size(), false); #ifdef USCXML_VERBOSE std::cerr << "Config: "; @@ -675,14 +757,14 @@ ESTABLISH_ENTRYSET: /* iterate for ancestors */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { entrySet |= USCXML_GET_STATE(i).ancestors; i = entrySet.find_next(i); } /* iterate for descendants */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { @@ -813,7 +895,7 @@ ESTABLISH_ENTRYSET: /* TAKE_TRANSITIONS: */ i = transSet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if ((USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(i).element); @@ -841,7 +923,7 @@ ESTABLISH_ENTRYSET: /* ENTER_STATES: */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if (BIT_HAS(i, _configuration)) { // already active @@ -924,7 +1006,7 @@ ESTABLISH_ENTRYSET: BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { tmpStates.reset(); k = _configuration.find_first(); - while (k != boost::dynamic_bitset<>::npos) { + while (k != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if (BIT_HAS(j, USCXML_GET_STATE(k).ancestors)) { if (USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_FINAL) { tmpStates ^= USCXML_GET_STATE(k).ancestors; @@ -981,7 +1063,7 @@ bool FastMicroStep::isInState(const std::string& stateId) { std::list<XERCESC_NS::DOMElement*> FastMicroStep::getConfiguration() { std::list<XERCESC_NS::DOMElement*> config; size_t i = _configuration.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { config.push_back(_states[i]->element); i = _configuration.find_next(i); } @@ -1038,7 +1120,7 @@ std::list<DOMElement*> FastMicroStep::getHistoryCompletion(const DOMElement* his /** * Print name of states contained in a (debugging). */ -void FastMicroStep::printStateNames(const boost::dynamic_bitset<>& a) { +void FastMicroStep::printStateNames(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& a) { size_t i; const char* seperator = ""; for (i = 0; i < a.size(); i++) { diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index 810b793..db3e724 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -33,6 +33,12 @@ #include <boost/dynamic_bitset.hpp> +#ifdef _WIN32 +#define BITSET_BLOCKTYPE size_t +#else +#define BITSET_BLOCKTYPE +#endif + namespace uscxml { /** @@ -57,11 +63,11 @@ protected: Transition() : element(NULL), source(0), onTrans(NULL), type(0) {} XERCESC_NS::DOMElement* element; - boost::dynamic_bitset<> conflicts; - boost::dynamic_bitset<> exitSet; + boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts; + boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet; uint32_t source; - boost::dynamic_bitset<> target; + boost::dynamic_bitset<BITSET_BLOCKTYPE> target; XERCESC_NS::DOMElement* onTrans; @@ -77,9 +83,9 @@ protected: State() : element(NULL), parent(0), documentOrder(0), doneData(NULL), type(0) {} XERCESC_NS::DOMElement* element; - boost::dynamic_bitset<> completion; - boost::dynamic_bitset<> children; - boost::dynamic_bitset<> ancestors; + boost::dynamic_bitset<BITSET_BLOCKTYPE> completion; + boost::dynamic_bitset<BITSET_BLOCKTYPE> children; + boost::dynamic_bitset<BITSET_BLOCKTYPE> ancestors; uint32_t parent; uint32_t documentOrder; @@ -108,12 +114,12 @@ protected: std::vector<Transition*> _transitions; std::list<XERCESC_NS::DOMElement*> _globalScripts; - boost::dynamic_bitset<> _configuration; - boost::dynamic_bitset<> _invocations; - boost::dynamic_bitset<> _history; - boost::dynamic_bitset<> _initializedData; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _configuration; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _invocations; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _history; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _initializedData; - std::set<boost::dynamic_bitset<> > _microstepConfigurations; + std::set<boost::dynamic_bitset<BITSET_BLOCKTYPE> > _microstepConfigurations; Binding _binding; XERCESC_NS::DOMElement* _scxml; @@ -130,13 +136,16 @@ private: bool conflictsCached(const XERCESC_NS::DOMElement* t1, const XERCESC_NS::DOMElement* t2, const XERCESC_NS::DOMElement* root); ///< overrides implementation Predicates::conflicts for speed + std::string toBase64(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset); + boost::dynamic_bitset<BITSET_BLOCKTYPE> fromBase64(const std::string& encoded); + std::list<XERCESC_NS::DOMElement*> getExitSetCached(const XERCESC_NS::DOMElement* transition, const XERCESC_NS::DOMElement* root); CachedPredicates _cache; #ifdef USCXML_VERBOSE - void printStateNames(const boost::dynamic_bitset<>& bitset); + void printStateNames(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset); #endif }; diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 43059bf..8762b95 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -27,11 +27,13 @@ #include "uscxml/messages/Event.h" #include "uscxml/util/String.h" #include "uscxml/util/Predicates.h" +#include "uscxml/util/MD5.hpp" #include "uscxml/plugins/InvokerImpl.h" #include "uscxml/interpreter/Logging.h" #include <iostream> +#include <fstream> #include <assert.h> #include <algorithm> @@ -108,6 +110,15 @@ InterpreterImpl::~InterpreterImpl() { // assert(_invokers.size() == 0); // ::xercesc_3_1::XMLPlatformUtils::Terminate(); + if (!envVarIsTrue("USCXML_NOCACHE_FILES")) { + // save our cache + std::string sharedTemp = URL::getTempDir(true); + std::ofstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); + if (dataFS) { + dataFS << _cache; + dataFS.close(); + } + } } void InterpreterImpl::cancel() { @@ -159,7 +170,6 @@ SCXML_STOP_SEARCH: _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); } - } void InterpreterImpl::init() { @@ -169,6 +179,33 @@ void InterpreterImpl::init() { setupDOM(); + if (!envVarIsTrue("USCXML_NOCACHE_FILES")) { + // try to open chached data from resource directory + std::string sharedTemp = URL::getTempDir(true); + std::ifstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); + if (dataFS.is_open()) { + std::string cacheStr((std::istreambuf_iterator<char>(dataFS)), + std::istreambuf_iterator<char>()); + _cache = Data::fromJSON(cacheStr); + } + + // get md5 of current document + std::stringstream ss; + ss << *_document; + _md5 = md5(ss.str()); + + if (_cache.compound.find("InterpreterImpl") != _cache.compound.end() && + _cache.compound["InterpreterImpl"].compound.find("md5") != _cache.compound["InterpreterImpl"].compound.end() && + _cache.compound["InterpreterImpl"].compound["md5"].atom != _md5) { + + // that's not our cache! + _cache.clear(); + } + + _cache.compound["InterpreterImpl"].compound["baseURL"] = Data(std::string(_baseURL)); + _cache.compound["InterpreterImpl"].compound["md5"] = Data(_md5); + } + // register io processors std::map<std::string, IOProcessorImpl*> ioProcs = _factory->getIOProcessors(); for (auto ioProcIter = ioProcs.begin(); ioProcIter != ioProcs.end(); ioProcIter++) { diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h index 1243f7c..0efc70a 100644 --- a/src/uscxml/interpreter/InterpreterImpl.h +++ b/src/uscxml/interpreter/InterpreterImpl.h @@ -143,6 +143,10 @@ public: return Interpreter(shared_from_this()); } + virtual Data& getCache() { + return _cache; + } + /** DataModelCallbacks */ @@ -284,6 +288,7 @@ protected: Factory* _factory; URL _baseURL; + std::string _md5; MicroStep _microStepper; DataModel _dataModel; @@ -305,6 +310,8 @@ protected: std::set<std::string> _autoForwarders; std::set<InterpreterMonitor*> _monitors; + Data _cache; + private: void setupDOM(); }; diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index 1544e78..e3f8299 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -60,6 +60,9 @@ public: virtual std::set<InterpreterMonitor*> getMonitors() = 0; virtual Interpreter getInterpreter() = 0; virtual Logger getLogger() = 0; + + /** Saved State */ + virtual Data& getCache() = 0; }; /** diff --git a/src/uscxml/messages/Data.cpp b/src/uscxml/messages/Data.cpp index b2d08e4..de9644b 100644 --- a/src/uscxml/messages/Data.cpp +++ b/src/uscxml/messages/Data.cpp @@ -283,8 +283,6 @@ std::string Data::toJSON(const Data& data) { } else { if (data.type == Data::VERBATIM) { os << "\"\""; // empty string - } else { - os << "null"; } } return os.str(); diff --git a/src/uscxml/messages/Data.h b/src/uscxml/messages/Data.h index 2cf1945..17d37cb 100644 --- a/src/uscxml/messages/Data.h +++ b/src/uscxml/messages/Data.h @@ -73,6 +73,16 @@ public: ~Data() {} + void clear() { + type = VERBATIM; + compound.clear(); + array.clear(); + atom.clear(); + adoptedDoc.reset(); + binary = Blob(); + node = NULL; + } + bool empty() const { bool hasContent = (atom.length() > 0 || !compound.empty() || !array.empty() || binary || node); return !hasContent; @@ -101,18 +111,20 @@ public: return (!compound.empty() && compound.find(key) != compound.end()); } - Data& operator[](const std::string& key) { - return operator[](key.c_str()); - } +#ifndef SWIGIMPORTED - const Data& operator[](const std::string& key) const { - return operator[](key.c_str()); + Data& operator[](const std::string& key) { + return compound[key]; } Data& operator[](const char* key) { return compound[key]; } + const Data& operator[](const std::string& key) const { + return compound.at(key); + } + const Data& operator[](const char* key) const { return compound.at(key); } @@ -125,6 +137,7 @@ public: for (size_t i = 0; i < index; i++, arrayIter++) {} return *arrayIter; } +#endif const Data at(const std::string& key) const { return at(key.c_str()); @@ -179,14 +192,6 @@ public: static std::string toJSON(const Data& data); std::string asJSON() const; - - std::map<std::string, Data> getCompound() { - return compound; - } - void setCompound(const std::map<std::string, Data>& compound) { - this->compound = compound; - } - std::list<Data> getArray() { return array; } @@ -215,6 +220,15 @@ public: this->type = type; } + // Bug in SWIG 3.0.8 Python: Data in a map has to be fully qualified! + std::map<std::string, uscxml::Data> getCompound() { + return compound; + } + + void setCompound(const std::map<std::string, uscxml::Data>& compound) { + this->compound = compound; + } + #ifdef SWIGIMPORTED protected: #endif diff --git a/src/uscxml/util/Base64.hpp b/src/uscxml/util/Base64.hpp index e3cbe03..d9f9ac1 100644 --- a/src/uscxml/util/Base64.hpp +++ b/src/uscxml/util/Base64.hpp @@ -40,9 +40,9 @@ USCXML_API inline std::string base64Decode(const std::string& data) { base64_init_decodestate(ctx); char* out = (char*)malloc(data.size()); - base64_decode_block(data.data(), data.size(), out, ctx); + size_t size = base64_decode_block(data.data(), data.size(), out, ctx); free(ctx); - std::string result(out); + std::string result(out, size); free(out); return result; } diff --git a/src/uscxml/util/Convenience.cpp b/src/uscxml/util/Convenience.cpp index a0263d2..4f47449 100644 --- a/src/uscxml/util/Convenience.cpp +++ b/src/uscxml/util/Convenience.cpp @@ -24,22 +24,22 @@ namespace uscxml { NumAttr::NumAttr(const std::string& str) { - size_t valueStart = str.find_first_of("0123456789."); - if (valueStart != std::string::npos) { - size_t valueEnd = str.find_last_of("0123456789."); - if (valueEnd != std::string::npos) { - value = str.substr(valueStart, (valueEnd - valueStart) + 1); - size_t unitStart = str.find_first_not_of(" \t", valueEnd + 1); - if (unitStart != std::string::npos) { - size_t unitEnd = str.find_last_of(" \t"); - if (unitEnd != std::string::npos && unitEnd > unitStart) { - unit = str.substr(unitStart, unitEnd - unitStart); - } else { - unit = str.substr(unitStart, str.length() - unitStart); - } - } - } - } + size_t valueStart = str.find_first_of("0123456789."); + if (valueStart != std::string::npos) { + size_t valueEnd = str.find_last_of("0123456789."); + if (valueEnd != std::string::npos) { + value = str.substr(valueStart, (valueEnd - valueStart) + 1); + size_t unitStart = str.find_first_not_of(" \t", valueEnd + 1); + if (unitStart != std::string::npos) { + size_t unitEnd = str.find_last_of(" \t"); + if (unitEnd != std::string::npos && unitEnd > unitStart) { + unit = str.substr(unitStart, unitEnd - unitStart); + } else { + unit = str.substr(unitStart, str.length() - unitStart); + } + } + } + } } diff --git a/src/uscxml/util/Convenience.h b/src/uscxml/util/Convenience.h index cb4416d..c12e0e4 100644 --- a/src/uscxml/util/Convenience.h +++ b/src/uscxml/util/Convenience.h @@ -45,7 +45,7 @@ template <typename T> T strTo(std::string tmp) { class USCXML_API NumAttr { public: - NumAttr(const std::string& str); + NumAttr(const std::string& str); std::string value; std::string unit; }; diff --git a/src/uscxml/util/URL.cpp b/src/uscxml/util/URL.cpp index 0885b47..845a972 100644 --- a/src/uscxml/util/URL.cpp +++ b/src/uscxml/util/URL.cpp @@ -29,14 +29,17 @@ #include <curl/curl.h> #include <uriparser/Uri.h> +#include <sys/types.h> +#include <sys/stat.h> #ifdef _WIN32 #include <direct.h> +#include <Shlobj.h> #define getcwd _getcwd #else #include <unistd.h> // getcwd -//#include <pwd.h> +#include <pwd.h> // getpwuid #endif #define DOWNLOAD_IF_NECESSARY if (!_isDownloaded) { download(true); } @@ -44,6 +47,157 @@ namespace uscxml { +std::string URL::currTmpDir; + +std::string URL::getTempDir(bool shared) { + +#ifdef _WIN32 + struct stat st = { 0 }; + TCHAR tmpPrefix [MAX_PATH]; + + // retrieve and optionally create temporary directory + if (!GetTempPath(MAX_PATH, tmpPrefix)) { + ERROR_EXECUTION_THROW(std::string("Cannot retrieve temporary directory")); + } + if (stat(tmpPrefix, &st) == -1) { + CreateDirectory(tmpPrefix, NULL); + if (GetLastError() == ERROR_PATH_NOT_FOUND) { + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory at '") + tmpPrefix + "'"); + } + } + + if (shared) { + // create uscxml folder in temporary directory + std::string sharedTmpDir = std::string(tmpPrefix) + PATH_SEPERATOR + "uscxml"; + + if (stat(sharedTmpDir.c_str(), &st) == -1) { + CreateDirectory (sharedTmpDir.c_str(), NULL); + if (GetLastError() == ERROR_PATH_NOT_FOUND) { + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory at '") + sharedTmpDir + "'"); + } + } + return sharedTmpDir; + + } else { + if (currTmpDir.size() == 0) { + // create random folder in temporary directory + // http://stackoverflow.com/questions/6287475/creating-a-unique-temporary-directory-from-pure-c-in-windows + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363875(v=vs.85).aspx + TCHAR tempFileName[MAX_PATH]; + if (GetTempFileName(tmpPrefix, // directory for tmp files + TEXT("uscxml."), // temp file name prefix + 0, // create unique name + tempFileName)) { + DeleteFile(tempFileName); + + if (stat(tempFileName, &st) == -1) { + CreateDirectory(tempFileName, NULL); + if (GetLastError() == ERROR_PATH_NOT_FOUND) { + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory at '") + tempFileName + "'"); + } + } + + currTmpDir = tempFileName; + } else { + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory at '") + tmpPrefix + "'"); + } + } + return currTmpDir; + } + +#else + std::string tmpPrefix = "/tmp"; + const char* tmpEnv = getenv("TMPDIR"); + if (tmpEnv != NULL) + tmpPrefix = tmpEnv; + + if (tmpPrefix[tmpPrefix.size() - 1] != PATH_SEPERATOR) + tmpPrefix += PATH_SEPERATOR; + + if (shared) { + struct stat st = {0}; + + std::string sharedTmpDir = tmpPrefix + "uscxml"; + if (stat(sharedTmpDir.c_str(), &st) == -1) { + int err = mkdir(sharedTmpDir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); // 777 + if (err) + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory at '") + sharedTmpDir + "':" + strerror(errno)); + + } + return sharedTmpDir; + + } else { + if (currTmpDir.size() == 0) { + std::string tmpPrefix = "/tmp"; + const char* tmpEnv = getenv("TMPDIR"); + if (tmpEnv != NULL) + tmpPrefix = tmpEnv; + const char* tmpName = mkdtemp((char*)std::string(tmpPrefix + "uscxml.XXXXXX").c_str()); // can we merely drop the constness? + if (tmpName != NULL) { + currTmpDir = tmpName; + } else { + ERROR_EXECUTION_THROW(std::string("Cannot create a temporary directory:") + strerror(errno)); + } + } + return currTmpDir; + } +#endif +} + +// Version for MacOSX in URL.mm +#if (!defined APPLE && !defined IOS) +std::string URL::getResourceDir() { +#ifdef _WIN32 + TCHAR szPath[MAX_PATH]; + std::string resourceDir; + if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, szPath) == S_OK) { + resourceDir = std::string(szPath) + PATH_SEPERATOR + "uscxml"; + } else { + char* envData = getenv("APPDATA"); + if (envData) { + resourceDir = std::string(envData) + PATH_SEPERATOR + "uscxml"; + } else { + ERROR_EXECUTION_THROW("Could not get resource directory"); + } + } + + struct stat st = { 0 }; + + if (stat(resourceDir.c_str(), &st) == -1) { + CreateDirectory(resourceDir.c_str(), NULL); + if (GetLastError() == ERROR_PATH_NOT_FOUND) { + ERROR_EXECUTION_THROW(std::string("Cannot create a resource directory at '") + resourceDir + "'"); + } + } + return resourceDir; + +#else + struct passwd* pw = getpwuid(getuid()); + std::string homedir(pw->pw_dir); + struct stat dirStat; + int err = 0; + + err = stat(std::string(homedir + PATH_SEPERATOR + ".config").c_str(), &dirStat); + if (err == ENOENT) { + err = mkdir(std::string(homedir + PATH_SEPERATOR + ".config").c_str(), S_IWUSR | S_IRUSR | S_IROTH); + } + + err = stat(std::string(homedir + PATH_SEPERATOR + ".config" + PATH_SEPERATOR + "uscxml").c_str(), &dirStat); + if (err != 0) { + // std::cout << std::string(homedir + PATH_SEPERATOR + ".config" + PATH_SEPERATOR + "uscxml") << std::endl; + err = mkdir(std::string(homedir + PATH_SEPERATOR + ".config" + PATH_SEPERATOR + "uscxml").c_str(), + S_IWUSR | S_IRUSR | S_IROTH | S_IRGRP | S_IXUSR | S_IXOTH | S_IXGRP); + } + + err = stat(std::string(homedir + PATH_SEPERATOR + ".config" + PATH_SEPERATOR + "uscxml").c_str(), &dirStat); + if (err == 0) { + return homedir + PATH_SEPERATOR + ".config" + PATH_SEPERATOR + "uscxml"; + } + return ""; +#endif +} +#endif + void URLImpl::prepareException(ErrorEvent& exception, int errorCode, const std::string& origUri, void* parser) { exception.data.compound["uri"].atom = origUri; @@ -808,9 +962,19 @@ void URLFetcher::perform() { rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); switch(rc) { - case -1: + case -1: { /* select error */ +#ifdef _WIN32 + char *s = NULL; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&s, 0, NULL); + LOGD(USCXML_WARN) << "select: " << s; + LocalFree(s); +#endif break; + } case 0: /* timeout */ default: { /* action */ std::lock_guard<std::recursive_mutex> lock(_mutex); diff --git a/src/uscxml/util/URL.h b/src/uscxml/util/URL.h index c83e3b1..9ad2f8e 100644 --- a/src/uscxml/util/URL.h +++ b/src/uscxml/util/URL.h @@ -136,6 +136,19 @@ public: URL(const std::string url) : _impl(new URLImpl(url)) {} + /** + * Get a persistant, shared directory for resources + * @return A path to an existing directory for resources. + */ + static std::string getResourceDir(); + + /** + * Get a temporary, shared or private directory for resources + * @param shared Whether the temporary directory is shared among instances. + * @return A path to an existing directory for temporary files. + */ + static std::string getTempDir(bool shared = true); + bool isAbsolute() { return _impl->isAbsolute(); } @@ -241,6 +254,8 @@ public: protected: std::shared_ptr<URLImpl> _impl; friend class URLFetcher; + static std::string currTmpDir; + }; class USCXML_API URLFetcher { @@ -270,6 +285,7 @@ protected: std::map<void*, void*> _handlesToHeaders; void* _multiHandle = NULL; char* _envProxy = NULL; + }; } diff --git a/src/uscxml/util/URL.mm b/src/uscxml/util/URL.mm new file mode 100644 index 0000000..881b3e2 --- /dev/null +++ b/src/uscxml/util/URL.mm @@ -0,0 +1,62 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "URL.h" +#include "Foundation/Foundation.h" + +#ifdef __has_feature +# if __has_feature(objc_arc) +# define(HAS_AUTORELEASE_POOL) +# endif +#endif + +namespace uscxml { + +std::string URL::getResourceDir() { + std::string path; +#if HAS_AUTORELEASE_POOL + @autoreleasepool { +#else + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif + NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier]; + if (bundleId != nil) { + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + path = [resourcePath cStringUsingEncoding:NSUTF8StringEncoding]; + } else { + // we are not actually in a bundle, fall back to /tmp + NSFileManager *fm = [NSFileManager defaultManager]; + NSURL* resURL = [[fm homeDirectoryForCurrentUser] URLByAppendingPathComponent:@".uscxml"]; + NSString* resPath = [NSString stringWithUTF8String:[resURL fileSystemRepresentation]]; + + if (![fm fileExistsAtPath:resPath]) { + [fm createDirectoryAtPath:resPath withIntermediateDirectories:YES attributes:nil error:nil]; + } + path = [resPath cStringUsingEncoding:NSUTF8StringEncoding]; + } + +#if HAS_AUTORELEASE_POOL + } +#else + [pool drain]; +#endif + return path; +} + +} |