summaryrefslogtreecommitdiffstats
path: root/src/uscxml
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2017-01-08 21:59:18 (GMT)
committerStefan Radomski <github@mintwerk.de>2017-01-08 21:59:18 (GMT)
commit030f3b483f54dbef6e164194a1771ef5b346312b (patch)
tree3f5b949b5ffed83d0b41a95d9fd3cfafd17cab2d /src/uscxml
parent1ab8b9a0dcaa131b8cccc735a1794ce39b351715 (diff)
downloaduscxml-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/uscxml')
-rw-r--r--src/uscxml/CMakeLists.txt10
-rw-r--r--src/uscxml/debug/DebugSession.cpp147
-rw-r--r--src/uscxml/debug/DebugSession.h13
-rw-r--r--src/uscxml/debug/DebuggerServlet.cpp2
-rw-r--r--src/uscxml/debug/DebuggerServlet.h2
-rw-r--r--src/uscxml/debug/InterpreterIssue.cpp4
-rw-r--r--src/uscxml/interpreter/BasicEventQueue.cpp18
-rw-r--r--src/uscxml/interpreter/FastMicroStep.cpp188
-rw-r--r--src/uscxml/interpreter/FastMicroStep.h33
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.cpp39
-rw-r--r--src/uscxml/interpreter/InterpreterImpl.h7
-rw-r--r--src/uscxml/interpreter/MicroStepImpl.h3
-rw-r--r--src/uscxml/messages/Data.cpp2
-rw-r--r--src/uscxml/messages/Data.h40
-rw-r--r--src/uscxml/util/Base64.hpp4
-rw-r--r--src/uscxml/util/Convenience.cpp32
-rw-r--r--src/uscxml/util/Convenience.h2
-rw-r--r--src/uscxml/util/URL.cpp168
-rw-r--r--src/uscxml/util/URL.h16
-rw-r--r--src/uscxml/util/URL.mm62
20 files changed, 617 insertions, 175 deletions
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;
+}
+
+}