From b510cc5e7bfce29b56838af62912268e3ac5b7e5 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 11 Nov 2021 15:05:49 +0100 Subject: [PATCH 01/51] fix tab to space --- src/bare_etiss_processor/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bare_etiss_processor/main.cpp b/src/bare_etiss_processor/main.cpp index ba60edf0f5..55ab2f265b 100644 --- a/src/bare_etiss_processor/main.cpp +++ b/src/bare_etiss_processor/main.cpp @@ -97,7 +97,7 @@ int main(int argc, const char *argv[]) // create a cpu core named core0 with the or1k architecture std::string CPUArchName = etiss::cfg().get("arch.cpu", ""); etiss::uint64 sa = etiss::cfg().get("vp.entry_point", dsys.get_startaddr()); - std::cout << " CPU start address: 0x" << std::hex << sa << std::dec << std::endl; + std::cout << " CPU start address: 0x" << std::hex << sa << std::dec << std::endl; std::shared_ptr cpu = etiss::CPUCore::create(CPUArchName, "core0"); if (!cpu) { @@ -186,5 +186,3 @@ int main(int argc, const char *argv[]) break; } } - - From 68ecbfcba7e89d59cba59876fbc480ca9b9cfc2e Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:18:32 +0100 Subject: [PATCH 02/51] Add initialize_virtualstruct as separate function. Full functionality of VirtualStruct require it being mounted with an instance of CPUCore::getStruct(). Move 'faults.xml' configuration resolve from etiss::initialize to initialize_virtual (requires mounted VirtualStruct). Add 'faults.xml' configuration as command line parameter. Make VirtualStruct::Field::flags_ member a non-const to allow runtime configuration of access type. --- include/etiss/ETISS.h | 15 +++++++++ include/etiss/VirtualStruct.h | 2 +- src/ETISS.cpp | 63 +++++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/include/etiss/ETISS.h b/include/etiss/ETISS.h index 9ecd13ad54..2698e501e0 100644 --- a/include/etiss/ETISS.h +++ b/include/etiss/ETISS.h @@ -217,6 +217,21 @@ void initialize(std::vector& args); */ std::shared_ptr getDefaultJIT(); +/** + * @brief Initialize and configure etiss::VirtualStruct root with etiss::CPUCore + * \p cpu_core. + * + * @detail This function also initializes configured faults which require a + * mounted \p core etiss::VirtualStruct. Add the virtual structure of the cpu to + * the VirtualStruct root. This allows to access the field of the cpu from a + * global context. See etiss::VirtualStruct::getVirtualStruct() and + * etiss::VirtualStruct::getResolvedField(). In this case e.g. the + * instructionPointer can be read from a global context by calling + * etiss::VirtualStruct::root()->getResolvedField("core0.instructionPointer") + * ->read(). + */ +void initialize_virtualstruct(std::shared_ptr cpu_core); + /** * @brief Shutdown ETISS * diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index 00df077b12..5f931494a6 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -217,7 +217,7 @@ class VirtualStruct : public std::enable_shared_from_this, public ///< "SR" OR1K CPU register: SPR[0][17] const std::string prettyname_; ///< alternative/human readable name of the field. e.g. representing the "SR" ///< OR1K CPU register: "SR" or "SupervisorRegister" - const int flags_; ///< read write flags as specified by the static const int parameters of Field: R,W,L + int flags_; ///< read write flags as specified by the static const int parameters of Field: R,W,L const size_t width_; ///< width in bytes (rounded up if neccessary) const size_t bitwidth_; ///< width in bits const AccessMode accessMode_; diff --git a/src/ETISS.cpp b/src/ETISS.cpp index 0299b98e38..ba91ff778b 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -35,6 +35,7 @@ #include "etiss/ETISS.h" #include "etiss/fault/Stressor.h" +#include "etiss/IntegratedLibrary/InstructionAccurateCallback.h" #include #include @@ -742,6 +743,7 @@ void etiss_initialize(const std::vector& args, bool forced = false) ("vp.sw_binary_rom", po::value(), "Path to binary file to be loaded into ROM.") ("vp.elf_file", po::value(), "Load ELF file.") ("vp.stats_file_path", po::value(), "Path where the output json file gets stored after bare processor is run.") + ("faults.xml", po::value(), "Path to faults XML file.") ("simple_mem_system.print_dbus_access", po::value(), "Traces accesses to the data bus.") ("simple_mem_system.print_ibus_access", po::value(), "Traces accesses to the instruction bus.") ("simple_mem_system.print_dbgbus_access", po::value(), "Traces accesses to the debug bus.") @@ -856,19 +858,6 @@ void etiss_initialize(const std::vector& args, bool forced = false) } } } - - // load fault files - { - std::string faults = cfg().get("faults.xml", ""); - if (!faults.empty()) - { - std::list ffs = etiss::split(faults, ';'); - for (auto ff : ffs) - { - etiss::fault::Stressor::loadXML(ff); - } - } - } } void etiss::initialize(std::vector& args) @@ -904,6 +893,54 @@ static class helper_class_etiss_1 bool etiss_shutdownOk = false; + +void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) +{ + auto mount_successful = etiss::VirtualStruct::root()->mountStruct(cpu_core->getName(), cpu_core->getStruct()); + + if(!mount_successful) + { + etiss::log(etiss::FATALERROR, std::string("Failed to mount ") + cpu_core->getName() + std::string("'s VirtualStruct, but failed: etiss::CPUCore not created!")); + } + else + { + etiss::log(etiss::VERBOSE, std::string("Mounted ") + cpu_core->getName() + std::string("'s VirtualStruct to root VirtualStruct")); + + // load fault files + std::string faults = cfg().get("faults.xml", ""); + if (!faults.empty()) + { + std::list ffs = etiss::split(faults, ';'); + for (auto ff : ffs) + { + auto stressor_successful = etiss::fault::Stressor::loadXML(ff, cpu_core->getID()); + if (!stressor_successful) + { + etiss::log(etiss::FATALERROR, std::string("Failed to load requested faults.xml \'") + ff + std::string("\' for ") + cpu_core->getName() + std::string(".")); + } + else + { + etiss::log(etiss::VERBOSE, std::string("Faults from \'") + ff + std::string("\' loaded for ") + cpu_core->getName() + std::string(".")); + } + } + + etiss::log(etiss::VERBOSE, std::string("Add InstructionAccurateCallback Plugin to ") + cpu_core->getName() + std::string(". Required for etiss::fault::Injector.")); + cpu_core->addPlugin(std::shared_ptr(new etiss::plugin::InstructionAccurateCallback())); + +#if defined(ETISS_DEBUG) + // Add applyCustomAction to for self test, can be overwritten by reassigning applyCustomAction of cpu_core + cpu_core->getStruct()->applyCustomAction = [](const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg) { + etiss::log(etiss::VERBOSE, std::string("Fault \'") + fault.name_ + std::string("\' Action: \'") + action.toString() + std::string("\'.")); + return true; + }; +#endif // defined(ETISS_DEBUG) + cpu_core->getStruct()->foreachField([](std::shared_ptr f) { + f->flags_ |= VirtualStruct::Field::F; + }); // enable Bitflip actions for all fields in cpu, by default only R|W is enabled, if we want to do basic Fault Injection into any of them, we need the F flag + } + } +} + void etiss::shutdown() { if (etiss_shutdownOk) // only on shutdown From fbfd3b88050a37910f34e2e9e23d29e501a38a7d Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:21:40 +0100 Subject: [PATCH 03/51] Allow Trigger::NOP and Action::NOP both can be configured (or injected), but are discarded. --- src/fault/Stressor.cpp | 55 ++++++++++++++++++++++++++---------------- src/fault/Trigger.cpp | 10 ++++++-- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index e9f44389b4..e89256662d 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -166,29 +166,36 @@ bool Stressor::addFault(const Fault &f) // Iterate through triggers of the fault for (std::vector::const_iterator iter = f.triggers.begin(); iter != f.triggers.end(); ++iter) { - Injector::ptr iptr = iter->getInjector(); - - if (iptr) + if(iter->getType() != etiss::fault::Trigger::NOP) // only add Trigger, if it is not a NOP { + Injector::ptr iptr = iter->getInjector(); + + if (iptr) + { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << iter->toString() << std::endl; + std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << iter->toString() << std::endl; #else - etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), - *iter); + etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), + *iter); #endif - iptr->addTrigger(*iter, f.id_); - } - else - { + iptr->addTrigger(*iter, f.id_); + } + else + { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Error: Injector not found for: " << iter->toString() - << std::endl; + std::cout << "etiss::fault::Stressor::addFault: Error: Injector not found for: " << iter->toString() + << std::endl; #else - etiss::log(etiss::ERROR, - std::string("etiss::fault::Stressor::addFault:") + std::string(" Injector not found for "), - *iter); + etiss::log(etiss::ERROR, + std::string("etiss::fault::Stressor::addFault:") + std::string(" Injector not found for "), + *iter); #endif - /// TODO signal error and roll back + /// TODO signal error and roll back + } + } + else // Trigger is of type NOP + { + etiss::log(etiss::WARNING, std::string("etiss::fault::Stressor::addFault:") + std::string(" Trigger is a NOP and is not added.")); } } @@ -201,7 +208,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #if CXX0X_UP_SUPPORTED std::lock_guard lock(faults_sync()); #endif - + bool ret = true; // find fault in fault-map std::map::iterator find = faults().find(fault_id); if (find != faults().end()) @@ -213,7 +220,12 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector if (iter->getType() == etiss::fault::Action::INJECTION) { /// TODO for time relative triggers resolve time must be called! - addFault(iter->getFault()); + addFault(iter->getFault(), true); + } + else if(iter->getType() == etiss::fault::Action::NOP) + { + etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); + return true; } else { @@ -234,7 +246,8 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #endif } std::string err; - if (!iter->getInjectorAddress().getInjector()->applyAction(find->second, *iter, err)) + bool ret_applyaction = iter->getInjectorAddress().getInjector()->applyAction(find->second, *iter, err); + if (!ret_applyaction) { #ifdef NO_ETISS std::cout << "Stressor::firedTrigger: Failed to apply action. Fault: " << fault_id << " [" @@ -244,7 +257,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector find->second, *iter, err); #endif } - return true; /// TODO: when returning true here. the next action will not be applied! + ret = ret && ret_applyaction; // mask return value with ret_applyaction foreach(!) action, return false, if one fails } else { @@ -268,7 +281,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #endif } - return true; + return ret; } void Stressor::clear() diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index 4119de889e..c7fd567f13 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -158,7 +158,6 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::fired(time_ps=") + std::to_string(time_ps) + std::string(", Injector*)")); - // std::cout << "Trigger::fired called at " << time_ps << " ps" << std::endl; switch (type_) { case META_COUNTER: @@ -238,6 +237,7 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) // return true; break; case NOP: + return true; break; } @@ -461,7 +461,13 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return false; } f = new Trigger(injector, count, true); - std::cout << "Injector2: " << f->getInjectorAddress().getInjectorPath() << std::endl; + etiss::log(etiss::VERBOSE, std::string("Injector2: ") + f->getInjectorAddress().getInjectorPath()); + return true; + } + + if (type == "NOP") + { + f = new Trigger(); return true; } From c0c4b0cd135089940b1db75089c1a0c20cc11422 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:23:39 +0100 Subject: [PATCH 04/51] fix deadlock when Action::INJECTION is results in Stressor::firedTrigger[lock mutex]->Stresser::addFault[lock mutex] --- include/etiss/fault/Stressor.h | 2 +- src/fault/Stressor.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index e7b1b562de..3b5b78506a 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -79,7 +79,7 @@ class Stressor * @param f the fault for adding to the map. * @return false if fault already exists in map. */ - static bool addFault(const Fault &f); + static bool addFault(const Fault &f, bool injected_fault = false); /** @brief Checks if the given trigger is valid and calls applyAction. * diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index e89256662d..12f4370049 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -135,10 +135,11 @@ bool Stressor::loadXML(const std::string &file, const int coreID) return ok; } -bool Stressor::addFault(const Fault &f) +bool Stressor::addFault(const Fault &f, bool injected_fault) { #if CXX0X_UP_SUPPORTED - std::lock_guard lock(faults_sync()); + if(!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur + std::lock_guard lock(faults_sync()); #endif #ifdef NO_ETISS From d0c70df48b028e763ae8be242ff5c6fe2569c848 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:24:06 +0100 Subject: [PATCH 05/51] Update example/bare_etiss_processor with new CPUCore instance forced initialize_virtualstruct call. Add WIP: Fault Injection How-To to examples/bare_etiss_processor/README.md and example `fault.xml` file for better reference. --- examples/bare_etiss_processor/faults.xml | 37 +++++++++++++++++ src/bare_etiss_processor/README.md | 51 ++++++++++++++++++++++++ src/bare_etiss_processor/main.cpp | 12 ++---- 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 examples/bare_etiss_processor/faults.xml diff --git a/examples/bare_etiss_processor/faults.xml b/examples/bare_etiss_processor/faults.xml new file mode 100644 index 0000000000..e63c2a0398 --- /dev/null +++ b/examples/bare_etiss_processor/faults.xml @@ -0,0 +1,37 @@ + + + + + 1 + + core%i% + instructionPointer + b0 + + + + + + core%i% + R1 + 1 + + + + + + + core%i% + instructionPointer + b8 + + + + + + + + + + + diff --git a/src/bare_etiss_processor/README.md b/src/bare_etiss_processor/README.md index a1c3d4e8e9..839c333436 100644 --- a/src/bare_etiss_processor/README.md +++ b/src/bare_etiss_processor/README.md @@ -182,3 +182,54 @@ When utilizing **gdbserver** to debug target software. ### ETISS output in terminal ![](etissSnapshot.png "ETISS output in terminal") + +## Fault Injection Demo + +Usage: + +- ./run_helper.sh ELFFILE --faults.xml=/path/to/faults.xml + +`faults.xml` Example: + +- `[]` are optional attributes +- `#` is a comment for this example + +``` + # a faults.xml root + # a has and + + # a must have a type of {META_COUNTER, VARIABLEVALUE, TIME, TIMERELATIVE, NOP} + 1 # the META_COUNTER here will trigger once its subtrigger fired times + # a can have a sub , awesome! + core%i% # the trigger type VARIABLEVALUE needs an that supplies the triggerable object, bare_etiss_processor constructs "core0" ETISS-FI will replace %i% to the core id "0" by default + instructionPointer # the of "core%i%" should be listened for, here, the instruction pointer + b0 # the that 's value will be compared to as a hexadecimal + + + + + # an must have a type of {BITFLIP, COMMAND, NOP, INJECTION}, here, Command + core%i% # an can have a different than its s, which means, you could trigger on one VirtualStruct and inject into another + test # the COMMAND type is a custom string encoded action the has to implement, by default no custom actions are supported. Set VirtualStructs::applyCustomAction member to a std::function of your choice and handle the passed string + + # why not have more than one , an additional "BITFLIP" + core%i% # again we need the + R1 # BITFLIP takes a , here R1 aka X1 in RISC-V, with the CPUCore all ISA-GPRs and the PC are + 1 # the target number + + # now, we inject a new fault as our action. + + + + + + + + + + + + + + +``` diff --git a/src/bare_etiss_processor/main.cpp b/src/bare_etiss_processor/main.cpp index 55ab2f265b..d9d1b0353a 100644 --- a/src/bare_etiss_processor/main.cpp +++ b/src/bare_etiss_processor/main.cpp @@ -110,14 +110,10 @@ int main(int argc, const char *argv[]) // reset CPU with a manual start address cpu->reset(&sa); - // add the virtual structure of the cpu to the VirtualStruct root. This allows - // to access the field of the cpu from a global context. See - // etiss::VirtualStruct::getVirtualStruct() and - // etiss::VirtualStruct::getResolvedField(). In this case e.g. the - // instructionPointer can be read from a global context by calling - // etiss::VirtualStruct::root()->getResolvedField("core0.instructionPointer") - // ->read(). - etiss::VirtualStruct::root()->mountStruct("core0", cpu->getStruct()); + // bind the cpu's VirtualStruct to etiss' root VirtualStruct and initialize faults + // if those where specified in config/cmdline + etiss::initialize_virtualstruct(cpu); + std::cout << "=== Finished Setting up test system ===" << std::endl << std::endl; std::cout << "=== Setting up plug-ins ===" << std::endl; From 54c0249fd3228b70fae8d96fb6be2f110be611d7 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:59:43 +0100 Subject: [PATCH 06/51] add *.xml example to examples cmake install target --- src/bare_etiss_processor/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bare_etiss_processor/CMakeLists.txt b/src/bare_etiss_processor/CMakeLists.txt index 6612d78efc..42a0519a12 100644 --- a/src/bare_etiss_processor/CMakeLists.txt +++ b/src/bare_etiss_processor/CMakeLists.txt @@ -75,4 +75,4 @@ install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/ DESTINATION examples FILES_MATCHING PATTERN "*.ini" -) \ No newline at end of file +) From 3c005fdcd7ac168cdb32c78fda7c1404fce2df8a Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 12 Nov 2021 12:00:31 +0100 Subject: [PATCH 07/51] update README "PC" is called "instructionPointer" in ETISS --- src/bare_etiss_processor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bare_etiss_processor/README.md b/src/bare_etiss_processor/README.md index 839c333436..ca16eb6680 100644 --- a/src/bare_etiss_processor/README.md +++ b/src/bare_etiss_processor/README.md @@ -214,7 +214,7 @@ Usage: # why not have more than one , an additional "BITFLIP" core%i% # again we need the - R1 # BITFLIP takes a , here R1 aka X1 in RISC-V, with the CPUCore all ISA-GPRs and the PC are + R1 # BITFLIP takes a , here R1 aka X1 in RISC-V, with the CPUCore all ISA-GPRs and the instructionPointer are 1 # the target number # now, we inject a new fault as our action. From 58f6822cca976595cdd64b101cccc1c766acd58e Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 12 Nov 2021 13:47:37 +0100 Subject: [PATCH 08/51] overload initialize_virtualstruct to allow direct setting of a custom action for CPUCore's VirtualStruct --- include/etiss/ETISS.h | 6 ++++++ src/ETISS.cpp | 14 ++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/etiss/ETISS.h b/include/etiss/ETISS.h index 2698e501e0..87c414377f 100644 --- a/include/etiss/ETISS.h +++ b/include/etiss/ETISS.h @@ -231,6 +231,12 @@ std::shared_ptr getDefaultJIT(); * ->read(). */ void initialize_virtualstruct(std::shared_ptr cpu_core); +/** + * @brief Extension of etiss::initialize_virtualstruct( + * std::shared_ptr) to allow direct setting of custom action for + * \p cpu_core etiss::VirtualStruct with \p fcustom_action. + */ +void initialize_virtualstruct(std::shared_ptr cpu_core, std::function const & fcustom_action); /** * @brief Shutdown ETISS diff --git a/src/ETISS.cpp b/src/ETISS.cpp index ba91ff778b..25de970e19 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -893,7 +893,6 @@ static class helper_class_etiss_1 bool etiss_shutdownOk = false; - void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) { auto mount_successful = etiss::VirtualStruct::root()->mountStruct(cpu_core->getName(), cpu_core->getStruct()); @@ -927,13 +926,6 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) etiss::log(etiss::VERBOSE, std::string("Add InstructionAccurateCallback Plugin to ") + cpu_core->getName() + std::string(". Required for etiss::fault::Injector.")); cpu_core->addPlugin(std::shared_ptr(new etiss::plugin::InstructionAccurateCallback())); -#if defined(ETISS_DEBUG) - // Add applyCustomAction to for self test, can be overwritten by reassigning applyCustomAction of cpu_core - cpu_core->getStruct()->applyCustomAction = [](const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg) { - etiss::log(etiss::VERBOSE, std::string("Fault \'") + fault.name_ + std::string("\' Action: \'") + action.toString() + std::string("\'.")); - return true; - }; -#endif // defined(ETISS_DEBUG) cpu_core->getStruct()->foreachField([](std::shared_ptr f) { f->flags_ |= VirtualStruct::Field::F; }); // enable Bitflip actions for all fields in cpu, by default only R|W is enabled, if we want to do basic Fault Injection into any of them, we need the F flag @@ -941,6 +933,12 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) } } +void etiss::initialize_virtualstruct(std::shared_ptr cpu_core, std::function const & fcustom_action) +{ + etiss::initialize_virtualstruct(cpu_core); + cpu_core->getStruct()->applyCustomAction = fcustom_action; +} + void etiss::shutdown() { if (etiss_shutdownOk) // only on shutdown From 4ee17e68373314fed3116cac2b990e65443cc046 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:02:53 +0100 Subject: [PATCH 09/51] update VirtualStruct::Field access flags on Stressor::addFault, e.g., fault injection flag of a field will be set if a fault for the field is configured through Stressor. Add "MASK" fault as Action. A Mask fault has a mask operation (and, or, xor, nand, nor) and a mask value (64-bit) acting on a : " = " --- include/etiss/VirtualStruct.h | 1 + include/etiss/fault/Action.h | 48 +++++++++- include/etiss/fault/Injector.h | 6 ++ src/ETISS.cpp | 4 - src/VirtualStruct.cpp | 84 +++++++++++++++-- src/fault/Action.cpp | 165 ++++++++++++++++++++++++++++++--- src/fault/Stressor.cpp | 25 ++++- 7 files changed, 302 insertions(+), 31 deletions(-) diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index 5f931494a6..b05dc21714 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -368,6 +368,7 @@ class VirtualStruct : public std::enable_shared_from_this, public virtual bool readField(void *fastfieldaccessptr, uint64_t &val, std::string &errormsg); virtual bool applyAction(const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg); + virtual bool update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string& errormsg); public: /// set this function to handle custom commands passed by etiss::fault::Action of the type diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 30fbda22ae..3f0afb0e02 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -79,16 +79,31 @@ class Action : public etiss::ToString public: enum Type { + /// NO Operation. used by default constructor + NOP = 0, /// applies a bit flip to a bit in a specified field BITFLIP, + /// applies a mask type injection (field = mask;) where can be any MaskOp + MASK, /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field that /// information must be passed within the command string COMMAND, - /// NO Operation. used by default constructor - NOP, /// an action that injects a fault definition (trigger + actions) INJECTION }; + enum MaskOp + { + AND, + OR, + XOR, + NAND, + NOR + }; + + /** + * @brief returns true if type_ is an action on a Field + */ + bool is_action_on_field(void) const; // Constructors /** @@ -107,6 +122,12 @@ class Action : public etiss::ToString * @brief the bit at the given position of the given field of the given injector will be flipped */ Action(const InjectorAddress &inj, const std::string &field, unsigned bit); + /** + * @note Type: MASK + * + * @brief applies a mask type injection (field op= mask;) where can be bitwise AND, OR, XOR, NAND, NOR + */ + Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value); /** * @note Type: Injection * @@ -116,6 +137,7 @@ class Action : public etiss::ToString // Getters Type getType() const; + const InjectorAddress &getInjectorAddress() const; /// COMMAND only @@ -128,21 +150,37 @@ class Action : public etiss::ToString /// INJECTION only const Fault &getFault() const; + MaskOp getMaskOp() const; + + uint64_t getMaskValue() const; + // Members std::string toString() const; ///< operator<< can be used. private: // Attributes - Type type_; ///< type of the Attribute - InjectorAddress inj_; + Type type_; ///< type of the Attribute + InjectorAddress inj_; ///< Address of Injector std::string command_; ///< command e.g. for booting OR1KVCPU std::string field_; ///< concerning Field (for fault injection) unsigned bit_; ///< concerning Bit (for fault injection) - std::vector fault_; ///< for other injections + MaskOp mask_op_; ///< mask operation (for mask injection) + uint64_t mask_value_; ///< mask value (for mask injection) + std::vector fault_; ///< for fault injection // private Members void ensure(Type); }; +/** + * @brief decode Action::MaskOp from string + */ +bool maskop_fromstring(Action::MaskOp& out, const std::string& in); +/** + * @brief encode Action::MaskOp to string + */ +std::string maskop_tostring(Action::MaskOp in); + + #if ETISS_FAULT_XML namespace xml diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index 6bfcb8cb9b..d8a21a8571 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -155,6 +155,12 @@ class Injector virtual bool acceleratedTrigger(const etiss::fault::Trigger &, int32_t fault_id); + /** + @brief Update the \p field of injector with access rights to allow \p type actions. + @detail For example, if \p action is of etiss::fault::Action::BITFLIP, \p field requires F flag set + */ + virtual bool update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string &errormsg) = 0; + public: // static /** @param injectorPath the full path/name to/off an injector. in case of using ETISS/VirtualStruct please have a look diff --git a/src/ETISS.cpp b/src/ETISS.cpp index 25de970e19..e366a7b24d 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -925,10 +925,6 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) etiss::log(etiss::VERBOSE, std::string("Add InstructionAccurateCallback Plugin to ") + cpu_core->getName() + std::string(". Required for etiss::fault::Injector.")); cpu_core->addPlugin(std::shared_ptr(new etiss::plugin::InstructionAccurateCallback())); - - cpu_core->getStruct()->foreachField([](std::shared_ptr f) { - f->flags_ |= VirtualStruct::Field::F; - }); // enable Bitflip actions for all fields in cpu, by default only R|W is enabled, if we want to do basic Fault Injection into any of them, we need the F flag } } } diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index e2e3011208..0112572064 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -178,9 +178,9 @@ bool VirtualStruct::Field::applyBitflip(unsigned position, uint64_t fault_id) bool VirtualStruct::Field::applyAction(const etiss::fault::Fault &f, const etiss::fault::Action &a, std::string &errormsg) { - if (!(flags_ & A)) + if (!(flags_ & (A|F))) { - errormsg = "field doesn't support advanced action handling"; + errormsg = "field doesn't support action handling"; return false; } return _applyAction(f, a, errormsg); @@ -214,6 +214,38 @@ bool VirtualStruct::Field::_applyBitflip(unsigned position, uint64_t fault_id) bool VirtualStruct::Field::_applyAction(const etiss::fault::Fault &f, const etiss::fault::Action &a, std::string &errormsg) { + if(a.getType() == etiss::fault::Action::Type::MASK) + { + uint64_t mask_value = a.getMaskValue(); + uint64_t val = read(), errval; + switch (a.getMaskOp()) + { + case etiss::fault::Action::MaskOp::AND: + errval = (val & mask_value); + break; + case etiss::fault::Action::MaskOp::OR: + errval = (val | mask_value); + break; + case etiss::fault::Action::MaskOp::XOR: + errval = (val ^ mask_value); + break; + case etiss::fault::Action::MaskOp::NAND: + errval = ~(val & mask_value); + break; + case etiss::fault::Action::MaskOp::NOR: + errval = ~(val | mask_value); + break; + } + write(errval); + std::stringstream ss; + ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << etiss::fault::maskop_tostring(a.getMaskOp()) << " 0x" << mask_value << "->0x" << errval << std::dec; + etiss::log(etiss::INFO, ss.str()); + return true; + } + else if(a.getType() == etiss::fault::Action::Type::BITFLIP) + { + return applyBitflip(a.getTargetBit(), f.id_); + } return false; } @@ -476,6 +508,7 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } return applyCustomAction(fault, action, errormsg); } + case etiss::fault::Action::MASK: [[fallthrough]]; case etiss::fault::Action::BITFLIP: // handle bitflip { Field *f; @@ -502,14 +535,10 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f errormsg = std::string("No such field: ") + getInjectorPath() + "." + action.getTargetField(); return false; } - if (f->flags_ & Field::A) + if (f->flags_ & (Field::A | Field::F)) { return f->applyAction(fault, action, errormsg); } - else if (f->flags_ & Field::F) - { - return f->applyBitflip(action.getTargetBit(), fault.id_); - } } return false; default: @@ -517,6 +546,47 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } } +bool VirtualStruct::update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string &errormsg) +{ + Field* f = nullptr; + auto find = fieldNames_.find(field); + if (find == fieldNames_.end()) + { + find = fieldPrettyNames_.find(field); + if (find == fieldPrettyNames_.end()) + { + f = 0; + } + else + { + f = find->second; + } + } + else + { + f = find->second; + } + + if(f) + { + switch(type) + { + case etiss::fault::Action::Type::MASK: [[fallthrough]]; + case etiss::fault::Action::Type::BITFLIP: + f->flags_ |= Field::F; + break; + default: + break; + } + } + else + { + errormsg = std::string("VirtualStruct:update_field_access_rights(): Required field not a field in VirtualStruct!"); + return false; + } + return true; +} + VSSync::VSSync() { mutex().lock(); diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index c7d381f8e0..eba5fa17fc 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -53,32 +53,106 @@ namespace etiss namespace fault { +bool maskop_fromstring(Action::MaskOp& out, const std::string& in) +{ + if (in == "AND" or in == "&") + { + out = Action::MaskOp::AND; + } + else if (in == "OR" or in == "|") + { + out = Action::MaskOp::OR; + } + else if (in == "XOR" or in == "^") + { + out = Action::MaskOp::XOR; + } + else if (in == "NAND" or in == "!&") + { + out = Action::MaskOp::NAND; + } + else if (in == "NOR" or in == "!|") + { + out = Action::MaskOp::NOR; + } + else + { + return false; + } + return true; +} + +std::string maskop_tostring(Action::MaskOp in) +{ + switch(in) + { + case Action::MaskOp::AND: + return "AND"; + case Action::MaskOp::OR: + return "OR"; + case Action::MaskOp::XOR: + return "XOR"; + case Action::MaskOp::NAND: + return "NAND"; + case Action::MaskOp::NOR: + return "NOR"; + default: + return "NOP"; + } +} + void Action::ensure(Type t) { if (type_ != t) throw "wrong action type"; } -Action::Action() : type_(NOP) +bool Action::is_action_on_field(void) const +{ + return ((type_ == BITFLIP || type_ == MASK) ? true : false); +} + +Action::Action() + : type_(NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action() called. ")); } -Action::Action(const InjectorAddress &inj, const std::string &command) : type_(COMMAND), inj_(inj), command_(command) +Action::Action(const InjectorAddress &inj, const std::string &command) + : type_(COMMAND) + , inj_(inj) + , command_(command) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", command=") + command + std::string(") called. ")); } Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bit) - : type_(BITFLIP), inj_(inj), field_(field), bit_(bit) + : type_(BITFLIP) + , inj_(inj) + , field_(field) + , bit_(bit) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", bit=") + std::to_string(bit) + std::string(") called. ")); } -Action::Action(const Fault &fault) : type_(INJECTION) +Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value) + : type_(MASK) + , inj_(inj) + , field_(field) + , mask_op_(mask_op) + , mask_value_(mask_value) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + + std::string(", field=") + field + std::string(", mask_op=") + std::to_string(mask_op) + + std::string(", mask_value=") + std::to_string(mask_value) + + std::string(") called. ")); +} + +Action::Action(const Fault &fault) + : type_(INJECTION) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(Fault &=") + fault.toString() + std::string(") called. ")); @@ -92,34 +166,56 @@ Action::Type Action::getType() const const InjectorAddress &Action::getInjectorAddress() const { + if(unlikely(!(type_ == BITFLIP || type_ == MASK || type_ == COMMAND))) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type has no Injector")); return inj_; } /// COMMAND only const std::string &Action::getCommand() const { + if(unlikely(type_ != COMMAND)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getCommand(): Requested Action::Type has no Command")); return command_; } -/// BITFLIP only +/// is_action_on_field only const std::string &Action::getTargetField() const { + if(unlikely(!(type_ == BITFLIP || type_ == MASK))) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getTargetField(): Requested Action::Type has no TargetField")); return field_; } /// BITFLIP only unsigned Action::getTargetBit() const { + if(unlikely(type_ != BITFLIP)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getTargetBit(): Requested Action::Type has no TargetBit")); return bit_; } const Fault &Action::getFault() const { - if (type_ != INJECTION) - throw "Action doesn't have the type INJECTION: cannot call getFault()"; + if(unlikely(type_ != INJECTION)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getFault(): Requested Action::Type has no injectable Fault")); return fault_.front(); } +Action::MaskOp Action::getMaskOp() const +{ + if(unlikely(type_ != MASK)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getMaskOp(): Requested Action::Type has no Mask")); + return mask_op_; +} + +uint64_t Action::getMaskValue() const +{ + if(unlikely(type_ != MASK)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getMaskValue(): Requested Action::Type has no Mask")); + return mask_value_; +} + std::string Action::toString() const { pugi::xml_document doc; @@ -164,20 +260,20 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D return true; if (!parse(findSingleNode(node, "injector", diag), inj, diag)) { - diag.unexpectedNode(node, "Failed to parse target injector"); + diag.unexpectedNode(node, "Failed to parse target "); return false; } std::string field; if (!parse(findSingleNode(node, "field", diag), field, diag)) { - diag.unexpectedNode(node, "Failed to parse target field"); + diag.unexpectedNode(node, "Failed to parse target "); return false; } setCoreName(field); unsigned bit; if (!parse(findSingleNode(node, "bit", diag), bit, diag)) { - diag.unexpectedNode(node, "Failed to parse target bit"); + diag.unexpectedNode(node, "Failed to parse target "); return false; } f = Action(inj, field, bit); @@ -188,13 +284,13 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D etiss::fault::InjectorAddress inj; if (!parse(findSingleNode(node, "injector", diag), inj, diag)) { - diag.unexpectedNode(node, "Failed to parse target injector"); + diag.unexpectedNode(node, "Failed to parse target "); return false; } std::string command; if (!parse(findSingleNode(node, "command", diag), command, diag)) { - diag.unexpectedNode(node, "Failed to parse target command"); + diag.unexpectedNode(node, "Failed to parse target "); return false; } f = Action(inj, command); @@ -205,12 +301,48 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D etiss::fault::Fault inj; if (!parse(findSingleNode(node, "fault", diag), inj, diag)) { - diag.unexpectedNode(node, "Failed to parse fault to inject"); + diag.unexpectedNode(node, "Failed to parse to inject"); return false; } f = Action(inj); return true; } + else if (type == "MASK") + { + etiss::fault::InjectorAddress inj; + if (!parse(findSingleNode(node, "injector", diag), inj, diag)) + { + diag.unexpectedNode(node, "Failed to parse target "); + return false; + } + std::string field; + if (!parse(findSingleNode(node, "field", diag), field, diag)) + { + diag.unexpectedNode(node, "Failed to parse target "); + return false; + } + setCoreName(field); + std::string op_str; + etiss::fault::Action::MaskOp op; + if (!parse(findSingleNode(node, "op", diag), op_str, diag)) + { + diag.unexpectedNode(node, "Failed to parse mask operation "); + return false; + } + if (! etiss::fault::maskop_fromstring(op, op_str)) + { + diag.unexpectedNode(node, "Failed to parse mask operation "); + return false; + } + uint64_t value; + if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) + { + diag.unexpectedNode(node, "Failed to parse mask operation "); + return false; + } + f = Action(inj, field, op, value); + return true; + } else { diag.unexpectedNode(node, std::string("Unknown type of action: ") + type); @@ -243,6 +375,13 @@ bool write(pugi::xml_node node, const etiss::fault::Action ok = ok & write_attr(node, "type", "INJECTION", diag); ok = ok & write(node.append_child("fault"), f.getFault(), diag); break; + case etiss::fault::Action::MASK: + ok = ok & write_attr(node, "type", "MASK", diag); + ok = ok & write(node.append_child("injector"), f.getInjectorAddress(), diag); + ok = ok & write(node.append_child("field"), f.getTargetField(), diag); + ok = ok & write(node.append_child("op"), etiss::fault::maskop_tostring(f.getMaskOp()), diag); + ok = ok & write(node.append_child("value"), f.getMaskValue(), diag); + break; } return false; } diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 12f4370049..1d75d04937 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -164,14 +164,16 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) // insert fault into map faults().insert(std::pair(f.id_, f)); + Injector::ptr iptr = nullptr; + // Iterate through triggers of the fault for (std::vector::const_iterator iter = f.triggers.begin(); iter != f.triggers.end(); ++iter) { if(iter->getType() != etiss::fault::Trigger::NOP) // only add Trigger, if it is not a NOP { - Injector::ptr iptr = iter->getInjector(); + iptr = iter->getInjector(); - if (iptr) + if (iptr != nullptr) { #ifdef NO_ETISS std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << iter->toString() << std::endl; @@ -179,6 +181,7 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), *iter); #endif + //TODO: iptr->enable_faulttype for requested field iptr->addTrigger(*iter, f.id_); } else @@ -200,6 +203,24 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) } } + if(iptr != nullptr) + { + for ( const auto& it: f.actions) + { + if(it.is_action_on_field()) + { + bool ret_update = false; + std::string errormsg; + ret_update = iptr->update_field_access_rights( it.getTargetField(), it.getType(), errormsg ); + if (! ret_update) + { + etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::addFault:") + errormsg); + } + } + + } + } + return true; } From fad029c28aab875e7cce6526ad778413e30247da Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 19 Nov 2021 20:08:14 +0100 Subject: [PATCH 10/51] Add smart static mapping of types to their string representatives (from xml). Add event type to types which allows to inject ETISS simulation events (all ETISS::RETURNCODES), e.g., to flush translation cache. Minor fixes in mask type --- .../InstructionAccurateCallback.h | 2 +- include/etiss/fault/Action.h | 77 +++-- include/etiss/fault/Misc.h | 130 ++++++++ include/etiss/fault/Stressor.h | 25 ++ .../InstructionAccurateCallback.cpp | 31 +- src/Misc.cpp | 10 +- src/VirtualStruct.cpp | 9 +- src/fault/Action.cpp | 307 +++++++++++++----- src/fault/Stressor.cpp | 22 +- 9 files changed, 484 insertions(+), 129 deletions(-) create mode 100644 include/etiss/fault/Misc.h diff --git a/include/etiss/IntegratedLibrary/InstructionAccurateCallback.h b/include/etiss/IntegratedLibrary/InstructionAccurateCallback.h index c418282588..011e2eb8fe 100644 --- a/include/etiss/IntegratedLibrary/InstructionAccurateCallback.h +++ b/include/etiss/IntegratedLibrary/InstructionAccurateCallback.h @@ -69,7 +69,7 @@ class InstructionAccurateCallback : public etiss::TranslationPlugin virtual std::string _getPluginName() const; public: - void call(); + etiss_int32 call_on_entry(); }; } // namespace plugin diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 3f0afb0e02..42bf5ea0d0 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -60,11 +60,13 @@ #include "etiss/fault/Fault.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/XML.h" +#include "etiss/fault/Misc.h" #else #include "fault/Defs.h" #include "fault/Fault.h" #include "fault/InjectorAddress.h" #include "fault/XML.h" +#include "fault/Misc.h" #endif namespace etiss @@ -77,29 +79,30 @@ class Fault; class Action : public etiss::ToString { public: - enum Type + enum class Type { - /// NO Operation. used by default constructor - NOP = 0, - /// applies a bit flip to a bit in a specified field - BITFLIP, - /// applies a mask type injection (field = mask;) where can be any MaskOp - MASK, - /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field that - /// information must be passed within the command string - COMMAND, - /// an action that injects a fault definition (trigger + actions) - INJECTION + NOP = 0 /// NO Operation. used by default constructor + , BITFLIP /// applies a bit flip to a bit in a specified field + , MASK /// applies a mask type injection (field = mask;) where can be any MaskOp + , COMMAND /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field that + /// information must be passed within the command string + , INJECTION /// an action that injects a fault definition (trigger + actions) + #ifndef NO_ETISS + /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE exception + , EVENT + #endif }; - enum MaskOp + typedef SmartType type_t; + enum class MaskOp { - AND, - OR, - XOR, - NAND, - NOR + NOP, + AND, + OR, + XOR, + NAND, + NOR }; - + typedef SmartType mask_op_t; /** * @brief returns true if type_ is an action on a Field */ @@ -110,6 +113,14 @@ class Action : public etiss::ToString * @note Type: NOP (no operation) */ Action(); +#ifndef NO_ETISS + /** + * @note Type: Exception + * + * @brief A etiss::RETURNCODE \p exception will be injected into the etiss simulation loop + */ + Action(etiss::int32 event); +#endif /** * @note Type: Command * @@ -136,7 +147,7 @@ class Action : public etiss::ToString Action(const Fault &fault); // Getters - Type getType() const; + const type_t& getType() const; const InjectorAddress &getInjectorAddress() const; @@ -150,36 +161,42 @@ class Action : public etiss::ToString /// INJECTION only const Fault &getFault() const; - MaskOp getMaskOp() const; + const mask_op_t& getMaskOp() const; uint64_t getMaskValue() const; - +#ifndef NO_ETISS + etiss::int32 getEvent() const; +#endif // Members std::string toString() const; ///< operator<< can be used. private: // Attributes - Type type_; ///< type of the Attribute + type_t type_; ///< type of the Attribute InjectorAddress inj_; ///< Address of Injector std::string command_; ///< command e.g. for booting OR1KVCPU std::string field_; ///< concerning Field (for fault injection) unsigned bit_; ///< concerning Bit (for fault injection) - MaskOp mask_op_; ///< mask operation (for mask injection) + mask_op_t mask_op_; ///< mask operation (for mask injection) uint64_t mask_value_; ///< mask value (for mask injection) std::vector fault_; ///< for fault injection - +#ifndef NO_ETISS + etiss::int32 event_; ///< exception, or rather etiss::RETURNCODE to + /// to be injected into the simulation loop +#endif // private Members void ensure(Type); }; +#ifndef NO_ETISS /** - * @brief decode Action::MaskOp from string + * @brief decode etiss::RETURNCODE from string */ -bool maskop_fromstring(Action::MaskOp& out, const std::string& in); +bool returncode_fromstring(etiss::int32& out, const std::string& in); /** - * @brief encode Action::MaskOp to string + * @brief encode etiss::RETURNCODE to string */ -std::string maskop_tostring(Action::MaskOp in); - +std::string returncode_tostring(etiss::int32 in); +#endif #if ETISS_FAULT_XML diff --git a/include/etiss/fault/Misc.h b/include/etiss/fault/Misc.h new file mode 100644 index 0000000000..1bdde8a994 --- /dev/null +++ b/include/etiss/fault/Misc.h @@ -0,0 +1,130 @@ +/** + + @copyright + +
+
+        Copyright 2018 Infineon Technologies AG
+
+        This file is part of ETISS tool, see .
+
+        The initial version of this software has been created with the funding support by the German Federal
+        Ministry of Education and Research (BMBF) in the project EffektiV under grant 01IS13022.
+
+        Redistribution and use in source and binary forms, with or without modification, are permitted
+        provided that the following conditions are met:
+
+        1. Redistributions of source code must retain the above copyright notice, this list of conditions and
+        the following disclaimer.
+
+        2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+        3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
+        or promote products derived from this software without specific prior written permission.
+
+        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+        DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+        PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+        HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+        POSSIBILITY OF SUCH DAMAGE.
+
+        
+ + @author Chair of Electronic Design Automation, TUM + + @date November 18, 2021 + + @version 0.1 + +*/ +/** + @file + + @brief general helpers for fault + + @detail + + + + +*/ + +#ifndef ETISS_INCLUDE_FAULT_MISC_H_ +#define ETISS_INCLUDE_FAULT_MISC_H_ + +#include +#include +#include + + +namespace etiss +{ +namespace fault +{ + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Action type class +template +class SmartType : public etiss::ToString +{ + public: + typedef std::map map_t; + private: + enum_t type_; + static map_t TABLE; + public: + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief decode a string \p type_str to \ref enum_t, return true if successful + static bool fromString(const std::string& type_str, enum_t& out) { + for(auto const& e: TABLE) + { + if (type_str == e.second) + { + out = e.first; + return true; + } + } + return false; + } + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief convert a \ref enum_t \p in to a std::string with the static \ref TABLE + static std::string toString(enum_t in) { + return TABLE.at(in); + } + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief return self as string + std::string toString() const { return toString(type_); } + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor takes \p type specifying action type + SmartType() : type_() {} + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor takes \p type specifying action type + SmartType(enum_t type) : type_(type) {} + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor takes \p type_str specifying action type string encoded + /// \note If string does not match, default \ref enum_t() is used. Make sure to adapt + SmartType(const std::string& type_str) { + auto ret = fromString(type_str, type_); + if(!ret) + etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") + + type_str + std::string("\" using default enum_t()")); + } + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Typecast operator to \ref code_t + operator enum_t() const { return type_; } + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Typecast operator to \ref code_t + operator std::string() const { + return toString(); + } +} /* class SmartType */ ; + +} // end of namespace fault + +} // end of namespace etiss + +#endif /* ETISS_INCLUDE_FAULT_MISC_H_ */ diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index 3b5b78506a..370a440a08 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -55,6 +55,7 @@ #ifndef NO_ETISS #include "etiss/fault/Fault.h" +#include "etiss/jit/ReturnCode.h" #else #include "fault/Fault.h" #endif @@ -68,6 +69,30 @@ namespace fault class Stressor { public: + enum Event { + TERMINATE = (1 << 0) +#ifndef NO_ETISS + , ETISS_FLUSH_TRANSLATION_CACHE = (1 << 1) + , ETISS_RELOAD_TRANSLATION_BLOCK = (1 << 2) +#else +#endif + }; + +#ifndef NO_ETISS + private: + static etiss::int32 event_code_; + public: + static etiss::int32 get_event(void) { return event_code_; } + static void set_event(etiss::int32 code) { event_code_ = code; } +#else + private: + static int event_code_; + public: + static int get_event(void) { return event_code_; } + static void set_event(int code) { event_code_ = code; } + static void set_event_flag(Event flag) { event_code_ |= flag; } +#endif + static void reset_event(void) {event_code_ = 0;} /** @brief extracts faults out of the given xml file. * @param file the xmlfile with fault triggers. * @return true if XML file could be loaded. diff --git a/src/IntegratedLibrary/InstructionAccurateCallback.cpp b/src/IntegratedLibrary/InstructionAccurateCallback.cpp index bd07731825..71a8c5aba7 100644 --- a/src/IntegratedLibrary/InstructionAccurateCallback.cpp +++ b/src/IntegratedLibrary/InstructionAccurateCallback.cpp @@ -41,15 +41,17 @@ */ #include "etiss/IntegratedLibrary/InstructionAccurateCallback.h" +#include "etiss/fault/Stressor.h" + #include "etiss/CPUCore.h" #include "etiss/Instruction.h" extern "C" { - void etiss_plugin_InstructionAccurateCallback(void *ptr) + etiss_int32 etiss_plugin_InstructionAccurateCallback_OnEntry(void *ptr) { etiss::plugin::InstructionAccurateCallback &vvl = *(etiss::plugin::InstructionAccurateCallback *)ptr; - vvl.call(); + return vvl.call_on_entry(); } } @@ -64,7 +66,7 @@ InstructionAccurateCallback::~InstructionAccurateCallback() {} void InstructionAccurateCallback::initCodeBlock(etiss::CodeBlock &block) const { - block.fileglobalCode().insert("extern void etiss_plugin_InstructionAccurateCallback(void *); "); + block.fileglobalCode().insert("extern etiss_int32 etiss_plugin_InstructionAccurateCallback_OnEntry(void *); "); } void InstructionAccurateCallback::finalizeInstrSet(etiss::instr::ModedInstructionSet &mis) const @@ -74,9 +76,8 @@ void InstructionAccurateCallback::finalizeInstrSet(etiss::instr::ModedInstructio is.foreach ([this](etiss::instr::Instruction &i) { i.addCallback( [this](etiss::instr::BitArray &, etiss::CodeSet &cs, etiss::instr::InstructionContext &) { - etiss::CodePart &p = cs.append(etiss::CodePart::INITIALREQUIRED); - p.getRegisterDependencies().add("cpuTime_ps", 8); - p.code() = std::string("etiss_plugin_InstructionAccurateCallback(") + getPointerCode() + ");"; + etiss::CodePart &pp = cs.prepend(etiss::CodePart::INITIALREQUIRED); + pp.code() = std::string("etiss_int32 ret_iac = etiss_plugin_InstructionAccurateCallback_OnEntry(") + getPointerCode() + ");\nif(ret_iac != 0)return(ret_iac);"; return true; }, 0); @@ -90,11 +91,25 @@ std::string InstructionAccurateCallback::_getPluginName() const return std::string("InstructionAccurateCallback"); } -void InstructionAccurateCallback::call() +etiss_int32 InstructionAccurateCallback::call_on_entry() { - plugin_core_->getStruct()->instructionAccurateCallback(plugin_cpu_->cpuTime_ps); + bool trigger_fired = false; + auto time = plugin_cpu_->cpuTime_ps; + + etiss::fault::Stressor::reset_event(); + + trigger_fired |= plugin_core_->getStruct()->instructionAccurateCallback(time); // call instruction callback of plugin-associated core + plugin_core_->getStruct()->foreachStruct( // call instruction callback of all VirtualStructs mounted on core + [time,&trigger_fired](const std::string &name, VirtualStruct &vs) + { + trigger_fired |= vs.instructionAccurateCallback(time); + } + ); + + return(trigger_fired ? etiss::fault::Stressor::get_event() : 0); // signal that a trigger has thrown an exception back to the JIT code calling } + } // namespace plugin } // namespace etiss diff --git a/src/Misc.cpp b/src/Misc.cpp index 8569e92c23..d48e361c39 100644 --- a/src/Misc.cpp +++ b/src/Misc.cpp @@ -391,7 +391,7 @@ std::pair etiss::Configuration::set_cmd_line_boost(con etiss::Configuration sobj; if (s.length() > 2) { - if (s.find("-f") == 0) + if (s.find("-f") == 0) { size_t epos = s.find_first_of('='); if (s.length() > 5 && s.substr(2, 3) == "no-") @@ -412,8 +412,8 @@ std::pair etiss::Configuration::set_cmd_line_boost(con etiss::log(etiss::VERBOSE, std::string("CONFIG: removed ") + tmp); return make_pair(std::string(), std::string()); } - } - else + } + else { if (epos == std::string::npos) { @@ -434,8 +434,8 @@ std::pair etiss::Configuration::set_cmd_line_boost(con return make_pair(s.substr(2), tval); } return make_pair(std::string(), std::string()); - } - } + } + } } return make_pair(std::string(), std::string()); } diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index 0112572064..5a7163c667 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -235,10 +235,13 @@ bool VirtualStruct::Field::_applyAction(const etiss::fault::Fault &f, const etis case etiss::fault::Action::MaskOp::NOR: errval = ~(val | mask_value); break; + case etiss::fault::Action::MaskOp::NOP: + errval = val; + break; } write(errval); std::stringstream ss; - ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << etiss::fault::maskop_tostring(a.getMaskOp()) << " 0x" << mask_value << "->0x" << errval << std::dec; + ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << std::string(a.getMaskOp()) << " 0x" << mask_value << "->0x" << errval << std::dec; etiss::log(etiss::INFO, ss.str()); return true; } @@ -508,8 +511,8 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } return applyCustomAction(fault, action, errormsg); } - case etiss::fault::Action::MASK: [[fallthrough]]; - case etiss::fault::Action::BITFLIP: // handle bitflip + case etiss::fault::Action::Type::MASK: [[fallthrough]]; + case etiss::fault::Action::Type::BITFLIP: // handle bitflip { Field *f; auto find = fieldNames_.find(action.getTargetField()); diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index eba5fa17fc..8d98789641 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -42,6 +42,7 @@ #ifndef NO_ETISS #include "etiss/fault/Action.h" +#include "etiss/jit/ReturnCode.h" #else #include "fault/Action.h" #endif @@ -53,54 +54,6 @@ namespace etiss namespace fault { -bool maskop_fromstring(Action::MaskOp& out, const std::string& in) -{ - if (in == "AND" or in == "&") - { - out = Action::MaskOp::AND; - } - else if (in == "OR" or in == "|") - { - out = Action::MaskOp::OR; - } - else if (in == "XOR" or in == "^") - { - out = Action::MaskOp::XOR; - } - else if (in == "NAND" or in == "!&") - { - out = Action::MaskOp::NAND; - } - else if (in == "NOR" or in == "!|") - { - out = Action::MaskOp::NOR; - } - else - { - return false; - } - return true; -} - -std::string maskop_tostring(Action::MaskOp in) -{ - switch(in) - { - case Action::MaskOp::AND: - return "AND"; - case Action::MaskOp::OR: - return "OR"; - case Action::MaskOp::XOR: - return "XOR"; - case Action::MaskOp::NAND: - return "NAND"; - case Action::MaskOp::NOR: - return "NOR"; - default: - return "NOP"; - } -} - void Action::ensure(Type t) { if (type_ != t) @@ -109,17 +62,26 @@ void Action::ensure(Type t) bool Action::is_action_on_field(void) const { - return ((type_ == BITFLIP || type_ == MASK) ? true : false); + return ((type_ == Type::BITFLIP || type_ == Type::MASK) ? true : false); } Action::Action() - : type_(NOP) + : type_(Type::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action() called. ")); } +#ifndef NO_ETISS +Action::Action(etiss::int32 event) + : type_(Type::EVENT) + , event_(event) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(etiss::int32 exception) called. ")); +} +#endif + Action::Action(const InjectorAddress &inj, const std::string &command) - : type_(COMMAND) + : type_(Type::COMMAND) , inj_(inj) , command_(command) { @@ -128,7 +90,7 @@ Action::Action(const InjectorAddress &inj, const std::string &command) } Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bit) - : type_(BITFLIP) + : type_(Type::BITFLIP) , inj_(inj) , field_(field) , bit_(bit) @@ -139,83 +101,100 @@ Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bi } Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value) - : type_(MASK) + : type_(Type::MASK) , inj_(inj) , field_(field) , mask_op_(mask_op) , mask_value_(mask_value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + - std::string(", field=") + field + std::string(", mask_op=") + std::to_string(mask_op) + + std::string(", field=") + field + std::string(", mask_op=") + std::string(mask_op_) + std::string(", mask_value=") + std::to_string(mask_value) + std::string(") called. ")); } Action::Action(const Fault &fault) - : type_(INJECTION) + : type_(Type::INJECTION) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(Fault &=") + fault.toString() + std::string(") called. ")); fault_.push_back(fault); } -Action::Type Action::getType() const +const Action::type_t& Action::getType() const { return type_; } const InjectorAddress &Action::getInjectorAddress() const { - if(unlikely(!(type_ == BITFLIP || type_ == MASK || type_ == COMMAND))) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type has no Injector")); + if(unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND))) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type is not Injector")); return inj_; } /// COMMAND only const std::string &Action::getCommand() const { - if(unlikely(type_ != COMMAND)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getCommand(): Requested Action::Type has no Command")); + if(unlikely(type_ != Type::COMMAND)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getCommand(): Requested Action::Type is not Command")); return command_; } /// is_action_on_field only const std::string &Action::getTargetField() const { - if(unlikely(!(type_ == BITFLIP || type_ == MASK))) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getTargetField(): Requested Action::Type has no TargetField")); + if(unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK))) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetField(): Requested Action::Type is not TargetField")); return field_; } /// BITFLIP only unsigned Action::getTargetBit() const { - if(unlikely(type_ != BITFLIP)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getTargetBit(): Requested Action::Type has no TargetBit")); + if(unlikely(type_ != Type::BITFLIP)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetBit(): Requested Action::Type is not TargetBit")); return bit_; } const Fault &Action::getFault() const { - if(unlikely(type_ != INJECTION)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getFault(): Requested Action::Type has no injectable Fault")); + if(unlikely(type_ != Type::INJECTION)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); return fault_.front(); } -Action::MaskOp Action::getMaskOp() const +//Action::MaskOp Action::getMaskOp() const +const Action::mask_op_t& Action::getMaskOp() const { - if(unlikely(type_ != MASK)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getMaskOp(): Requested Action::Type has no Mask")); + if(unlikely(type_ != Type::MASK)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); return mask_op_; } +//std::string Action::getMaskOpString() const +//{ +// if(unlikely(type_ != Type::MASK)) +// etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); +// return mask_op_; +//} + uint64_t Action::getMaskValue() const { - if(unlikely(type_ != MASK)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getMaskValue(): Requested Action::Type has no Mask")); + if(unlikely(type_ != Type::MASK)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskValue(): Requested Action::Type is not Mask")); return mask_value_; } +#ifndef NO_ETISS +etiss::int32 Action::getEvent() const +{ + if(unlikely(type_ != Type::EVENT)) + etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getEvent(): Requested Action::Type is not Event")); + return event_; +} +#endif + std::string Action::toString() const { pugi::xml_document doc; @@ -329,7 +308,7 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D diag.unexpectedNode(node, "Failed to parse mask operation "); return false; } - if (! etiss::fault::maskop_fromstring(op, op_str)) + if (! etiss::fault::Action::mask_op_t::fromString(op_str, op)) { diag.unexpectedNode(node, "Failed to parse mask operation "); return false; @@ -343,6 +322,25 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D f = Action(inj, field, op, value); return true; } +#ifndef NO_ETISS + else if (type == "EVENT") + { + std::string event_str; + etiss::int32 event; + if (!parse(findSingleNode(node, "cause", diag), event_str, diag)) + { + diag.unexpectedNode(node, "Failed to parse node of event type: "); + return false; + } + if (! etiss::fault::returncode_fromstring(event, event_str)) + { + diag.unexpectedNode(node, "Failed to parse event . Supported values: {NOERROR, RELOADBLOCKS, RELOADCURRENTBLOCK, CPUFINISHED, EXCEPTION:[cause(hex)]}"); + return false; + } + f = Action(event); + return true; + } +#endif else { diag.unexpectedNode(node, std::string("Unknown type of action: ") + type); @@ -358,30 +356,36 @@ bool write(pugi::xml_node node, const etiss::fault::Action bool ok = true; switch (f.getType()) { - case etiss::fault::Action::NOP: + case etiss::fault::Action::Type::NOP: return write_attr(node, "type", "NOP", diag); break; - case etiss::fault::Action::BITFLIP: + case etiss::fault::Action::Type::BITFLIP: ok = ok & write_attr(node, "type", "BITFLIP", diag); ok = ok & write(node.append_child("injector"), f.getInjectorAddress(), diag); ok = ok & write(node.append_child("field"), f.getTargetField(), diag); ok = ok & write(node.append_child("bit"), f.getTargetBit(), diag); break; - case etiss::fault::Action::COMMAND: + case etiss::fault::Action::Type::COMMAND: ok = ok & write_attr(node, "type", "COMMAND", diag); ok = ok & write(node.append_child("command"), f.getCommand(), diag); break; - case etiss::fault::Action::INJECTION: + case etiss::fault::Action::Type::INJECTION: ok = ok & write_attr(node, "type", "INJECTION", diag); ok = ok & write(node.append_child("fault"), f.getFault(), diag); break; - case etiss::fault::Action::MASK: + case etiss::fault::Action::Type::MASK: ok = ok & write_attr(node, "type", "MASK", diag); ok = ok & write(node.append_child("injector"), f.getInjectorAddress(), diag); ok = ok & write(node.append_child("field"), f.getTargetField(), diag); - ok = ok & write(node.append_child("op"), etiss::fault::maskop_tostring(f.getMaskOp()), diag); + ok = ok & write(node.append_child("op"), SmartType::toString(f.getMaskOp()), diag); ok = ok & write(node.append_child("value"), f.getMaskValue(), diag); break; +#ifndef NO_ETISS + case etiss::fault::Action::Type::EVENT: + ok = ok & write_attr(node, "type", "EVENT", diag); + ok = ok & write(node.append_child("cause"), etiss::fault::returncode_tostring(f.getEvent()), diag); + break; +#endif } return false; } @@ -389,6 +393,153 @@ bool write(pugi::xml_node node, const etiss::fault::Action } // namespace xml #endif +#ifndef NO_ETISS +bool returncode_fromstring(etiss::int32& out, const std::string& in) +{ + if (in == "NOERROR" or in == "RETURNCODE::NOERROR") + { + out = etiss::RETURNCODE::NOERROR; + } + else if (in == "RELOADBLOCKS" or in == "RETURNCODE::RELOADBLOCKS") + { + out = etiss::RETURNCODE::RELOADBLOCKS; + } + else if (in == "RELOADCURRENTBLOCK" or in == "RETURNCODE::RELOADCURRENTBLOCK") + { + out = etiss::RETURNCODE::RELOADCURRENTBLOCK; + } + else if (in == "CPUFINISHED" or in == "RETURNCODE::CPUFINISHED") + { + out = etiss::RETURNCODE::CPUFINISHED; + } + else if (in == "DBUS_WRITE_ERROR" or in == "RETURNCODE::DBUS_WRITE_ERROR") + { + out = etiss::RETURNCODE::DBUS_WRITE_ERROR; + } + else if (in == "IBUS_READ_ERROR" or in == "RETURNCODE::IBUS_READ_ERROR") + { + out = etiss::RETURNCODE::IBUS_READ_ERROR; + } + else if (in == "IBUS_WRITE_ERROR" or in == "RETURNCODE::IBUS_WRITE_ERROR") + { + out = etiss::RETURNCODE::IBUS_WRITE_ERROR; + } + else if (in == "INTERRUPT" or in == "RETURNCODE::INTERRUPT") + { + out = etiss::RETURNCODE::INTERRUPT; + } + else if (in == "RESET" or in == "RETURNCODE::RESET") + { + out = etiss::RETURNCODE::RESET; + } + else if (in == "ILLEGALINSTRUCTION" or in == "RETURNCODE::ILLEGALINSTRUCTION") + { + out = etiss::RETURNCODE::ILLEGALINSTRUCTION; + } + else if (in == "ILLEGALJUMP" or in == "RETURNCODE::ILLEGALJUMP") + { + out = etiss::RETURNCODE::ILLEGALJUMP; + } + else if (in == "INSTR_PAGEFAULT" or in == "RETURNCODE::INSTR_PAGEFAULT") + { + out = etiss::RETURNCODE::INSTR_PAGEFAULT; + } + else if (in == "LOAD_PAGEFAULT" or in == "RETURNCODE::LOAD_PAGEFAULT") + { + out = etiss::RETURNCODE::LOAD_PAGEFAULT; + } + else if (in == "STORE_PAGEFAULT" or in == "RETURNCODE::STORE_PAGEFAULT") + { + out = etiss::RETURNCODE::STORE_PAGEFAULT; + } + else if (in == "SYSCALL" or in == "RETURNCODE::SYSCALL") + { + out = etiss::RETURNCODE::SYSCALL; + } + else if (in == "PAGEFAULT" or in == "RETURNCODE::PAGEFAULT") + { + out = etiss::RETURNCODE::PAGEFAULT; + } + else if (in == "BREAKPOINT" or in == "RETURNCODE::BREAKPOINT") + { + out = etiss::RETURNCODE::BREAKPOINT; + } + else + { + return false; + } + return true; +} + +std::string returncode_tostring(etiss::int32 in) +{ + switch(in) + { + case etiss::RETURNCODE::NOERROR: + return "NOERROR"; + case etiss::RETURNCODE::RELOADBLOCKS: + return "RELOADBLOCKS"; + case etiss::RETURNCODE::RELOADCURRENTBLOCK: + return "RELOADCURRENTBLOCK"; + case etiss::RETURNCODE::INTERRUPT: + return "INTERRUPT"; + case etiss::RETURNCODE::DBUS_WRITE_ERROR: + return "DBUS_WRITE_ERROR"; + case etiss::RETURNCODE::IBUS_READ_ERROR: + return "IBUS_READ_ERROR"; + case etiss::RETURNCODE::IBUS_WRITE_ERROR: + return "IBUS_WRITE_ERROR"; + case etiss::RETURNCODE::RESET: + return "RESET"; + case etiss::RETURNCODE::ILLEGALINSTRUCTION: + return "ILLEGALINSTRUCTION"; + case etiss::RETURNCODE::ILLEGALJUMP: + return "ILLEGALJUMP"; + case etiss::RETURNCODE::INSTR_PAGEFAULT: + return "INSTR_PAGEFAULT"; + case etiss::RETURNCODE::LOAD_PAGEFAULT: + return "LOAD_PAGEFAULT"; + case etiss::RETURNCODE::STORE_PAGEFAULT: + return "STORE_PAGEFAULT"; + case etiss::RETURNCODE::SYSCALL: + return "SYSCALL"; + case etiss::RETURNCODE::PAGEFAULT: + return "PAGEFAULT"; + case etiss::RETURNCODE::BREAKPOINT: + return "BREAKPOINT"; + case etiss::RETURNCODE::CPUFINISHED: + return "CPUFINISHED"; + default: + std::stringstream ss; + ss << "EXCEPTION:" << std::hex << in; + return ss.str(); + } +} + +#endif + +template<> +Action::type_t::map_t Action::type_t::TABLE = { + {Action::Type::NOP, "NOP"} + , {Action::Type::BITFLIP, "BITFLIP"} + , {Action::Type::MASK, "MASK"} + , {Action::Type::COMMAND, "COMMAND"} + , {Action::Type::INJECTION, "INJECTION"} +#ifndef NO_ETISS + , {Action::Type::EVENT, "EVENT"} +#endif +}; + +template<> +Action::mask_op_t::map_t Action::mask_op_t::TABLE = { + {Action::MaskOp::AND, "AND"} + , {Action::MaskOp::OR, "OR"} + , {Action::MaskOp::XOR, "XOR"} + , {Action::MaskOp::NAND, "NAND"} + , {Action::MaskOp::NOR, "NOR"} + , {Action::MaskOp::NOP, "NOP"} +}; + } // namespace fault } // namespace etiss diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 1d75d04937..2991ccc6de 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -77,6 +77,12 @@ static std::map &faults() return map; } +#ifndef NO_ETISS +etiss::int32 Stressor::event_code_; +#else +int Stressor::event_code_; +#endif + bool Stressor::loadXML(const std::string &file, const int coreID) { @@ -169,7 +175,7 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) // Iterate through triggers of the fault for (std::vector::const_iterator iter = f.triggers.begin(); iter != f.triggers.end(); ++iter) { - if(iter->getType() != etiss::fault::Trigger::NOP) // only add Trigger, if it is not a NOP + if(iter->getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP { iptr = iter->getInjector(); @@ -239,16 +245,24 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector for (std::vector::iterator iter = find->second.actions.begin(); iter != find->second.actions.end(); ++iter) { - if (iter->getType() == etiss::fault::Action::INJECTION) + if (iter->getType() == etiss::fault::Action::Type::INJECTION) { /// TODO for time relative triggers resolve time must be called! addFault(iter->getFault(), true); } - else if(iter->getType() == etiss::fault::Action::NOP) + else if(iter->getType() == etiss::fault::Action::Type::NOP) { etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); return true; } +#ifndef NO_ETISS + else if(iter->getType() == etiss::fault::Action::Type::EVENT) + { + etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EVENT")); + set_event(iter->getEvent()); + return true; + } +#endif else { if (iter->getInjectorAddress().getInjector()) @@ -260,7 +274,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #endif { #ifndef NO_ETISS - etiss::log(etiss::WARNING, + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Stressor::firedTrigger: Action") + std::string(" injector is not the injector that triggered this event.") + std::string(" threadsafety must be ensured by user."), From e75be13d0812a6a0dad733d8de9d114235b71db9 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 19 Nov 2021 20:12:41 +0100 Subject: [PATCH 11/51] Add as soon as possbile (ASAP) type which forces Injector::instructionAccurateCallback() to trigger on next trigger check. Update Trigger typing code. --- include/etiss/fault/Trigger.h | 26 +- src/fault/Trigger.cpp | 521 +++++++++++++++++++--------------- 2 files changed, 304 insertions(+), 243 deletions(-) diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index ed72b5562a..312534c50f 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -63,9 +63,11 @@ #include "etiss/Misc.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/XML.h" +#include "etiss/fault/Misc.h" #else #include "fault/InjectorAddress.h" #include "fault/XML.h" +#include "fault/Misc.h" #endif namespace etiss @@ -84,22 +86,28 @@ typedef Injector *Injector_ptr; class Trigger : public etiss::ToString { public: - enum Type + enum class Type { - META_COUNTER, - VARIABLEVALUE, - TIME, + META_COUNTER + , VARIABLEVALUE + , TIME /// needs to be resolved. this can only be used in connection with an /// injection action - TIMERELATIVE, - NOP + , TIMERELATIVE + , ASAP + , NOP }; - + typedef SmartType type_t; // constructors /** Type: NOP (no operation) */ Trigger(); + // constructors + /** + Type: typed but empty constructor (used for ASAP) + */ + Trigger(const InjectorAddress &target_injector); /** * @note Type: META_COUNTER * @@ -141,7 +149,7 @@ class Trigger : public etiss::ToString bool isNOP() const; const std::string &getTriggerField() const; const uint64_t &getTriggerFieldValue() const; - Type getType() const; + const type_t& getType() const; // Members /** @brief this function checks if the Trigger has just fired. @@ -160,7 +168,7 @@ class Trigger : public etiss::ToString std::string toString() const; ///< operator<< can be used. private: // Attributes - Type type_; + type_t type_; std::string field_; Trigger *sub_; InjectorAddress inj_; diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index c7fd567f13..f701a38ed1 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -62,41 +62,61 @@ void Trigger::ensure(Type type) const if (type_ != type) { etiss::log(etiss::FATALERROR, std::string("etiss::fault::Trigger::ensure: Type mismatch type=") + - std::to_string(type) + " type_=" + std::to_string(type_)); + type_t::toString(type) + " type_=" + std::string(type_)); throw "called function of different trigger type"; } } -Trigger::Trigger() : type_(NOP) +Trigger::Trigger() + : type_(Type::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (NOP)")); } + +Trigger::Trigger(const InjectorAddress &target_injector) + : type_(Type::ASAP) + , inj_(target_injector) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger(const InjectorAddress &target_injector) : type_ (ASAP)")); +} + Trigger::Trigger(const Trigger &sub, uint64_t count) - : type_(META_COUNTER), sub_(new Trigger(sub)), param1_(count), param2_(0) + : type_(Type::META_COUNTER) + , sub_(new Trigger(sub)) + , param1_(count) + , param2_(0) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (META_COUNTER)")); } Trigger::Trigger(const InjectorAddress &target_injector, const std::string &field, uint64_t value) - : type_(VARIABLEVALUE), field_(field), inj_(target_injector), fieldptr_(0), param1_(value) + : type_(Type::VARIABLEVALUE) + , field_(field) + , inj_(target_injector) + , fieldptr_(0) + , param1_(value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (VARIABLEVALUE)")); } Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool relative) - : type_(relative ? TIMERELATIVE : TIME), inj_(target_injector), param1_(time_ps), param2_(0) + : type_(relative ? Type::TIMERELATIVE : Type::TIME) + , inj_(target_injector) + , param1_(time_ps) + , param2_(0) { relative ? etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIMERELATIVE)")) : etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIME)")); } -Trigger::Trigger(const Trigger &cpy) : type_(NOP) +Trigger::Trigger(const Trigger &cpy) + : type_(Type::NOP) { *this = cpy; } Trigger &Trigger::operator=(const Trigger &cpy) { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) delete sub_; - if (type_ == VARIABLEVALUE) + if (type_ == Type::VARIABLEVALUE) { if (inj_.isResolved() && fieldptr_) { @@ -106,31 +126,35 @@ Trigger &Trigger::operator=(const Trigger &cpy) type_ = cpy.type_; switch (type_) { - case META_COUNTER: + case Type::META_COUNTER: sub_ = new Trigger(*cpy.sub_); param1_ = cpy.param1_; param2_ = cpy.param2_; break; - case VARIABLEVALUE: + case Type::VARIABLEVALUE: field_ = cpy.field_; inj_ = cpy.inj_; fieldptr_ = 0; param1_ = cpy.param1_; break; - case TIMERELATIVE: [[fallthrough]]; - case TIME: + case Type::TIMERELATIVE: [[fallthrough]]; + case Type::TIME: inj_ = cpy.inj_; param1_ = cpy.param1_; param2_ = cpy.param2_; break; - case NOP: + case Type::ASAP: + inj_ = cpy.inj_; + break; + case Type::NOP: break; } return *this; } #if CXX0X_UP_SUPPORTED -Trigger::Trigger(Trigger &&cpy) : type_(NOP) +Trigger::Trigger(Trigger &&cpy) + : type_(Type::NOP) { operator=(cpy); } @@ -143,9 +167,9 @@ Trigger &Trigger::operator=(Trigger &&cpy) Trigger::~Trigger() { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) delete sub_; - if (type_ == VARIABLEVALUE) + if (type_ == Type::VARIABLEVALUE) { if (inj_.isResolved() && fieldptr_) { @@ -160,85 +184,94 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) std::string(", Injector*)")); switch (type_) { - case META_COUNTER: - if (sub_->fired(time_ps, target_injector)) - { - ++param2_; // increase count - if (param1_ == param2_) - { - return true; // count reached -> fire event - } - } - break; - case VARIABLEVALUE: - { - if (fieldptr_ == 0) - { - std::string errmsg; - fieldptr_ = target_injector->fastFieldAccessPtr(field_, errmsg); - if (fieldptr_ == 0) - { -#ifdef NO_ETISS - std::cout << "Trigger::fired: Failed to get field" << std::endl; -#else - etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this); -#endif - return false; - } - } - uint64_t val = 0; - std::string errmsg; - if (inj_.getInjector()) - { - if (!inj_.getInjector()->readField(fieldptr_, val, errmsg)) - { -#ifdef NO_ETISS - std::cout << "Trigger::fired: Failed to read field: " << errmsg << std::endl; -#else - etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this, errmsg); -#endif - return false; - } - } - else - { -#ifdef NO_ETISS - std::cout << "Trigger::fired: Failed get injector: " << inj_.getInjectorPath() << std::endl; -#else - etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, inj_); -#endif - } - return val == param1_; - } - break; - case TIMERELATIVE: - etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, inj_); - resolveTime(time_ps); - [[fallthrough]]; - case TIME: - /* TODO: Why doing it like this ? this would always fire after time_ps has reached */ - // Possibly because might be called not exaclty but later than excact trigger time, then late triggering should - // be done - - // if (time_ps >= param1_ && param2_ <= time_ps) - if (param1_ <= time_ps && param2_ == 0) // testing alternative param2_ says if trigger has already fired - { - /// TODO decide how to handle the case of multiple trigger checks at the same simulation time - - // param2_ = time_ps+1; - param2_ = 1; // testing alternative: param2_ says if trigger has already fired - return true; - } - // param2_ = time_ps; - // testing alternative param2_ says if trigger has already fired - - // DO NOT DO IT LIKE THIS, MIGHT NOT BE CALLED EXACTLY WITH TIME OF TRIGGER - // if (time_ps == param1_) - // return true; - break; - case NOP: - return true; - break; + case Type::META_COUNTER: + { + if (sub_->fired(time_ps, target_injector)) + { + ++param2_; // increase count + if (param1_ == param2_) + { + return true; // count reached -> fire event + } + } + break; + } + case Type::VARIABLEVALUE: + { + if (fieldptr_ == 0) + { + std::string errmsg; + fieldptr_ = target_injector->fastFieldAccessPtr(field_, errmsg); + if (fieldptr_ == 0) + { + #ifdef NO_ETISS + std::cout << "Trigger::fired: Failed to get field" << std::endl; + #else + etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this); + #endif + return false; + } + } + uint64_t val = 0; + std::string errmsg; + if (inj_.getInjector()) + { + if (!inj_.getInjector()->readField(fieldptr_, val, errmsg)) + { + #ifdef NO_ETISS + std::cout << "Trigger::fired: Failed to read field: " << errmsg << std::endl; + #else + etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this, errmsg); + #endif + return false; + } + } + else + { + #ifdef NO_ETISS + std::cout << "Trigger::fired: Failed get injector: " << inj_.getInjectorPath() << std::endl; + #else + etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, inj_); + #endif + } + return val == param1_; + } + case Type::TIMERELATIVE: + { + etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, inj_); + resolveTime(time_ps); + [[fallthrough]]; + } + case Type::TIME: + { + /* TODO: Why doing it like this ? this would always fire after time_ps has reached */ + // Possibly because might be called not exaclty but later than excact trigger time, then late triggering should + // be done + + // if (time_ps >= param1_ && param2_ <= time_ps) + if (param1_ <= time_ps && param2_ == 0) // testing alternative param2_ says if trigger has already fired + { + /// TODO decide how to handle the case of multiple trigger checks at the same simulation time + // param2_ = time_ps+1; + param2_ = 1; // testing alternative: param2_ says if trigger has already fired + return true; + } + // param2_ = time_ps; + // testing alternative param2_ says if trigger has already fired + + // DO NOT DO IT LIKE THIS, MIGHT NOT BE CALLED EXACTLY WITH TIME OF TRIGGER + // if (time_ps == param1_) + // return true; + break; + } + case Type::ASAP: + { + return true; // as soon as possible means on next check + } + case Type::NOP: + { + return true; + } } return false; @@ -248,53 +281,53 @@ void Trigger::resolveTime(uint64_t time) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::resolveTime(time=") + std::to_string(time) + std::string(")")); - if (type_ == TIMERELATIVE) + if (type_ == Type::TIMERELATIVE) { - type_ = TIME; + type_ = Type::TIME; param1_ = param1_ + time; } - else if (type_ == META_COUNTER) + else if (type_ == Type::META_COUNTER) { return getSubTrigger().resolveTime(time); } } bool Trigger::isResolved() const { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return getSubTrigger().isResolved(); } - return type_ != TIMERELATIVE; + return type_ != Type::TIMERELATIVE; } uint64_t Trigger::getTriggerCount() const { - ensure(META_COUNTER); + ensure(Type::META_COUNTER); return param1_; } Trigger &Trigger::getSubTrigger() { - ensure(META_COUNTER); + ensure(Type::META_COUNTER); return *sub_; } const Trigger &Trigger::getSubTrigger() const { - ensure(META_COUNTER); + ensure(Type::META_COUNTER); return *sub_; } uint64_t Trigger::getTriggerTime() const { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return getSubTrigger().getTriggerTime(); } try { - ensure(TIME); + ensure(Type::TIME); } catch (char const *) { - ensure(TIMERELATIVE); + ensure(Type::TIMERELATIVE); } return param1_; } @@ -304,7 +337,7 @@ const InjectorAddress &Trigger::getInjectorAddress() const } const Injector_ptr &Trigger::getInjector() const { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return sub_->getInjector(); } @@ -315,36 +348,36 @@ const Injector_ptr &Trigger::getInjector() const } bool Trigger::isNOP() const { - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return sub_->isNOP(); } else { - return type_ == NOP; + return type_ == Type::NOP; } } const std::string &Trigger::getTriggerField() const { // std::cout << "Trigger::getTriggerField() called" << std::endl; - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return getSubTrigger().getTriggerField(); } - ensure(VARIABLEVALUE); + ensure(Type::VARIABLEVALUE); return field_; } const uint64_t &Trigger::getTriggerFieldValue() const { // std::cout << "Trigger::getTriggerFieldValue() called" << std::endl; - if (type_ == META_COUNTER) + if (type_ == Type::META_COUNTER) { return getSubTrigger().getTriggerFieldValue(); } - ensure(VARIABLEVALUE); + ensure(Type::VARIABLEVALUE); return param1_; } -Trigger::Type Trigger::getType() const +const Trigger::type_t& Trigger::getType() const { return type_; } @@ -352,24 +385,20 @@ Trigger::Type Trigger::getType() const std::string Trigger::toString() const { std::stringstream ss; - ss << "Trigger {"; + ss << "Trigger { type=" << std::string(type_); switch (type_) { - case META_COUNTER: - ss << " type=META_COUNTER triggerCount=" << +param1_ << " currentCount=" << +param2_; + case Type::META_COUNTER: + ss << " triggerCount=" << +param1_ << " currentCount=" << +param2_; break; - case VARIABLEVALUE: - ss << " type=VARIABLEVALUE field=" << field_ << " triggerValue=" << +param1_; + case Type::VARIABLEVALUE: + ss << " field=" << field_ << " triggerValue=" << +param1_; break; - case TIME: - ss << " type=TIME triggerTime=" << +param1_; - break; - case TIMERELATIVE: - ss << " type=TIMERELATIVE triggerTime=" << +param1_; - break; - case NOP: - ss << " type=NOP"; + case Type::TIMERELATIVE: [[fallthrough]]; + case Type::TIME: + ss << " triggerTime=" << +param1_; break; + default: break; } ss << "}"; return ss.str(); @@ -384,93 +413,106 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::xml::parse") + std::string("(node, Trigger*&, Diagnostics)")); f = 0; - std::string type; - if (!parse_attr(node, "type", type, diag)) + std::string type_str; + if (!parse_attr(node, "type", type_str, diag)) { return false; } - - if (type == "META_COUNTER") + etiss::fault::Trigger::type_t type(type_str); + + switch(type) { - uint64_t count; - if (!parse(findSingleNode(node, "count", diag), count, diag)) + case etiss::fault::Trigger::Type::META_COUNTER: { - return false; - } - etiss::fault::Trigger *sub = 0; - if ((!parse(findSingleNode(node, "trigger", diag), sub, diag)) || (sub == 0)) - { - diag.unexpectedNode(node, "Failed to parse sub trigger"); - return false; + uint64_t count; + if (!parse(findSingleNode(node, "count", diag), count, diag)) + { + return false; + } + etiss::fault::Trigger *sub = 0; + if ((!parse(findSingleNode(node, "trigger", diag), sub, diag)) || (sub == 0)) + { + diag.unexpectedNode(node, "Failed to parse sub trigger"); + return false; + } + f = new etiss::fault::Trigger(*sub, count); + delete sub; + return true; } - f = new etiss::fault::Trigger(*sub, count); - delete sub; - return true; - } - if (type == "VARIABLEVALUE") - { - uint64_t value; - if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) + case etiss::fault::Trigger::Type::VARIABLEVALUE: { - return false; - } - std::string field; - if (!parse(findSingleNode(node, "field", diag), field, diag)) - { - return false; - } - setCoreName(field); - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) - { - return false; + uint64_t value; + if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) + { + return false; + } + std::string field; + if (!parse(findSingleNode(node, "field", diag), field, diag)) + { + return false; + } + setCoreName(field); + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; + } + f = new Trigger(injector, field, value); + return true; } - f = new Trigger(injector, field, value); - return true; - } - if (type == "TIME") - { - uint64_t count; - if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) + case etiss::fault::Trigger::Type::TIME: { - return false; /// TODO support other time formats/better nodes e.g. + uint64_t count; + if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) + { + return false; /// TODO support other time formats/better nodes e.g. + } + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; + } + f = new Trigger(injector, count); + return true; } - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + + case etiss::fault::Trigger::Type::TIMERELATIVE: //if (type == "TIMERELATIVE") { - return false; + uint64_t count; + if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) + { + return false; /// TODO support other time formats/better nodes e.g. + } + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; + } + f = new Trigger(injector, count, true); + etiss::log(etiss::VERBOSE, std::string("Injector2: ") + f->getInjectorAddress().getInjectorPath()); + return true; } - f = new Trigger(injector, count); - return true; - } - if (type == "TIMERELATIVE") - { - uint64_t count; - if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) + case etiss::fault::Trigger::Type::ASAP: { - return false; /// TODO support other time formats/better nodes e.g. + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; + } + f = new Trigger(injector); + return true; } - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + default: // NOP { - return false; + f = new Trigger(); + return true; } - f = new Trigger(injector, count, true); - etiss::log(etiss::VERBOSE, std::string("Injector2: ") + f->getInjectorAddress().getInjectorPath()); - return true; } - - if (type == "NOP") - { - f = new Trigger(); - return true; - } - return false; } template <> @@ -500,52 +542,52 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg { etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::xml::write") + std::string("(node, Trigger&, Diagnostics)")); - // std::cout << "write called " << std::endl; + switch (f.getType()) { - case etiss::fault::Trigger::META_COUNTER: - { - write_attr(node, "type", "META_COUNTER", diag); - write(node.append_child("count"), f.getTriggerCount(), diag); - write(node.append_child("trigger"), f.getSubTrigger(), diag); - } - return true; - case etiss::fault::Trigger::VARIABLEVALUE: - { - write_attr(node, "type", "VARIABLEVALUE", diag); - write(node.append_child("field"), f.getTriggerField(), diag); - write(node.append_child("count"), f.getTriggerFieldValue(), diag); - Injector_ptr ptr = f.getInjector(); - if (!ptr) + case etiss::fault::Trigger::Type::META_COUNTER: { - diag.errors.push_back("A fault trigger has no target_injector. failed to " - "get path of trigger"); - return false; + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("count"), f.getTriggerCount(), diag); + write(node.append_child("trigger"), f.getSubTrigger(), diag); } - write(node.append_child("injector"), ptr->getInjectorPath(), diag); - } - return true; - case etiss::fault::Trigger::TIME: - case etiss::fault::Trigger::TIMERELATIVE: - { - write_attr(node, "type", - (f.getType() == etiss::fault::Trigger::TIMERELATIVE) ? "TIMERELATIVE" : "TIME", diag); - write(node.append_child("time_ps"), f.getTriggerTime(), diag); - Injector_ptr ptr = f.getInjector(); - if (!ptr) + return true; + case etiss::fault::Trigger::Type::VARIABLEVALUE: { - diag.errors.push_back("A fault trigger has no target_injector. failed to " - "get path of trigger"); - return false; + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("field"), f.getTriggerField(), diag); + write(node.append_child("count"), f.getTriggerFieldValue(), diag); + Injector_ptr ptr = f.getInjector(); + if (!ptr) + { + diag.errors.push_back("A fault trigger has no target_injector. failed to " + "get path of trigger"); + return false; + } + write(node.append_child("injector"), ptr->getInjectorPath(), diag); + } + return true; + case etiss::fault::Trigger::Type::TIME: [[fallthrough]]; + case etiss::fault::Trigger::Type::TIMERELATIVE: + { + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("time_ps"), f.getTriggerTime(), diag); + Injector_ptr ptr = f.getInjector(); + if (!ptr) + { + diag.errors.push_back("A fault trigger has no target_injector. failed to " + "get path of trigger"); + return false; + } + write(node.append_child("injector"), ptr->getInjectorPath(), diag); + return true; + } + case etiss::fault::Trigger::Type::ASAP: [[fallthrough]]; + case etiss::fault::Trigger::Type::NOP: + { + write_attr(node, "type", std::string(f.getType()), diag); + return true; } - write(node.append_child("injector"), ptr->getInjectorPath(), diag); - } - return true; - case etiss::fault::Trigger::NOP: - { - write_attr(node, "type", "NOP", diag); - } - return true; } diag.errors.push_back("etiss::fault::xml::write " @@ -557,6 +599,17 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg } // namespace xml #endif + +template<> +Trigger::type_t::map_t Trigger::type_t::TABLE = { + {Trigger::Type::META_COUNTER, "META_COUNTER"} + , {Trigger::Type::VARIABLEVALUE, "VARIABLEVALUE"} + , {Trigger::Type::TIME, "TIME"} + , {Trigger::Type::TIMERELATIVE, "TIMERELATIVE"} + , {Trigger::Type::ASAP, "ASAP"} + , {Trigger::Type::NOP, "NOP"} +}; + } // namespace fault } // namespace etiss From 724c3967dbd1bcbef65fce2f06f19322240c33df Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 19 Nov 2021 20:14:24 +0100 Subject: [PATCH 12/51] Add getName() pure abstract to VirtualStructSupport for easier identfication of mounted structs. Minor typo fixes. --- include/etiss/VirtualStruct.h | 3 ++- src/VirtualStruct.cpp | 2 +- src/fault/Injector.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index b05dc21714..3aca130609 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -66,7 +66,7 @@ class VSSync; /** abstract representation of an module of a simulation which could be a embedded device of the cpu of an embedded -device. it is recommended to model and address the full hardware hirachy. e.g.
 device1
+device. it is recommended to model and address the full hardware hierachy. e.g. 
 device1
             -> cpu
                 -> cache
             -> bus
@@ -450,6 +450,7 @@ class VirtualStructSupport
         @return may never be NULL
     */
     virtual std::shared_ptr getStruct() = 0;
+    virtual const std::string &getName() = 0;
 };
 
 #define ETISS_VIRTUALSTRUCT_ADDFIELD(VIRTUALSTRUCT, STRUCT, FIELD, NAME, PRETTYNAME) \
diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp
index 5a7163c667..067ee92649 100644
--- a/src/VirtualStruct.cpp
+++ b/src/VirtualStruct.cpp
@@ -501,7 +501,7 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f
 {
     switch (action.getType())
     {
-    case etiss::fault::Action::COMMAND: // handle command
+    case etiss::fault::Action::Type::COMMAND: // handle command
     {
         if (!applyCustomAction)
         {
diff --git a/src/fault/Injector.cpp b/src/fault/Injector.cpp
index ec235b257e..fce50976fd 100644
--- a/src/fault/Injector.cpp
+++ b/src/fault/Injector.cpp
@@ -67,7 +67,7 @@ Injector::Injector()
 void Injector::freeFastFieldAccessPtr(void *)
 {
     etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::Injector::freeFastFieldAccessPtr(void*)"));
-    etiss::log(etiss::INFO, std::string("etiss::fault::Injector::freeFastFieldAccessPtr(void*) not implemented"));
+    etiss::log(etiss::VERBOSE, std::string("etiss::fault::Injector::freeFastFieldAccessPtr(void*) not implemented"));
 }
 
 bool Injector::needsCallbacks()

From edba0743a4f508d030aa6c906e6bacc5f67bfa7f Mon Sep 17 00:00:00 2001
From: JoGei <62236820+JoGei@users.noreply.github.com>
Date: Fri, 19 Nov 2021 20:16:15 +0100
Subject: [PATCH 13/51] Add MemoryFaultSystem that allows memory manipulation
 through custom actions from fault injection. Extends SimpleMemSystem.

---
 .../fault/MemoryManipulationSystem.h          | 266 ++++++++++++++++++
 include/etiss/SimpleMemSystem.h               |   7 +-
 .../fault/MemoryManipulationSystem.cpp        | 246 ++++++++++++++++
 3 files changed, 517 insertions(+), 2 deletions(-)
 create mode 100644 include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h
 create mode 100644 src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp

diff --git a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h
new file mode 100644
index 0000000000..c15d9f204b
--- /dev/null
+++ b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h
@@ -0,0 +1,266 @@
+/**
+
+        @copyright
+
+        
+
+        Copyright 2018 Infineon Technologies AG
+
+        This file is part of ETISS tool, see .
+
+        The initial version of this software has been created with the funding support by the German Federal
+        Ministry of Education and Research (BMBF) in the project EffektiV under grant 01IS13022.
+
+        Redistribution and use in source and binary forms, with or without modification, are permitted
+        provided that the following conditions are met:
+
+        1. Redistributions of source code must retain the above copyright notice, this list of conditions and
+        the following disclaimer.
+
+        2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+        3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
+        or promote products derived from this software without specific prior written permission.
+
+        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+        DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+        PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+        HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+        POSSIBILITY OF SUCH DAMAGE.
+
+        
+ + @author Chair of Electronic Design Automation, TUM + + @date November 16, 2021 + + @version 0.1 + +*/ +/** + @file + + @brief simple test system implementation with fault functionality + +*/ + +#ifndef ETISS_INCLUDE_INTEGRATEDLIBRARY_FAULT_MEMORYMANIPULATIONSYSTEM_H_ +#define ETISS_INCLUDE_INTEGRATEDLIBRARY_FAULT_MEMORYMANIPULATIONSYSTEM_H_ +#include "etiss/System.h" +#include "etiss/make_unique.h" +#include + +#include "etiss/SimpleMemSystem.h" + +#include "etiss/VirtualStruct.h" + +#include +#include +#include + +#include + +#include "etiss/fault/Misc.h" + +namespace etiss +{ +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Memory word faulter base class +class MemoryWordManipulatorBase +{ + public: + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Memory operation type code + enum class MemOpType { + COPY + , AND + , OR + , XOR + , NAND + , NOR + , UNDEF + }; + typedef etiss::fault::SmartType mem_op_t; + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Memory operation class + class MemOp : mem_op_t + { + public: + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief executes memory operation with operands \p src1 and \p src2 and returns result + template + word_t operator()(word_t src1, word_t src2) const; + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor takes string encoded memory operation \ref MemOpType + MemOp(const std::string& memop_str); + } /* class MemOp */; + + //////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Memory operation type code + enum class MemManipCmd { + PUSH + , POP + , RMW + , RRMW + , UNDEF + }; + typedef etiss::fault::SmartType mem_manip_cmd_t; + + virtual etiss::int32 push(size_t address) = 0; + virtual etiss::int32 pop(size_t address) = 0; + virtual etiss::int32 rmw(size_t address, MemOp op, etiss::uint64 mod_val) = 0; + virtual etiss::int32 rrmw(size_t dstsrc1_address, MemOp op, size_t src2_address) = 0; +} /* class MemoryWordManipulatorBase */; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Memory word faulter class template \p word_t +template +class MemoryWordManipulator : public MemoryWordManipulatorBase +{ + private: + std::function mem_read_word_; + ///< function to read a single word from memory + std::function mem_write_word_; + ///< function to write a single word to memory + std::stack memstack_{}; + ///< memory stack to allow read modify write manipulations to memory + + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief pop the last added element from the memory stack and write it to memory \p address + /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access + virtual etiss::int32 pop(size_t address); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief read memory from \p address and push it to memory stack + /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access + virtual etiss::int32 push(size_t address); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief read-modify-write memory word at \p address with \p mod_val by bit-wise operation \p op + /// \p mod_val is type casted to \p word_t + /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access + virtual etiss::int32 rmw(size_t address, MemOp op, etiss::uint64 mod_val); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief read-readmodify-write memory word at \p dstsrc1_address with memory word at + /// \p src2_address by bit-wise operation \p op + /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access + virtual etiss::int32 rrmw(size_t dstsrc1_address, MemOp op, size_t src2_address); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor taking \p mem_read_word and \p mem_write_word functions for memory + /// fault action + MemoryWordManipulator( + std::function const & mem_read_word, + std::function const & mem_write_word); +} /* class MemoryWordManipulator */; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Simple etiss:System implementation for testing +class MemoryManipulationSystem : public VirtualStructSupport, public SimpleMemSystem +{ + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Constructor takes \p name for reference of this class as a name \ref Injector + MemoryManipulationSystem(const std::string& name = "system"); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief get virtual struct associated name + inline const std::string& getName() { return name_; } + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief get virtual struct + std::shared_ptr getStruct(void); + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief initialize this virtual struct and mount it to \p cpu_core + void init_manipulation(std::shared_ptr vs_parent); + + private: + std::string name_; + etiss::uint8 arch_width_; + std::shared_ptr vsystem_; + std::unique_ptr mem_manipulator_; + +}; + + +/* template "implemenations" */ +template +word_t MemoryWordManipulatorBase::MemOp::operator()(word_t src1, word_t src2) const +{ + switch(*this) + { + case MemOpType::COPY: + return src2; + case MemOpType::AND: + return (src1 & src2); + case MemOpType::OR: + return (src1 | src2); + case MemOpType::XOR: + return(src1 ^ src2); + case MemOpType::NAND: + return ~(src1 & src2); + case MemOpType::NOR: + return ~(src1 | src2); + default: break; + } + return src1; +} + +template +etiss::int32 MemoryWordManipulator::pop(size_t address) +{ + etiss::int32 return_code; + mem_write_word_(address, memstack_.top(), return_code); + memstack_.pop(); + return return_code; +} + +template +etiss::int32 MemoryWordManipulator::push(size_t address) +{ + etiss::int32 return_code; + memstack_.push(mem_read_word_(address, return_code)); + return return_code; +} + +template +etiss::int32 MemoryWordManipulator::rmw(size_t address, MemOp op, etiss::uint64 mod_val) +{ + etiss::int32 return_code; + word_t mem_val = mem_read_word_(address, return_code); + if(return_code == RETURNCODE::NOERROR) + { + mem_val = op(mem_val, static_cast(mod_val)); + mem_write_word_(address, mem_val, return_code); + } + return return_code; +} +template +etiss::int32 MemoryWordManipulator::rrmw(size_t dstsrc1_address, MemOp op, size_t src2_address) +{ + etiss::int32 return_code; + word_t src1mem_val = mem_read_word_(dstsrc1_address, return_code); + if(return_code == RETURNCODE::NOERROR) + { + word_t src2mem_val = mem_read_word_(src2_address, return_code); + if(return_code == RETURNCODE::NOERROR) + { + src1mem_val = op(src1mem_val, src2mem_val); + mem_write_word_(dstsrc1_address, src1mem_val, return_code); + } + } + return return_code; +} +template +MemoryWordManipulator::MemoryWordManipulator( + std::function const & mem_read_word, + std::function const & mem_write_word) + : mem_read_word_(mem_read_word) + , mem_write_word_(mem_write_word) +{ +} + +} // namespace etiss + + +#endif // ETISS_INCLUDE_INTEGRATEDLIBRARY_FAULT_MEMORYMANIPULATIONSYSTEM_H_ diff --git a/include/etiss/SimpleMemSystem.h b/include/etiss/SimpleMemSystem.h index 099b4061aa..6f92fafb59 100644 --- a/include/etiss/SimpleMemSystem.h +++ b/include/etiss/SimpleMemSystem.h @@ -162,11 +162,13 @@ class SimpleMemSystem : public System etiss::uint64 get_startaddr(void) { return (start_addr_); } void add_memsegment(std::unique_ptr& mseg, const void *raw_data, size_t file_size_bytes); + protected: + template + etiss::int32 dbus_access(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len); + private: std::vector> msegs_{}; - template - etiss::int32 dbus_access(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint8 *buf, etiss::uint32 len); etiss::uint64 start_addr_{ 0 }; @@ -193,4 +195,5 @@ class SimpleMemSystem : public System } // namespace etiss +void access_error(ETISS_CPU *cpu, etiss::uint64 addr, etiss::uint32 len, std::string error, etiss::Verbosity verbosity); #endif diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp new file mode 100644 index 0000000000..ca879e631d --- /dev/null +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -0,0 +1,246 @@ +/** + + @copyright + +
+
+        Copyright 2018 Infineon Technologies AG
+
+        This file is part of ETISS tool, see .
+
+        The initial version of this software has been created with the funding support by the German Federal
+        Ministry of Education and Research (BMBF) in the project EffektiV under grant 01IS13022.
+
+        Redistribution and use in source and binary forms, with or without modification, are permitted
+        provided that the following conditions are met:
+
+        1. Redistributions of source code must retain the above copyright notice, this list of conditions and
+        the following disclaimer.
+
+        2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
+        and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+        3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
+        or promote products derived from this software without specific prior written permission.
+
+        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+        DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+        PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+        HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+        POSSIBILITY OF SUCH DAMAGE.
+
+        
+ + @author Chair of Electronic Design Automation, TUM + + @date November 16, 2021 + + @version 0.1 + +*/ +/** + @file + + @brief implementation of etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h + + @detail + +*/ + +#include "etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h" +#include "etiss/Misc.h" +#include "elfio/elfio.hpp" +#include + +using namespace etiss; + +std::shared_ptr MemoryManipulationSystem::getStruct(void) +{ + if(!vsystem_) + { + vsystem_ = etiss::VirtualStruct::allocate(this, [](etiss::VirtualStruct::Field *f) { delete f; }); + + } + return vsystem_; +} + +void MemoryManipulationSystem::init_manipulation(std::shared_ptr vs_parent) +{ + auto mount_successful = vs_parent->getStruct()->mountStruct(this->getName(), this->getStruct()); + + if(!mount_successful) + { + etiss::log(etiss::FATALERROR, std::string("Failed to mount ") + this->getName() + std::string("'s VirtualStruct to ") + vs_parent->getName()); + } + else + { + uint8_t arch_width = 0; + + if (etiss::cfg().isSet("arch.cpu")) { + std::stringstream ss; + auto cpu_arch_str = etiss::cfg().get("arch.cpu", ""); + arch_width = cpu_arch_str == "RISCV64" ? 64 : 32; + } else { + // get architecture automatically + std::string elf_file = etiss::cfg().get("vp.elf_file", ""); + ELFIO::elfio reader; + if (!reader.load(elf_file)) + { + etiss::log(etiss::FATALERROR, "ELF reader could not process file"); + } + arch_width = reader.get_class() == ELFCLASS64 ? 64 : 32; + } + + etiss::log(etiss::VERBOSE, std::string("Mounted ") + this->getName() + std::string("'s VirtualStruct to CPUCore ") + vs_parent->getName()); + + if (!etiss::cfg().isSet("vp.elf_file")) + { + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem::initialize_virtualstruct: Requires \"vp.elf_file\" config to retrieve architecture bit-width.")); + } + + std::string elf_file = etiss::cfg().get("vp.elf_file", ""); + + ELFIO::elfio reader; + + if (!reader.load(elf_file)) + { + etiss::log(etiss::FATALERROR, "ELF reader could not process file"); + } + + if (arch_width == 32) + { + auto read = [this](size_t address, etiss::int32& return_code) { + etiss::uint32 x; + return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); + return x; + }; + auto write = [this](size_t address, etiss::uint32 word, etiss::int32& return_code) { + return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); + }; + mem_manipulator_ = std::make_unique>(read, write); + } + else if (arch_width == 64) + { + auto read = [this](size_t address, etiss::int32& return_code) { + etiss::uint64 x; + return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); + return x; + }; + auto write = [this](size_t address, etiss::uint64 word, etiss::int32& return_code) { + return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); + }; + mem_manipulator_ = std::make_unique>(read, write); + } + else + { + etiss::log(etiss::FATALERROR, std::string("Failed to initiliaze MemStack: Architecture bit width not set.")); + } + + getStruct()->applyCustomAction = [this](const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg) { + auto cmd = action.getCommand(); + + size_t pos = 0; + std::vector split_cmd; + while ((pos = cmd.find(" ")) != std::string::npos) { + split_cmd.push_back(cmd.substr(0, pos)); + cmd.erase(0, pos + 1); + } + split_cmd.push_back(cmd); + if (split_cmd.size() > 1) + { + std::stringstream ss; + etiss::uint64 dst_address; + ss << std::hex << split_cmd[1]; + ss >> dst_address; + etiss::int32 return_code; + + MemoryWordManipulatorBase::mem_manip_cmd_t mem_manip_cmd(split_cmd[0]); + switch(mem_manip_cmd) + { + case MemoryWordManipulatorBase::MemManipCmd::PUSH: + return_code = mem_manipulator_->push(dst_address); + break; + case MemoryWordManipulatorBase::MemManipCmd::POP: + return_code = mem_manipulator_->pop(dst_address); + break; + case MemoryWordManipulatorBase::MemManipCmd::RMW: + { + etiss::uint64 val; + std::stringstream ssval; + ssval << std::hex << split_cmd[3]; + ssval >> val; + return_code = mem_manipulator_->rmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), val); + break; + } + case MemoryWordManipulatorBase::MemManipCmd::RRMW: + { + etiss::uint64 src2_addr; + std::stringstream ssval; + ssval << std::hex << split_cmd[3]; + ssval >> src2_addr; + return_code = mem_manipulator_->rrmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), src2_addr); + break; + } + default: /* UNDEF */ + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + + action.getCommand() + std::string("\' unrecognized Action. Invalid memory manipulation command: ") + + split_cmd[0] ); + return false; + } + + if (return_code != etiss::RETURNCODE::NOERROR) + { + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + action.getCommand() + std::string("\' memory access error") ); + return false; + } + + return true; + } + + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + action.getCommand() + std::string("\' unrecognized Action") ); + return false; + }; + } +} + +MemoryManipulationSystem::MemoryManipulationSystem(const std::string& name) + : SimpleMemSystem() + , name_(name) + , vsystem_() + , mem_manipulator_() +{ + +} + +/* MemoryWordManipulatorBase implementation */ + +template<> +etiss::MemoryWordManipulatorBase::mem_op_t::map_t etiss::MemoryWordManipulatorBase::mem_op_t::TABLE = { + {MemoryWordManipulatorBase::MemOpType::COPY, "COPY"} + , {MemoryWordManipulatorBase::MemOpType::AND, "OR"} + , {MemoryWordManipulatorBase::MemOpType::OR, "OR"} + , {MemoryWordManipulatorBase::MemOpType::XOR, "XOR"} + , {MemoryWordManipulatorBase::MemOpType::NAND, "NAND"} + , {MemoryWordManipulatorBase::MemOpType::NOR, "NOR"} + , {MemoryWordManipulatorBase::MemOpType::UNDEF, "UNDEF"} +}; + + +MemoryWordManipulatorBase::MemOp::MemOp(const std::string& memop_str) + : mem_op_t(memop_str) +{ + if(*this == MemOpType::UNDEF) + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/MemFaulter: Unrecognized op code: ") + memop_str ); +} + +template<> +etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::map_t etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::TABLE = { + {MemoryWordManipulatorBase::MemManipCmd::PUSH, "push"} + , {MemoryWordManipulatorBase::MemManipCmd::POP, "pop"} + , {MemoryWordManipulatorBase::MemManipCmd::RMW, "rmw"} + , {MemoryWordManipulatorBase::MemManipCmd::RRMW, "rrmw"} + , {MemoryWordManipulatorBase::MemManipCmd::UNDEF, "UNDEF"} +}; From 0185fe5c807ab435fa9879ecdeabbdf86f742941 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 19 Nov 2021 20:35:19 +0100 Subject: [PATCH 14/51] remove trailing whitespaces --- .../fault/MemoryManipulationSystem.h | 6 ++--- include/etiss/fault/Misc.h | 8 +++---- .../fault/MemoryManipulationSystem.cpp | 24 +++++++++---------- src/fault/Trigger.cpp | 8 +++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h index c15d9f204b..7d53da98ae 100644 --- a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h +++ b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h @@ -98,7 +98,7 @@ class MemoryWordManipulatorBase /// \brief Constructor takes string encoded memory operation \ref MemOpType MemOp(const std::string& memop_str); } /* class MemOp */; - + //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory operation type code enum class MemManipCmd { @@ -109,7 +109,7 @@ class MemoryWordManipulatorBase , UNDEF }; typedef etiss::fault::SmartType mem_manip_cmd_t; - + virtual etiss::int32 push(size_t address) = 0; virtual etiss::int32 pop(size_t address) = 0; virtual etiss::int32 rmw(size_t address, MemOp op, etiss::uint64 mod_val) = 0; @@ -144,7 +144,7 @@ class MemoryWordManipulator : public MemoryWordManipulatorBase /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access virtual etiss::int32 rmw(size_t address, MemOp op, etiss::uint64 mod_val); //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief read-readmodify-write memory word at \p dstsrc1_address with memory word at + /// \brief read-readmodify-write memory word at \p dstsrc1_address with memory word at /// \p src2_address by bit-wise operation \p op /// \return etiss::RETURNCODE encoded via \ref MemoryManipulationSystem::dbus_access virtual etiss::int32 rrmw(size_t dstsrc1_address, MemOp op, size_t src2_address); diff --git a/include/etiss/fault/Misc.h b/include/etiss/fault/Misc.h index 1bdde8a994..06042e10ff 100644 --- a/include/etiss/fault/Misc.h +++ b/include/etiss/fault/Misc.h @@ -65,7 +65,7 @@ namespace etiss { namespace fault { - + //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Action type class template @@ -82,7 +82,7 @@ class SmartType : public etiss::ToString static bool fromString(const std::string& type_str, enum_t& out) { for(auto const& e: TABLE) { - if (type_str == e.second) + if (type_str == e.second) { out = e.first; return true; @@ -110,7 +110,7 @@ class SmartType : public etiss::ToString SmartType(const std::string& type_str) { auto ret = fromString(type_str, type_); if(!ret) - etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") + etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") + type_str + std::string("\" using default enum_t()")); } //////////////////////////////////////////////////////////////////////////////////////////// @@ -118,7 +118,7 @@ class SmartType : public etiss::ToString operator enum_t() const { return type_; } //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Typecast operator to \ref code_t - operator std::string() const { + operator std::string() const { return toString(); } } /* class SmartType */ ; diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp index ca879e631d..b024a75868 100644 --- a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -56,7 +56,7 @@ #include using namespace etiss; - + std::shared_ptr MemoryManipulationSystem::getStruct(void) { if(!vsystem_) @@ -76,9 +76,9 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrgetName() + std::string("'s VirtualStruct to ") + vs_parent->getName()); } else - { + { uint8_t arch_width = 0; - + if (etiss::cfg().isSet("arch.cpu")) { std::stringstream ss; auto cpu_arch_str = etiss::cfg().get("arch.cpu", ""); @@ -93,10 +93,10 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrgetName() + std::string("'s VirtualStruct to CPUCore ") + vs_parent->getName()); - - if (!etiss::cfg().isSet("vp.elf_file")) + + if (!etiss::cfg().isSet("vp.elf_file")) { etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem::initialize_virtualstruct: Requires \"vp.elf_file\" config to retrieve architecture bit-width.")); } @@ -156,7 +156,7 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr> dst_address; etiss::int32 return_code; - + MemoryWordManipulatorBase::mem_manip_cmd_t mem_manip_cmd(split_cmd[0]); switch(mem_manip_cmd) { @@ -185,12 +185,12 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr -etiss::MemoryWordManipulatorBase::mem_op_t::map_t etiss::MemoryWordManipulatorBase::mem_op_t::TABLE = { +etiss::MemoryWordManipulatorBase::mem_op_t::map_t etiss::MemoryWordManipulatorBase::mem_op_t::TABLE = { {MemoryWordManipulatorBase::MemOpType::COPY, "COPY"} , {MemoryWordManipulatorBase::MemOpType::AND, "OR"} , {MemoryWordManipulatorBase::MemOpType::OR, "OR"} @@ -237,7 +237,7 @@ MemoryWordManipulatorBase::MemOp::MemOp(const std::string& memop_str) } template<> -etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::map_t etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::TABLE = { +etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::map_t etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::TABLE = { {MemoryWordManipulatorBase::MemManipCmd::PUSH, "push"} , {MemoryWordManipulatorBase::MemManipCmd::POP, "pop"} , {MemoryWordManipulatorBase::MemManipCmd::RMW, "rmw"} diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index f701a38ed1..d58d9ce4e3 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -67,7 +67,7 @@ void Trigger::ensure(Type type) const } } -Trigger::Trigger() +Trigger::Trigger() : type_(Type::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (NOP)")); @@ -107,7 +107,7 @@ Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool : etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIME)")); } -Trigger::Trigger(const Trigger &cpy) +Trigger::Trigger(const Trigger &cpy) : type_(Type::NOP) { *this = cpy; @@ -419,7 +419,7 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return false; } etiss::fault::Trigger::type_t type(type_str); - + switch(type) { case etiss::fault::Trigger::Type::META_COUNTER: @@ -601,7 +601,7 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg template<> -Trigger::type_t::map_t Trigger::type_t::TABLE = { +Trigger::type_t::map_t Trigger::type_t::TABLE = { {Trigger::Type::META_COUNTER, "META_COUNTER"} , {Trigger::Type::VARIABLEVALUE, "VARIABLEVALUE"} , {Trigger::Type::TIME, "TIME"} From ef00af6be634be99e8d2dbef2a5cfe28ffe73af9 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 19 Nov 2021 20:36:55 +0100 Subject: [PATCH 15/51] replace "or" with primary token. Add parentheses in if() --- src/fault/Action.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 8d98789641..c29de124ee 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -396,71 +396,71 @@ bool write(pugi::xml_node node, const etiss::fault::Action #ifndef NO_ETISS bool returncode_fromstring(etiss::int32& out, const std::string& in) { - if (in == "NOERROR" or in == "RETURNCODE::NOERROR") + if ( (in == "NOERROR") || (in == "RETURNCODE::NOERROR") ) { out = etiss::RETURNCODE::NOERROR; } - else if (in == "RELOADBLOCKS" or in == "RETURNCODE::RELOADBLOCKS") + else if ( (in == "RELOADBLOCKS") || (in == "RETURNCODE::RELOADBLOCKS") ) { out = etiss::RETURNCODE::RELOADBLOCKS; } - else if (in == "RELOADCURRENTBLOCK" or in == "RETURNCODE::RELOADCURRENTBLOCK") + else if ( (in == "RELOADCURRENTBLOCK") || (in == "RETURNCODE::RELOADCURRENTBLOCK") ) { out = etiss::RETURNCODE::RELOADCURRENTBLOCK; } - else if (in == "CPUFINISHED" or in == "RETURNCODE::CPUFINISHED") + else if ( (in == "CPUFINISHED") || (in == "RETURNCODE::CPUFINISHED") ) { out = etiss::RETURNCODE::CPUFINISHED; } - else if (in == "DBUS_WRITE_ERROR" or in == "RETURNCODE::DBUS_WRITE_ERROR") + else if ( (in == "DBUS_WRITE_ERROR") || (in == "RETURNCODE::DBUS_WRITE_ERROR") ) { out = etiss::RETURNCODE::DBUS_WRITE_ERROR; } - else if (in == "IBUS_READ_ERROR" or in == "RETURNCODE::IBUS_READ_ERROR") + else if ( (in == "IBUS_READ_ERROR") || (in == "RETURNCODE::IBUS_READ_ERROR") ) { out = etiss::RETURNCODE::IBUS_READ_ERROR; } - else if (in == "IBUS_WRITE_ERROR" or in == "RETURNCODE::IBUS_WRITE_ERROR") + else if ( (in == "IBUS_WRITE_ERROR") || (in == "RETURNCODE::IBUS_WRITE_ERROR") ) { out = etiss::RETURNCODE::IBUS_WRITE_ERROR; } - else if (in == "INTERRUPT" or in == "RETURNCODE::INTERRUPT") + else if ( (in == "INTERRUPT") || (in == "RETURNCODE::INTERRUPT") ) { out = etiss::RETURNCODE::INTERRUPT; } - else if (in == "RESET" or in == "RETURNCODE::RESET") + else if ( (in == "RESET") || (in == "RETURNCODE::RESET") ) { out = etiss::RETURNCODE::RESET; } - else if (in == "ILLEGALINSTRUCTION" or in == "RETURNCODE::ILLEGALINSTRUCTION") + else if ( (in == "ILLEGALINSTRUCTION") || (in == "RETURNCODE::ILLEGALINSTRUCTION") ) { out = etiss::RETURNCODE::ILLEGALINSTRUCTION; } - else if (in == "ILLEGALJUMP" or in == "RETURNCODE::ILLEGALJUMP") + else if ( (in == "ILLEGALJUMP") || (in == "RETURNCODE::ILLEGALJUMP") ) { out = etiss::RETURNCODE::ILLEGALJUMP; } - else if (in == "INSTR_PAGEFAULT" or in == "RETURNCODE::INSTR_PAGEFAULT") + else if ( (in == "INSTR_PAGEFAULT") || in == ("RETURNCODE::INSTR_PAGEFAULT") ) { out = etiss::RETURNCODE::INSTR_PAGEFAULT; } - else if (in == "LOAD_PAGEFAULT" or in == "RETURNCODE::LOAD_PAGEFAULT") + else if ( (in == "LOAD_PAGEFAULT") || (in == "RETURNCODE::LOAD_PAGEFAULT") ) { out = etiss::RETURNCODE::LOAD_PAGEFAULT; } - else if (in == "STORE_PAGEFAULT" or in == "RETURNCODE::STORE_PAGEFAULT") + else if ( (in == "STORE_PAGEFAULT") || (in == "RETURNCODE::STORE_PAGEFAULT") ) { out = etiss::RETURNCODE::STORE_PAGEFAULT; } - else if (in == "SYSCALL" or in == "RETURNCODE::SYSCALL") + else if ( (in == "SYSCALL") || (in == "RETURNCODE::SYSCALL") ) { out = etiss::RETURNCODE::SYSCALL; } - else if (in == "PAGEFAULT" or in == "RETURNCODE::PAGEFAULT") + else if ( (in == "PAGEFAULT") || (in == "RETURNCODE::PAGEFAULT") ) { out = etiss::RETURNCODE::PAGEFAULT; } - else if (in == "BREAKPOINT" or in == "RETURNCODE::BREAKPOINT") + else if ( (in == "BREAKPOINT") || (in == "RETURNCODE::BREAKPOINT") ) { out = etiss::RETURNCODE::BREAKPOINT; } @@ -531,7 +531,7 @@ Action::type_t::map_t Action::type_t::TABLE = { }; template<> -Action::mask_op_t::map_t Action::mask_op_t::TABLE = { +Action::mask_op_t::map_t Action::mask_op_t::TABLE = { {Action::MaskOp::AND, "AND"} , {Action::MaskOp::OR, "OR"} , {Action::MaskOp::XOR, "XOR"} From db116a056de720683154efdef45475783a7851f6 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:35:45 +0100 Subject: [PATCH 16/51] use std::stoll instead of stringstream if number base is known --- .../fault/MemoryManipulationSystem.cpp | 174 +++++++++--------- src/fault/XML.cpp | 12 +- 2 files changed, 92 insertions(+), 94 deletions(-) diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp index b024a75868..5adc85c729 100644 --- a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -59,31 +59,33 @@ using namespace etiss; std::shared_ptr MemoryManipulationSystem::getStruct(void) { - if(!vsystem_) + if (!vsystem_) { vsystem_ = etiss::VirtualStruct::allocate(this, [](etiss::VirtualStruct::Field *f) { delete f; }); - } return vsystem_; } void MemoryManipulationSystem::init_manipulation(std::shared_ptr vs_parent) { - auto mount_successful = vs_parent->getStruct()->mountStruct(this->getName(), this->getStruct()); + auto mount_successful = vs_parent->getStruct()->mountStruct(this->getName(), this->getStruct()); - if(!mount_successful) + if (!mount_successful) { - etiss::log(etiss::FATALERROR, std::string("Failed to mount ") + this->getName() + std::string("'s VirtualStruct to ") + vs_parent->getName()); + etiss::log(etiss::FATALERROR, std::string("Failed to mount ") + this->getName() + + std::string("'s VirtualStruct to ") + vs_parent->getName()); } else { uint8_t arch_width = 0; - if (etiss::cfg().isSet("arch.cpu")) { - std::stringstream ss; + if (etiss::cfg().isSet("arch.cpu")) + { auto cpu_arch_str = etiss::cfg().get("arch.cpu", ""); arch_width = cpu_arch_str == "RISCV64" ? 64 : 32; - } else { + } + else + { // get architecture automatically std::string elf_file = etiss::cfg().get("vp.elf_file", ""); ELFIO::elfio reader; @@ -94,11 +96,13 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrgetName() + std::string("'s VirtualStruct to CPUCore ") + vs_parent->getName()); + etiss::log(etiss::VERBOSE, std::string("Mounted ") + this->getName() + + std::string("'s VirtualStruct to CPUCore ") + vs_parent->getName()); if (!etiss::cfg().isSet("vp.elf_file")) { - etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem::initialize_virtualstruct: Requires \"vp.elf_file\" config to retrieve architecture bit-width.")); + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem::initialize_virtualstruct: Requires " + "\"vp.elf_file\" config to retrieve architecture bit-width.")); } std::string elf_file = etiss::cfg().get("vp.elf_file", ""); @@ -112,135 +116,135 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); - return x; + auto read = [this](size_t address, etiss::int32 &return_code) { + etiss::uint32 x; + return_code = dbus_access(nullptr, static_cast(address), + reinterpret_cast(&x), sizeof(x)); + return x; }; - auto write = [this](size_t address, etiss::uint32 word, etiss::int32& return_code) { - return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); + auto write = [this](size_t address, etiss::uint32 word, etiss::int32 &return_code) { + return_code = dbus_access(nullptr, static_cast(address), + reinterpret_cast(&word), sizeof(word)); }; mem_manipulator_ = std::make_unique>(read, write); } else if (arch_width == 64) { - auto read = [this](size_t address, etiss::int32& return_code) { - etiss::uint64 x; - return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); - return x; + auto read = [this](size_t address, etiss::int32 &return_code) { + etiss::uint64 x; + return_code = dbus_access(nullptr, static_cast(address), + reinterpret_cast(&x), sizeof(x)); + return x; }; - auto write = [this](size_t address, etiss::uint64 word, etiss::int32& return_code) { - return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); + auto write = [this](size_t address, etiss::uint64 word, etiss::int32 &return_code) { + return_code = dbus_access(nullptr, static_cast(address), + reinterpret_cast(&word), sizeof(word)); }; mem_manipulator_ = std::make_unique>(read, write); } else { - etiss::log(etiss::FATALERROR, std::string("Failed to initiliaze MemStack: Architecture bit width not set.")); + etiss::log(etiss::FATALERROR, + std::string("Failed to initiliaze MemStack: Architecture bit width not set.")); } - getStruct()->applyCustomAction = [this](const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg) { + getStruct()->applyCustomAction = [this](const etiss::fault::Fault &fault, const etiss::fault::Action &action, + std::string &errormsg) { auto cmd = action.getCommand(); size_t pos = 0; std::vector split_cmd; - while ((pos = cmd.find(" ")) != std::string::npos) { + while ((pos = cmd.find(" ")) != std::string::npos) + { split_cmd.push_back(cmd.substr(0, pos)); cmd.erase(0, pos + 1); } split_cmd.push_back(cmd); if (split_cmd.size() > 1) { - std::stringstream ss; etiss::uint64 dst_address; - ss << std::hex << split_cmd[1]; - ss >> dst_address; etiss::int32 return_code; + dst_address = std::stoll(split_cmd[1], nullptr, 16); MemoryWordManipulatorBase::mem_manip_cmd_t mem_manip_cmd(split_cmd[0]); - switch(mem_manip_cmd) + switch (mem_manip_cmd) + { + case MemoryWordManipulatorBase::MemManipCmd::PUSH: + return_code = mem_manipulator_->push(dst_address); + break; + case MemoryWordManipulatorBase::MemManipCmd::POP: + return_code = mem_manipulator_->pop(dst_address); + break; + case MemoryWordManipulatorBase::MemManipCmd::RMW: { - case MemoryWordManipulatorBase::MemManipCmd::PUSH: - return_code = mem_manipulator_->push(dst_address); - break; - case MemoryWordManipulatorBase::MemManipCmd::POP: - return_code = mem_manipulator_->pop(dst_address); - break; - case MemoryWordManipulatorBase::MemManipCmd::RMW: - { - etiss::uint64 val; - std::stringstream ssval; - ssval << std::hex << split_cmd[3]; - ssval >> val; - return_code = mem_manipulator_->rmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), val); - break; - } - case MemoryWordManipulatorBase::MemManipCmd::RRMW: - { - etiss::uint64 src2_addr; - std::stringstream ssval; - ssval << std::hex << split_cmd[3]; - ssval >> src2_addr; - return_code = mem_manipulator_->rrmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), src2_addr); - break; - } - default: /* UNDEF */ - etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") - + action.getCommand() + std::string("\' unrecognized Action. Invalid memory manipulation command: ") - + split_cmd[0] ); - return false; + etiss::uint64 val; + val = std::stoll(split_cmd[3], nullptr, 16); + return_code = + mem_manipulator_->rmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), val); + break; + } + case MemoryWordManipulatorBase::MemManipCmd::RRMW: + { + etiss::uint64 src2_addr; + src2_addr = std::stoll(split_cmd[3], nullptr, 16); + return_code = + mem_manipulator_->rrmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), src2_addr); + break; + } + default: /* UNDEF */ + etiss::log(etiss::FATALERROR, + std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + + action.getCommand() + + std::string("\' unrecognized Action. Invalid memory manipulation command: ") + + split_cmd[0]); + return false; } if (return_code != etiss::RETURNCODE::NOERROR) { - etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + action.getCommand() + std::string("\' memory access error") ); + etiss::log(etiss::FATALERROR, + std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + + action.getCommand() + std::string("\' memory access error")); return false; } return true; } - etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + action.getCommand() + std::string("\' unrecognized Action") ); + etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/VirtualStruct/applyCustomAction: \'") + + action.getCommand() + std::string("\' unrecognized Action")); return false; }; } } -MemoryManipulationSystem::MemoryManipulationSystem(const std::string& name) - : SimpleMemSystem() - , name_(name) - , vsystem_() - , mem_manipulator_() +MemoryManipulationSystem::MemoryManipulationSystem(const std::string &name) + : SimpleMemSystem(), name_(name), vsystem_(), mem_manipulator_() { - } /* MemoryWordManipulatorBase implementation */ -template<> +template <> etiss::MemoryWordManipulatorBase::mem_op_t::map_t etiss::MemoryWordManipulatorBase::mem_op_t::TABLE = { - {MemoryWordManipulatorBase::MemOpType::COPY, "COPY"} - , {MemoryWordManipulatorBase::MemOpType::AND, "OR"} - , {MemoryWordManipulatorBase::MemOpType::OR, "OR"} - , {MemoryWordManipulatorBase::MemOpType::XOR, "XOR"} - , {MemoryWordManipulatorBase::MemOpType::NAND, "NAND"} - , {MemoryWordManipulatorBase::MemOpType::NOR, "NOR"} - , {MemoryWordManipulatorBase::MemOpType::UNDEF, "UNDEF"} + { MemoryWordManipulatorBase::MemOpType::COPY, "COPY" }, { MemoryWordManipulatorBase::MemOpType::AND, "OR" }, + { MemoryWordManipulatorBase::MemOpType::OR, "OR" }, { MemoryWordManipulatorBase::MemOpType::XOR, "XOR" }, + { MemoryWordManipulatorBase::MemOpType::NAND, "NAND" }, { MemoryWordManipulatorBase::MemOpType::NOR, "NOR" }, + { MemoryWordManipulatorBase::MemOpType::UNDEF, "UNDEF" } }; - -MemoryWordManipulatorBase::MemOp::MemOp(const std::string& memop_str) - : mem_op_t(memop_str) +MemoryWordManipulatorBase::MemOp::MemOp(const std::string &memop_str) : mem_op_t(memop_str) { - if(*this == MemOpType::UNDEF) - etiss::log(etiss::FATALERROR, std::string("MemoryManipulationSystem/MemFaulter: Unrecognized op code: ") + memop_str ); + if (*this == MemOpType::UNDEF) + etiss::log(etiss::FATALERROR, + std::string("MemoryManipulationSystem/MemFaulter: Unrecognized op code: ") + memop_str); } -template<> +template <> etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::map_t etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::TABLE = { - {MemoryWordManipulatorBase::MemManipCmd::PUSH, "push"} - , {MemoryWordManipulatorBase::MemManipCmd::POP, "pop"} - , {MemoryWordManipulatorBase::MemManipCmd::RMW, "rmw"} - , {MemoryWordManipulatorBase::MemManipCmd::RRMW, "rrmw"} - , {MemoryWordManipulatorBase::MemManipCmd::UNDEF, "UNDEF"} + { MemoryWordManipulatorBase::MemManipCmd::PUSH, "push" }, + { MemoryWordManipulatorBase::MemManipCmd::POP, "pop" }, + { MemoryWordManipulatorBase::MemManipCmd::RMW, "rmw" }, + { MemoryWordManipulatorBase::MemManipCmd::RRMW, "rrmw" }, + { MemoryWordManipulatorBase::MemManipCmd::UNDEF, "UNDEF" } }; diff --git a/src/fault/XML.cpp b/src/fault/XML.cpp index 156a4ec694..1580511d37 100644 --- a/src/fault/XML.cpp +++ b/src/fault/XML.cpp @@ -143,18 +143,14 @@ bool parse_hex(pugi::xml_node node, uint64_t &dst, Diagnostics &diag) return false; } uint64_t val; - std::stringstream ss; - ss << std::hex << ret; - ss >> val; + val = std::stoll(ret, nullptr, 16); dst = val; return true; } template <> bool write(pugi::xml_node node, const uint64_t &src, Diagnostics &diag) { - std::stringstream ss; - ss << src; - return write(node, ss.str(), diag); + return write(node, std::to_string(src), diag); } template <> @@ -170,9 +166,7 @@ bool parse(pugi::xml_node node, unsigned &dst, Diagnostics &diag) template <> bool write(pugi::xml_node node, const unsigned &src, Diagnostics &diag) { - std::stringstream ss; - ss << src; - return write(node, ss.str(), diag); + return write(node, std::to_string(src), diag); } template <> From 79411fb898f041d8c3616b1bd1a5248a0312b4d4 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:47:27 +0100 Subject: [PATCH 17/51] apply clang format to fault injection code base --- .../fault/MemoryManipulationSystem.h | 111 +- include/etiss/VirtualStruct.h | 11 +- include/etiss/fault/Action.h | 47 +- include/etiss/fault/Injector.h | 3 +- include/etiss/fault/Misc.h | 43 +- include/etiss/fault/Stressor.h | 12 +- include/etiss/fault/Trigger.h | 15 +- include/etiss/fault/xml/pugixml.hpp | 2153 +++++++++-------- .../InstructionAccurateCallback.cpp | 19 +- src/VirtualStruct.cpp | 53 +- src/fault/Action.cpp | 194 +- src/fault/Stressor.cpp | 37 +- src/fault/Trigger.cpp | 468 ++-- 13 files changed, 1611 insertions(+), 1555 deletions(-) diff --git a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h index 7d53da98ae..cbf1738dbd 100644 --- a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h +++ b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h @@ -75,14 +75,15 @@ class MemoryWordManipulatorBase public: //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory operation type code - enum class MemOpType { - COPY - , AND - , OR - , XOR - , NAND - , NOR - , UNDEF + enum class MemOpType + { + COPY, + AND, + OR, + XOR, + NAND, + NOR, + UNDEF }; typedef etiss::fault::SmartType mem_op_t; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -92,21 +93,22 @@ class MemoryWordManipulatorBase public: //////////////////////////////////////////////////////////////////////////////////////////// /// \brief executes memory operation with operands \p src1 and \p src2 and returns result - template + template word_t operator()(word_t src1, word_t src2) const; //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Constructor takes string encoded memory operation \ref MemOpType - MemOp(const std::string& memop_str); + MemOp(const std::string &memop_str); } /* class MemOp */; //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory operation type code - enum class MemManipCmd { - PUSH - , POP - , RMW - , RRMW - , UNDEF + enum class MemManipCmd + { + PUSH, + POP, + RMW, + RRMW, + UNDEF }; typedef etiss::fault::SmartType mem_manip_cmd_t; @@ -118,16 +120,16 @@ class MemoryWordManipulatorBase //////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory word faulter class template \p word_t -template +template class MemoryWordManipulator : public MemoryWordManipulatorBase { private: - std::function mem_read_word_; - ///< function to read a single word from memory - std::function mem_write_word_; - ///< function to write a single word to memory + std::function mem_read_word_; + ///< function to read a single word from memory + std::function mem_write_word_; + ///< function to write a single word to memory std::stack memstack_{}; - ///< memory stack to allow read modify write manipulations to memory + ///< memory stack to allow read modify write manipulations to memory public: //////////////////////////////////////////////////////////////////////////////////////////////// @@ -152,21 +154,21 @@ class MemoryWordManipulator : public MemoryWordManipulatorBase /// \brief Constructor taking \p mem_read_word and \p mem_write_word functions for memory /// fault action MemoryWordManipulator( - std::function const & mem_read_word, - std::function const & mem_write_word); + std::function const &mem_read_word, + std::function const &mem_write_word); } /* class MemoryWordManipulator */; //////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Simple etiss:System implementation for testing -class MemoryManipulationSystem : public VirtualStructSupport, public SimpleMemSystem +class MemoryManipulationSystem : public VirtualStructSupport, public SimpleMemSystem { public: //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Constructor takes \p name for reference of this class as a name \ref Injector - MemoryManipulationSystem(const std::string& name = "system"); + MemoryManipulationSystem(const std::string &name = "system"); //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief get virtual struct associated name - inline const std::string& getName() { return name_; } + inline const std::string &getName() { return name_; } //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief get virtual struct std::shared_ptr getStruct(void); @@ -179,34 +181,33 @@ class MemoryManipulationSystem : public VirtualStructSupport, public SimpleMemS etiss::uint8 arch_width_; std::shared_ptr vsystem_; std::unique_ptr mem_manipulator_; - }; - /* template "implemenations" */ -template +template word_t MemoryWordManipulatorBase::MemOp::operator()(word_t src1, word_t src2) const { - switch(*this) - { + switch (*this) + { case MemOpType::COPY: - return src2; + return src2; case MemOpType::AND: - return (src1 & src2); + return (src1 & src2); case MemOpType::OR: - return (src1 | src2); + return (src1 | src2); case MemOpType::XOR: - return(src1 ^ src2); + return (src1 ^ src2); case MemOpType::NAND: - return ~(src1 & src2); + return ~(src1 & src2); case MemOpType::NOR: - return ~(src1 | src2); - default: break; - } - return src1; + return ~(src1 | src2); + default: + break; + } + return src1; } -template +template etiss::int32 MemoryWordManipulator::pop(size_t address) { etiss::int32 return_code; @@ -215,7 +216,7 @@ etiss::int32 MemoryWordManipulator::pop(size_t address) return return_code; } -template +template etiss::int32 MemoryWordManipulator::push(size_t address) { etiss::int32 return_code; @@ -223,44 +224,42 @@ etiss::int32 MemoryWordManipulator::push(size_t address) return return_code; } -template +template etiss::int32 MemoryWordManipulator::rmw(size_t address, MemOp op, etiss::uint64 mod_val) { etiss::int32 return_code; word_t mem_val = mem_read_word_(address, return_code); - if(return_code == RETURNCODE::NOERROR) + if (return_code == RETURNCODE::NOERROR) { mem_val = op(mem_val, static_cast(mod_val)); mem_write_word_(address, mem_val, return_code); } return return_code; } -template +template etiss::int32 MemoryWordManipulator::rrmw(size_t dstsrc1_address, MemOp op, size_t src2_address) { etiss::int32 return_code; word_t src1mem_val = mem_read_word_(dstsrc1_address, return_code); - if(return_code == RETURNCODE::NOERROR) + if (return_code == RETURNCODE::NOERROR) { word_t src2mem_val = mem_read_word_(src2_address, return_code); - if(return_code == RETURNCODE::NOERROR) + if (return_code == RETURNCODE::NOERROR) { - src1mem_val = op(src1mem_val, src2mem_val); - mem_write_word_(dstsrc1_address, src1mem_val, return_code); + src1mem_val = op(src1mem_val, src2mem_val); + mem_write_word_(dstsrc1_address, src1mem_val, return_code); } } return return_code; } -template +template MemoryWordManipulator::MemoryWordManipulator( - std::function const & mem_read_word, - std::function const & mem_write_word) - : mem_read_word_(mem_read_word) - , mem_write_word_(mem_write_word) + std::function const &mem_read_word, + std::function const &mem_write_word) + : mem_read_word_(mem_read_word), mem_write_word_(mem_write_word) { } } // namespace etiss - #endif // ETISS_INCLUDE_INTEGRATEDLIBRARY_FAULT_MEMORYMANIPULATIONSYSTEM_H_ diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index 3aca130609..11809dd4a1 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -301,7 +301,8 @@ class VirtualStruct : public std::enable_shared_from_this, public }; private: - VirtualStruct(void *structure, std::function dtor = [](Field *f) { delete f; }); + VirtualStruct( + void *structure, std::function dtor = [](Field *f) { delete f; }); public: virtual ~VirtualStruct(); @@ -335,8 +336,9 @@ class VirtualStruct : public std::enable_shared_from_this, public flags |= Field::W; if (supportsListener) flags |= Field::L; - Field *f = new Field(*this, name, prettyname, flags, sizeof(T), false, [read]() { return (uint64_t)read(); }, - [write](uint64_t v) { write((T)v); }); + Field *f = new Field( + *this, name, prettyname, flags, sizeof(T), false, [read]() { return (uint64_t)read(); }, + [write](uint64_t v) { write((T)v); }); if (addField(f, noerrorprint)) return true; delete f; @@ -368,7 +370,8 @@ class VirtualStruct : public std::enable_shared_from_this, public virtual bool readField(void *fastfieldaccessptr, uint64_t &val, std::string &errormsg); virtual bool applyAction(const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg); - virtual bool update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string& errormsg); + virtual bool update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, + std::string &errormsg); public: /// set this function to handle custom commands passed by etiss::fault::Action of the type diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 42bf5ea0d0..299bbaa074 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -82,25 +82,30 @@ class Action : public etiss::ToString enum class Type { NOP = 0 /// NO Operation. used by default constructor - , BITFLIP /// applies a bit flip to a bit in a specified field - , MASK /// applies a mask type injection (field = mask;) where can be any MaskOp - , COMMAND /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field that - /// information must be passed within the command string - , INJECTION /// an action that injects a fault definition (trigger + actions) - #ifndef NO_ETISS - /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE exception - , EVENT - #endif + , + BITFLIP /// applies a bit flip to a bit in a specified field + , + MASK /// applies a mask type injection (field = mask;) where can be any MaskOp + , + COMMAND /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field + /// that information must be passed within the command string + , + INJECTION /// an action that injects a fault definition (trigger + actions) +#ifndef NO_ETISS + /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE exception + , + EVENT +#endif }; typedef SmartType type_t; enum class MaskOp { - NOP, - AND, - OR, - XOR, - NAND, - NOR + NOP, + AND, + OR, + XOR, + NAND, + NOR }; typedef SmartType mask_op_t; /** @@ -147,7 +152,7 @@ class Action : public etiss::ToString Action(const Fault &fault); // Getters - const type_t& getType() const; + const type_t &getType() const; const InjectorAddress &getInjectorAddress() const; @@ -161,7 +166,7 @@ class Action : public etiss::ToString /// INJECTION only const Fault &getFault() const; - const mask_op_t& getMaskOp() const; + const mask_op_t &getMaskOp() const; uint64_t getMaskValue() const; #ifndef NO_ETISS @@ -170,7 +175,7 @@ class Action : public etiss::ToString // Members std::string toString() const; ///< operator<< can be used. - private: // Attributes + private: // Attributes type_t type_; ///< type of the Attribute InjectorAddress inj_; ///< Address of Injector std::string command_; ///< command e.g. for booting OR1KVCPU @@ -180,8 +185,8 @@ class Action : public etiss::ToString uint64_t mask_value_; ///< mask value (for mask injection) std::vector fault_; ///< for fault injection #ifndef NO_ETISS - etiss::int32 event_; ///< exception, or rather etiss::RETURNCODE to - /// to be injected into the simulation loop + etiss::int32 event_; ///< exception, or rather etiss::RETURNCODE to + /// to be injected into the simulation loop #endif // private Members void ensure(Type); @@ -191,7 +196,7 @@ class Action : public etiss::ToString /** * @brief decode etiss::RETURNCODE from string */ -bool returncode_fromstring(etiss::int32& out, const std::string& in); +bool returncode_fromstring(etiss::int32 &out, const std::string &in); /** * @brief encode etiss::RETURNCODE to string */ diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index d8a21a8571..3d8127d300 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -159,7 +159,8 @@ class Injector @brief Update the \p field of injector with access rights to allow \p type actions. @detail For example, if \p action is of etiss::fault::Action::BITFLIP, \p field requires F flag set */ - virtual bool update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string &errormsg) = 0; + virtual bool update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, + std::string &errormsg) = 0; public: // static /** diff --git a/include/etiss/fault/Misc.h b/include/etiss/fault/Misc.h index 06042e10ff..c98bf86887 100644 --- a/include/etiss/fault/Misc.h +++ b/include/etiss/fault/Misc.h @@ -60,7 +60,6 @@ #include #include - namespace etiss { namespace fault @@ -68,33 +67,34 @@ namespace fault //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Action type class -template +template class SmartType : public etiss::ToString { public: - typedef std::map map_t; + typedef std::map map_t; + private: enum_t type_; static map_t TABLE; + public: //////////////////////////////////////////////////////////////////////////////////////////// /// \brief decode a string \p type_str to \ref enum_t, return true if successful - static bool fromString(const std::string& type_str, enum_t& out) { - for(auto const& e: TABLE) - { - if (type_str == e.second) + static bool fromString(const std::string &type_str, enum_t &out) + { + for (auto const &e : TABLE) { - out = e.first; - return true; + if (type_str == e.second) + { + out = e.first; + return true; + } } - } - return false; + return false; } //////////////////////////////////////////////////////////////////////////////////////////// /// \brief convert a \ref enum_t \p in to a std::string with the static \ref TABLE - static std::string toString(enum_t in) { - return TABLE.at(in); - } + static std::string toString(enum_t in) { return TABLE.at(in); } //////////////////////////////////////////////////////////////////////////////////////////// /// \brief return self as string std::string toString() const { return toString(type_); } @@ -107,21 +107,20 @@ class SmartType : public etiss::ToString //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Constructor takes \p type_str specifying action type string encoded /// \note If string does not match, default \ref enum_t() is used. Make sure to adapt - SmartType(const std::string& type_str) { + SmartType(const std::string &type_str) + { auto ret = fromString(type_str, type_); - if(!ret) - etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") - + type_str + std::string("\" using default enum_t()")); + if (!ret) + etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") + type_str + + std::string("\" using default enum_t()")); } //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Typecast operator to \ref code_t operator enum_t() const { return type_; } //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Typecast operator to \ref code_t - operator std::string() const { - return toString(); - } -} /* class SmartType */ ; + operator std::string() const { return toString(); } +} /* class SmartType */; } // end of namespace fault diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index 370a440a08..ece6ddf15f 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -69,11 +69,13 @@ namespace fault class Stressor { public: - enum Event { + enum Event + { TERMINATE = (1 << 0) #ifndef NO_ETISS - , ETISS_FLUSH_TRANSLATION_CACHE = (1 << 1) - , ETISS_RELOAD_TRANSLATION_BLOCK = (1 << 2) + , + ETISS_FLUSH_TRANSLATION_CACHE = (1 << 1), + ETISS_RELOAD_TRANSLATION_BLOCK = (1 << 2) #else #endif }; @@ -81,18 +83,20 @@ class Stressor #ifndef NO_ETISS private: static etiss::int32 event_code_; + public: static etiss::int32 get_event(void) { return event_code_; } static void set_event(etiss::int32 code) { event_code_ = code; } #else private: static int event_code_; + public: static int get_event(void) { return event_code_; } static void set_event(int code) { event_code_ = code; } static void set_event_flag(Event flag) { event_code_ |= flag; } #endif - static void reset_event(void) {event_code_ = 0;} + static void reset_event(void) { event_code_ = 0; } /** @brief extracts faults out of the given xml file. * @param file the xmlfile with fault triggers. * @return true if XML file could be loaded. diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index 312534c50f..a542171814 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -88,14 +88,15 @@ class Trigger : public etiss::ToString public: enum class Type { - META_COUNTER - , VARIABLEVALUE - , TIME + META_COUNTER, + VARIABLEVALUE, + TIME /// needs to be resolved. this can only be used in connection with an /// injection action - , TIMERELATIVE - , ASAP - , NOP + , + TIMERELATIVE, + ASAP, + NOP }; typedef SmartType type_t; // constructors @@ -149,7 +150,7 @@ class Trigger : public etiss::ToString bool isNOP() const; const std::string &getTriggerField() const; const uint64_t &getTriggerFieldValue() const; - const type_t& getType() const; + const type_t &getType() const; // Members /** @brief this function checks if the Trigger has just fired. diff --git a/include/etiss/fault/xml/pugixml.hpp b/include/etiss/fault/xml/pugixml.hpp index 99d94297d4..519f7a1ad7 100644 --- a/include/etiss/fault/xml/pugixml.hpp +++ b/include/etiss/fault/xml/pugixml.hpp @@ -13,7 +13,7 @@ #ifndef PUGIXML_VERSION // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 140 +#define PUGIXML_VERSION 140 #endif // Include user configuration file (this can define various configuration macros) @@ -31,1281 +31,1334 @@ // Include exception header for XPath #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) -# include +#include #endif // Include STL headers #ifndef PUGIXML_NO_STL -# include -# include -# include +#include +#include +#include #endif // Macro for deprecated features #ifndef PUGIXML_DEPRECATED -# if defined(__GNUC__) -# define PUGIXML_DEPRECATED __attribute__((deprecated)) -# elif defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGIXML_DEPRECATED __declspec(deprecated) -# else -# define PUGIXML_DEPRECATED -# endif +#if defined(__GNUC__) +#define PUGIXML_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) && _MSC_VER >= 1300 +#define PUGIXML_DEPRECATED __declspec(deprecated) +#else +#define PUGIXML_DEPRECATED +#endif #endif // If no API is defined, assume default #ifndef PUGIXML_API -# define PUGIXML_API +#define PUGIXML_API #endif // If no API for classes is defined, assume default #ifndef PUGIXML_CLASS -# define PUGIXML_CLASS PUGIXML_API +#define PUGIXML_CLASS PUGIXML_API #endif // If no API for functions is defined, assume default #ifndef PUGIXML_FUNCTION -# define PUGIXML_FUNCTION PUGIXML_API +#define PUGIXML_FUNCTION PUGIXML_API #endif // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG -# if defined(__cplusplus) && __cplusplus >= 201103 -# define PUGIXML_HAS_LONG_LONG -# elif defined(_MSC_VER) && _MSC_VER >= 1400 -# define PUGIXML_HAS_LONG_LONG -# endif +#if defined(__cplusplus) && __cplusplus >= 201103 +#define PUGIXML_HAS_LONG_LONG +#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#define PUGIXML_HAS_LONG_LONG +#endif #endif // Character interface macros #ifdef PUGIXML_WCHAR_MODE -# define PUGIXML_TEXT(t) L ## t -# define PUGIXML_CHAR wchar_t +#define PUGIXML_TEXT(t) L##t +#define PUGIXML_CHAR wchar_t #else -# define PUGIXML_TEXT(t) t -# define PUGIXML_CHAR char +#define PUGIXML_TEXT(t) t +#define PUGIXML_CHAR char #endif namespace pugi { - // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE - typedef PUGIXML_CHAR char_t; +// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE +typedef PUGIXML_CHAR char_t; #ifndef PUGIXML_NO_STL - // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE - typedef std::basic_string, std::allocator > string_t; +// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE +typedef std::basic_string, std::allocator> string_t; #endif -} +} // namespace pugi // The PugiXML namespace namespace pugi { - // Tree node types - enum xml_node_type - { - node_null, // Empty (null) node handle - node_document, // A document tree's absolute root - node_element, // Element tag, i.e. '' - node_pcdata, // Plain character data, i.e. 'text' - node_cdata, // Character data, i.e. '' - node_comment, // Comment tag, i.e. '' - node_pi, // Processing instruction, i.e. '' - node_declaration, // Document declaration, i.e. '' - node_doctype // Document type declaration, i.e. '' - }; - - // Parsing options - - // Minimal parsing mode (equivalent to turning all other flags off). - // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. - const unsigned int parse_minimal = 0x0000; - - // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. - const unsigned int parse_pi = 0x0001; +// Tree node types +enum xml_node_type +{ + node_null, // Empty (null) node handle + node_document, // A document tree's absolute root + node_element, // Element tag, i.e. '' + node_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' +}; + +// Parsing options + +// Minimal parsing mode (equivalent to turning all other flags off). +// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. +const unsigned int parse_minimal = 0x0000; + +// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. +const unsigned int parse_pi = 0x0001; + +// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. +const unsigned int parse_comments = 0x0002; + +// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. +const unsigned int parse_cdata = 0x0004; + +// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. +// This flag is off by default; turning it on usually results in slower parsing and more memory consumption. +const unsigned int parse_ws_pcdata = 0x0008; + +// This flag determines if character and entity references are expanded during parsing. This flag is on by default. +const unsigned int parse_escapes = 0x0010; + +// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. +const unsigned int parse_eol = 0x0020; + +// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is +// on by default. +const unsigned int parse_wconv_attribute = 0x0040; + +// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag +// is off by default. +const unsigned int parse_wnorm_attribute = 0x0080; + +// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by +// default. +const unsigned int parse_declaration = 0x0100; + +// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by +// default. +const unsigned int parse_doctype = 0x0200; + +// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that +// consists only of whitespace is added to the DOM tree. This flag is off by default; turning it on may result in slower +// parsing and more memory consumption. +const unsigned int parse_ws_pcdata_single = 0x0400; + +// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off +// by default. +const unsigned int parse_trim_pcdata = 0x0800; + +// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an +// empty document is a valid document. This flag is off by default. +const unsigned int parse_fragment = 0x1000; + +// The default parsing mode. +// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, +// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. +const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + +// The full parsing mode. +// Nodes of all types are added to the DOM tree, character/reference entities are expanded, +// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. +const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; + +// These flags determine the encoding of input data for XML document +enum xml_encoding +{ + encoding_auto, // Auto-detect input encoding using BOM or < / +class xml_object_range +{ + public: + typedef It const_iterator; + typedef It iterator; - #ifndef PUGIXML_NO_XPATH - class xpath_node; - class xpath_node_set; - class xpath_query; - class xpath_variable_set; - #endif + xml_object_range(It b, It e) : _begin(b), _end(e) {} - // Range-based for loop support - template class xml_object_range - { - public: - typedef It const_iterator; - typedef It iterator; - - xml_object_range(It b, It e): _begin(b), _end(e) - { - } - - It begin() const { return _begin; } - It end() const { return _end; } - - private: - It _begin, _end; - }; - - // Writer interface for node printing (see xml_node::print) - class PUGIXML_CLASS xml_writer - { - public: - virtual ~xml_writer() {} - - // Write memory chunk into stream/file/whatever - virtual void write(const void* data, size_t size) = 0; - }; - - // xml_writer implementation for FILE* - class PUGIXML_CLASS xml_writer_file: public xml_writer - { - public: - // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio - xml_writer_file(void* file); - - virtual void write(const void* data, size_t size); - - private: - void* file; - }; - - #ifndef PUGIXML_NO_STL - // xml_writer implementation for streams - class PUGIXML_CLASS xml_writer_stream: public xml_writer - { - public: - // Construct writer from an output stream object - xml_writer_stream(std::basic_ostream >& stream); - xml_writer_stream(std::basic_ostream >& stream); - - virtual void write(const void* data, size_t size); + It begin() const { return _begin; } + It end() const { return _end; } - private: - std::basic_ostream >* narrow_stream; - std::basic_ostream >* wide_stream; - }; - #endif + private: + It _begin, _end; +}; - // A light-weight handle for manipulating attributes in DOM tree - class PUGIXML_CLASS xml_attribute - { - friend class xml_attribute_iterator; - friend class xml_node; - - private: - xml_attribute_struct* _attr; - - typedef void (*unspecified_bool_type)(xml_attribute***); - - public: - // Default constructor. Constructs an empty attribute. - xml_attribute(); - - // Constructs attribute from internal pointer - explicit xml_attribute(xml_attribute_struct* attr); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped attribute pointers) - bool operator==(const xml_attribute& r) const; - bool operator!=(const xml_attribute& r) const; - bool operator<(const xml_attribute& r) const; - bool operator>(const xml_attribute& r) const; - bool operator<=(const xml_attribute& r) const; - bool operator>=(const xml_attribute& r) const; - - // Check if attribute is empty - bool empty() const; - - // Get attribute name/value, or "" if attribute is empty - const char_t* name() const; - const char_t* value() const; - - // Get attribute value, or the default value if attribute is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty - bool as_bool(bool def = false) const; - - // Set attribute name/value (returns false if attribute is empty or there is not enough memory) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set_value(int rhs); - bool set_value(unsigned int rhs); - bool set_value(double rhs); - bool set_value(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set_value(long long rhs); - bool set_value(unsigned long long rhs); - #endif - - // Set attribute value (equivalent to set_value without error checking) - xml_attribute& operator=(const char_t* rhs); - xml_attribute& operator=(int rhs); - xml_attribute& operator=(unsigned int rhs); - xml_attribute& operator=(double rhs); - xml_attribute& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_attribute& operator=(long long rhs); - xml_attribute& operator=(unsigned long long rhs); - #endif - - // Get next/previous attribute in the attribute list of the parent node - xml_attribute next_attribute() const; - xml_attribute previous_attribute() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_attribute_struct* internal_object() const; - }; +// Writer interface for node printing (see xml_node::print) +class PUGIXML_CLASS xml_writer +{ + public: + virtual ~xml_writer() {} -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); + // Write memory chunk into stream/file/whatever + virtual void write(const void *data, size_t size) = 0; +}; + +// xml_writer implementation for FILE* +class PUGIXML_CLASS xml_writer_file : public xml_writer +{ + public: + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio + xml_writer_file(void *file); + + virtual void write(const void *data, size_t size); + + private: + void *file; +}; + +#ifndef PUGIXML_NO_STL +// xml_writer implementation for streams +class PUGIXML_CLASS xml_writer_stream : public xml_writer +{ + public: + // Construct writer from an output stream object + xml_writer_stream(std::basic_ostream> &stream); + xml_writer_stream(std::basic_ostream> &stream); + + virtual void write(const void *data, size_t size); + + private: + std::basic_ostream> *narrow_stream; + std::basic_ostream> *wide_stream; +}; #endif - // A light-weight handle for manipulating nodes in DOM tree - class PUGIXML_CLASS xml_node - { - friend class xml_attribute_iterator; - friend class xml_node_iterator; - friend class xml_named_node_iterator; +// A light-weight handle for manipulating attributes in DOM tree +class PUGIXML_CLASS xml_attribute +{ + friend class xml_attribute_iterator; + friend class xml_node; + + private: + xml_attribute_struct *_attr; - protected: - xml_node_struct* _root; + typedef void (*unspecified_bool_type)(xml_attribute ***); - typedef void (*unspecified_bool_type)(xml_node***); - - public: - // Default constructor. Constructs an empty node. - xml_node(); - - // Constructs node from internal pointer - explicit xml_node(xml_node_struct* p); - - // Safe bool conversion operator - operator unspecified_bool_type() const; + public: + // Default constructor. Constructs an empty attribute. + xml_attribute(); - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped node pointers) - bool operator==(const xml_node& r) const; - bool operator!=(const xml_node& r) const; - bool operator<(const xml_node& r) const; - bool operator>(const xml_node& r) const; - bool operator<=(const xml_node& r) const; - bool operator>=(const xml_node& r) const; - - // Check if node is empty. - bool empty() const; - - // Get node type - xml_node_type type() const; - - // Get node name, or "" if node is empty or it has no name - const char_t* name() const; - - // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. - const char_t* value() const; - - // Get attribute list - xml_attribute first_attribute() const; - xml_attribute last_attribute() const; + // Constructs attribute from internal pointer + explicit xml_attribute(xml_attribute_struct *attr); - // Get children list - xml_node first_child() const; - xml_node last_child() const; - - // Get next/previous sibling in the children list of the parent node - xml_node next_sibling() const; - xml_node previous_sibling() const; + // Safe bool conversion operator + operator unspecified_bool_type() const; - // Get parent node - xml_node parent() const; + // Borland C++ workaround + bool operator!() const; - // Get root of DOM tree this node belongs to - xml_node root() const; - - // Get text object for the current node - xml_text text() const; - - // Get child, attribute or next/previous sibling with the specified name - xml_node child(const char_t* name) const; - xml_attribute attribute(const char_t* name) const; - xml_node next_sibling(const char_t* name) const; - xml_node previous_sibling(const char_t* name) const; - - // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA - const char_t* child_value() const; - - // Get child value of child with specified name. Equivalent to child(name).child_value(). - const char_t* child_value(const char_t* name) const; - - // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Add attribute with specified name. Returns added attribute, or empty attribute on errors. - xml_attribute append_attribute(const char_t* name); - xml_attribute prepend_attribute(const char_t* name); - xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); - xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); + // Comparison operators (compares wrapped attribute pointers) + bool operator==(const xml_attribute &r) const; + bool operator!=(const xml_attribute &r) const; + bool operator<(const xml_attribute &r) const; + bool operator>(const xml_attribute &r) const; + bool operator<=(const xml_attribute &r) const; + bool operator>=(const xml_attribute &r) const; - // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. - xml_attribute append_copy(const xml_attribute& proto); - xml_attribute prepend_copy(const xml_attribute& proto); - xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); - xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); - - // Add child node with specified type. Returns added node, or empty node on errors. - xml_node append_child(xml_node_type type = node_element); - xml_node prepend_child(xml_node_type type = node_element); - xml_node insert_child_after(xml_node_type type, const xml_node& node); - xml_node insert_child_before(xml_node_type type, const xml_node& node); - - // Add child element with specified name. Returns added node, or empty node on errors. - xml_node append_child(const char_t* name); - xml_node prepend_child(const char_t* name); - xml_node insert_child_after(const char_t* name, const xml_node& node); - xml_node insert_child_before(const char_t* name, const xml_node& node); - - // Add a copy of the specified node as a child. Returns added node, or empty node on errors. - xml_node append_copy(const xml_node& proto); - xml_node prepend_copy(const xml_node& proto); - xml_node insert_copy_after(const xml_node& proto, const xml_node& node); - xml_node insert_copy_before(const xml_node& proto, const xml_node& node); - - // Remove specified attribute - bool remove_attribute(const xml_attribute& a); - bool remove_attribute(const char_t* name); - - // Remove specified child - bool remove_child(const xml_node& n); - bool remove_child(const char_t* name); - - // Parses buffer as an XML document fragment and appends all nodes as children of the current node. - // Copies/converts the buffer, so it may be deleted or changed after the function returns. - // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. - xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Find attribute using predicate. Returns first attribute for which predicate returned true. - template xml_attribute find_attribute(Predicate pred) const - { - if (!_root) return xml_attribute(); + // Check if attribute is empty + bool empty() const; - for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) - if (pred(attrib)) - return attrib; + // Get attribute name/value, or "" if attribute is empty + const char_t *name() const; + const char_t *value() const; - return xml_attribute(); - } + // Get attribute value, or the default value if attribute is empty + const char_t *as_string(const char_t *def = PUGIXML_TEXT("")) const; - // Find child node using predicate. Returns first child for which predicate returned true. - template xml_node find_child(Predicate pred) const - { - if (!_root) return xml_node(); - - for (xml_node node = first_child(); node; node = node.next_sibling()) - if (pred(node)) - return node; - - return xml_node(); - } + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; - // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. - template xml_node find_node(Predicate pred) const - { - if (!_root) return xml_node(); - - xml_node cur = first_child(); +#ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; +#endif - while (cur._root && cur._root != _root) - { - if (pred(cur)) return cur; + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if + // attribute is empty + bool as_bool(bool def = false) const; - if (cur.first_child()) cur = cur.first_child(); - else if (cur.next_sibling()) cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); - - if (cur._root != _root) cur = cur.next_sibling(); - } - } - - return xml_node(); - } - - // Find child node by attribute name/value - xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; - xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; - - #ifndef PUGIXML_NO_STL - // Get the absolute node path from root as a text string. - string_t path(char_t delimiter = '/') const; - #endif - - // Search for a node by path consisting of node names and . or .. elements. - xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; - - // Recursively traverse subtree with xml_tree_walker - bool traverse(xml_tree_walker& walker); + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) + bool set_name(const char_t *rhs); + bool set_value(const char_t *rhs); - #ifndef PUGIXML_NO_XPATH - // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_single_node(const xpath_query& query) const; + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to + // "true"/"false") + bool set_value(int rhs); + bool set_value(unsigned int rhs); + bool set_value(double rhs); + bool set_value(bool rhs); - // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node_set select_nodes(const xpath_query& query) const; - #endif +#ifdef PUGIXML_HAS_LONG_LONG + bool set_value(long long rhs); + bool set_value(unsigned long long rhs); +#endif - // Print subtree using a writer object - void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + // Set attribute value (equivalent to set_value without error checking) + xml_attribute &operator=(const char_t *rhs); + xml_attribute &operator=(int rhs); + xml_attribute &operator=(unsigned int rhs); + xml_attribute &operator=(double rhs); + xml_attribute &operator=(bool rhs); - #ifndef PUGIXML_NO_STL - // Print subtree to stream - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; - #endif +#ifdef PUGIXML_HAS_LONG_LONG + xml_attribute &operator=(long long rhs); + xml_attribute &operator=(unsigned long long rhs); +#endif - // Child nodes iterators - typedef xml_node_iterator iterator; + // Get next/previous attribute in the attribute list of the parent node + xml_attribute next_attribute() const; + xml_attribute previous_attribute() const; - iterator begin() const; - iterator end() const; + // Get hash value (unique for handles to the same object) + size_t hash_value() const; - // Attribute iterators - typedef xml_attribute_iterator attribute_iterator; + // Get internal pointer + xml_attribute_struct *internal_object() const; +}; - attribute_iterator attributes_begin() const; - attribute_iterator attributes_end() const; +#ifdef __BORLANDC__ +// Borland C++ workaround +bool PUGIXML_FUNCTION operator&&(const xml_attribute &lhs, bool rhs); +bool PUGIXML_FUNCTION operator||(const xml_attribute &lhs, bool rhs); +#endif - // Range-based for support - xml_object_range children() const; - xml_object_range children(const char_t* name) const; - xml_object_range attributes() const; +// A light-weight handle for manipulating nodes in DOM tree +class PUGIXML_CLASS xml_node +{ + friend class xml_attribute_iterator; + friend class xml_node_iterator; + friend class xml_named_node_iterator; + + protected: + xml_node_struct *_root; - // Get node offset in parsed file/string (in char_t units) for debugging purposes - ptrdiff_t offset_debug() const; + typedef void (*unspecified_bool_type)(xml_node ***); + + public: + // Default constructor. Constructs an empty node. + xml_node(); + + // Constructs node from internal pointer + explicit xml_node(xml_node_struct *p); + + // Safe bool conversion operator + operator unspecified_bool_type() const; - // Get hash value (unique for handles to the same object) - size_t hash_value() const; + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped node pointers) + bool operator==(const xml_node &r) const; + bool operator!=(const xml_node &r) const; + bool operator<(const xml_node &r) const; + bool operator>(const xml_node &r) const; + bool operator<=(const xml_node &r) const; + bool operator>=(const xml_node &r) const; + + // Check if node is empty. + bool empty() const; + + // Get node type + xml_node_type type() const; + + // Get node name, or "" if node is empty or it has no name + const char_t *name() const; + + // Get node value, or "" if node is empty or it has no value + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access + // text inside nodes. + const char_t *value() const; + + // Get attribute list + xml_attribute first_attribute() const; + xml_attribute last_attribute() const; + + // Get children list + xml_node first_child() const; + xml_node last_child() const; + + // Get next/previous sibling in the children list of the parent node + xml_node next_sibling() const; + xml_node previous_sibling() const; + + // Get parent node + xml_node parent() const; + + // Get root of DOM tree this node belongs to + xml_node root() const; + + // Get text object for the current node + xml_text text() const; + + // Get child, attribute or next/previous sibling with the specified name + xml_node child(const char_t *name) const; + xml_attribute attribute(const char_t *name) const; + xml_node next_sibling(const char_t *name) const; + xml_node previous_sibling(const char_t *name) const; + + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA + const char_t *child_value() const; + + // Get child value of child with specified name. Equivalent to child(name).child_value(). + const char_t *child_value(const char_t *name) const; + + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) + bool set_name(const char_t *rhs); + bool set_value(const char_t *rhs); + + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. + xml_attribute append_attribute(const char_t *name); + xml_attribute prepend_attribute(const char_t *name); + xml_attribute insert_attribute_after(const char_t *name, const xml_attribute &attr); + xml_attribute insert_attribute_before(const char_t *name, const xml_attribute &attr); + + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. + xml_attribute append_copy(const xml_attribute &proto); + xml_attribute prepend_copy(const xml_attribute &proto); + xml_attribute insert_copy_after(const xml_attribute &proto, const xml_attribute &attr); + xml_attribute insert_copy_before(const xml_attribute &proto, const xml_attribute &attr); + + // Add child node with specified type. Returns added node, or empty node on errors. + xml_node append_child(xml_node_type type = node_element); + xml_node prepend_child(xml_node_type type = node_element); + xml_node insert_child_after(xml_node_type type, const xml_node &node); + xml_node insert_child_before(xml_node_type type, const xml_node &node); + + // Add child element with specified name. Returns added node, or empty node on errors. + xml_node append_child(const char_t *name); + xml_node prepend_child(const char_t *name); + xml_node insert_child_after(const char_t *name, const xml_node &node); + xml_node insert_child_before(const char_t *name, const xml_node &node); + + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. + xml_node append_copy(const xml_node &proto); + xml_node prepend_copy(const xml_node &proto); + xml_node insert_copy_after(const xml_node &proto, const xml_node &node); + xml_node insert_copy_before(const xml_node &proto, const xml_node &node); + + // Remove specified attribute + bool remove_attribute(const xml_attribute &a); + bool remove_attribute(const char_t *name); + + // Remove specified child + bool remove_child(const xml_node &n); + bool remove_child(const char_t *name); + + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes + // does not immediately reclaim that memory. + xml_parse_result append_buffer(const void *contents, size_t size, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + + // Find attribute using predicate. Returns first attribute for which predicate returned true. + template + xml_attribute find_attribute(Predicate pred) const + { + if (!_root) + return xml_attribute(); + + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) + if (pred(attrib)) + return attrib; + + return xml_attribute(); + } + + // Find child node using predicate. Returns first child for which predicate returned true. + template + xml_node find_child(Predicate pred) const + { + if (!_root) + return xml_node(); + + for (xml_node node = first_child(); node; node = node.next_sibling()) + if (pred(node)) + return node; + + return xml_node(); + } + + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate + // returned true. + template + xml_node find_node(Predicate pred) const + { + if (!_root) + return xml_node(); + + xml_node cur = first_child(); + + while (cur._root && cur._root != _root) + { + if (pred(cur)) + return cur; + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur._root != _root) + cur = cur.parent(); + + if (cur._root != _root) + cur = cur.next_sibling(); + } + } + + return xml_node(); + } + + // Find child node by attribute name/value + xml_node find_child_by_attribute(const char_t *name, const char_t *attr_name, const char_t *attr_value) const; + xml_node find_child_by_attribute(const char_t *attr_name, const char_t *attr_value) const; - // Get internal pointer - xml_node_struct* internal_object() const; - }; +#ifndef PUGIXML_NO_STL + // Get the absolute node path from root as a text string. + string_t path(char_t delimiter = '/') const; +#endif + + // Search for a node by path consisting of node names and . or .. elements. + xml_node first_element_by_path(const char_t *path, char_t delimiter = '/') const; + + // Recursively traverse subtree with xml_tree_walker + bool traverse(xml_tree_walker &walker); + +#ifndef PUGIXML_NO_XPATH + // Select single node by evaluating XPath query. Returns first node from the resulting node set. + xpath_node select_single_node(const char_t *query, xpath_variable_set *variables = 0) const; + xpath_node select_single_node(const xpath_query &query) const; + + // Select node set by evaluating XPath query + xpath_node_set select_nodes(const char_t *query, xpath_variable_set *variables = 0) const; + xpath_node_set select_nodes(const xpath_query &query) const; +#endif + + // Print subtree using a writer object + void print(xml_writer &writer, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, + xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + +#ifndef PUGIXML_NO_STL + // Print subtree to stream + void print(std::basic_ostream> &os, const char_t *indent = PUGIXML_TEXT("\t"), + unsigned int flags = format_default, xml_encoding encoding = encoding_auto, + unsigned int depth = 0) const; + void print(std::basic_ostream> &os, const char_t *indent = PUGIXML_TEXT("\t"), + unsigned int flags = format_default, unsigned int depth = 0) const; +#endif + + // Child nodes iterators + typedef xml_node_iterator iterator; + + iterator begin() const; + iterator end() const; + + // Attribute iterators + typedef xml_attribute_iterator attribute_iterator; + + attribute_iterator attributes_begin() const; + attribute_iterator attributes_end() const; + + // Range-based for support + xml_object_range children() const; + xml_object_range children(const char_t *name) const; + xml_object_range attributes() const; + + // Get node offset in parsed file/string (in char_t units) for debugging purposes + ptrdiff_t offset_debug() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_node_struct *internal_object() const; +}; #ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); +// Borland C++ workaround +bool PUGIXML_FUNCTION operator&&(const xml_node &lhs, bool rhs); +bool PUGIXML_FUNCTION operator||(const xml_node &lhs, bool rhs); #endif - // A helper for working with text inside PCDATA nodes - class PUGIXML_CLASS xml_text - { - friend class xml_node; +// A helper for working with text inside PCDATA nodes +class PUGIXML_CLASS xml_text +{ + friend class xml_node; - xml_node_struct* _root; + xml_node_struct *_root; - typedef void (*unspecified_bool_type)(xml_text***); + typedef void (*unspecified_bool_type)(xml_text ***); - explicit xml_text(xml_node_struct* root); + explicit xml_text(xml_node_struct *root); - xml_node_struct* _data_new(); - xml_node_struct* _data() const; + xml_node_struct *_data_new(); + xml_node_struct *_data() const; - public: - // Default constructor. Constructs an empty object. - xml_text(); + public: + // Default constructor. Constructs an empty object. + xml_text(); - // Safe bool conversion operator - operator unspecified_bool_type() const; + // Safe bool conversion operator + operator unspecified_bool_type() const; - // Borland C++ workaround - bool operator!() const; + // Borland C++ workaround + bool operator!() const; - // Check if text object is empty - bool empty() const; + // Check if text object is empty + bool empty() const; - // Get text, or "" if object is empty - const char_t* get() const; + // Get text, or "" if object is empty + const char_t *get() const; - // Get text, or the default value if object is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + // Get text, or the default value if object is empty + const char_t *as_string(const char_t *def = PUGIXML_TEXT("")) const; - // Get text as a number, or the default value if conversion did not succeed or object is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; + // Get text as a number, or the default value if conversion did not succeed or object is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif +#ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; +#endif - // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty - bool as_bool(bool def = false) const; + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty + bool as_bool(bool def = false) const; - // Set text (returns false if object is empty or there is not enough memory) - bool set(const char_t* rhs); + // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t *rhs); - // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set(int rhs); - bool set(unsigned int rhs); - bool set(double rhs); - bool set(bool rhs); + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set(int rhs); + bool set(unsigned int rhs); + bool set(double rhs); + bool set(bool rhs); - #ifdef PUGIXML_HAS_LONG_LONG - bool set(long long rhs); - bool set(unsigned long long rhs); - #endif +#ifdef PUGIXML_HAS_LONG_LONG + bool set(long long rhs); + bool set(unsigned long long rhs); +#endif - // Set text (equivalent to set without error checking) - xml_text& operator=(const char_t* rhs); - xml_text& operator=(int rhs); - xml_text& operator=(unsigned int rhs); - xml_text& operator=(double rhs); - xml_text& operator=(bool rhs); + // Set text (equivalent to set without error checking) + xml_text &operator=(const char_t *rhs); + xml_text &operator=(int rhs); + xml_text &operator=(unsigned int rhs); + xml_text &operator=(double rhs); + xml_text &operator=(bool rhs); - #ifdef PUGIXML_HAS_LONG_LONG - xml_text& operator=(long long rhs); - xml_text& operator=(unsigned long long rhs); - #endif +#ifdef PUGIXML_HAS_LONG_LONG + xml_text &operator=(long long rhs); + xml_text &operator=(unsigned long long rhs); +#endif - // Get the data node (node_pcdata or node_cdata) for this object - xml_node data() const; - }; + // Get the data node (node_pcdata or node_cdata) for this object + xml_node data() const; +}; #ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); +// Borland C++ workaround +bool PUGIXML_FUNCTION operator&&(const xml_text &lhs, bool rhs); +bool PUGIXML_FUNCTION operator||(const xml_text &lhs, bool rhs); #endif - // Child node iterator (a bidirectional iterator over a collection of xml_node) - class PUGIXML_CLASS xml_node_iterator - { - friend class xml_node; +// Child node iterator (a bidirectional iterator over a collection of xml_node) +class PUGIXML_CLASS xml_node_iterator +{ + friend class xml_node; - private: - mutable xml_node _wrap; - xml_node _parent; + private: + mutable xml_node _wrap; + xml_node _parent; - xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); + xml_node_iterator(xml_node_struct *ref, xml_node_struct *parent); - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node *pointer; + typedef xml_node &reference; - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif +#ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; +#endif - // Default constructor - xml_node_iterator(); + // Default constructor + xml_node_iterator(); - // Construct an iterator which points to the specified node - xml_node_iterator(const xml_node& node); + // Construct an iterator which points to the specified node + xml_node_iterator(const xml_node &node); - // Iterator operators - bool operator==(const xml_node_iterator& rhs) const; - bool operator!=(const xml_node_iterator& rhs) const; + // Iterator operators + bool operator==(const xml_node_iterator &rhs) const; + bool operator!=(const xml_node_iterator &rhs) const; - xml_node& operator*() const; - xml_node* operator->() const; + xml_node &operator*() const; + xml_node *operator->() const; - const xml_node_iterator& operator++(); - xml_node_iterator operator++(int); + const xml_node_iterator &operator++(); + xml_node_iterator operator++(int); - const xml_node_iterator& operator--(); - xml_node_iterator operator--(int); - }; + const xml_node_iterator &operator--(); + xml_node_iterator operator--(int); +}; - // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) - class PUGIXML_CLASS xml_attribute_iterator - { - friend class xml_node; +// Attribute iterator (a bidirectional iterator over a collection of xml_attribute) +class PUGIXML_CLASS xml_attribute_iterator +{ + friend class xml_node; - private: - mutable xml_attribute _wrap; - xml_node _parent; + private: + mutable xml_attribute _wrap; + xml_node _parent; - xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); + xml_attribute_iterator(xml_attribute_struct *ref, xml_node_struct *parent); - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_attribute value_type; - typedef xml_attribute* pointer; - typedef xml_attribute& reference; + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_attribute value_type; + typedef xml_attribute *pointer; + typedef xml_attribute &reference; - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif +#ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; +#endif - // Default constructor - xml_attribute_iterator(); + // Default constructor + xml_attribute_iterator(); - // Construct an iterator which points to the specified attribute - xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); + // Construct an iterator which points to the specified attribute + xml_attribute_iterator(const xml_attribute &attr, const xml_node &parent); - // Iterator operators - bool operator==(const xml_attribute_iterator& rhs) const; - bool operator!=(const xml_attribute_iterator& rhs) const; + // Iterator operators + bool operator==(const xml_attribute_iterator &rhs) const; + bool operator!=(const xml_attribute_iterator &rhs) const; - xml_attribute& operator*() const; - xml_attribute* operator->() const; + xml_attribute &operator*() const; + xml_attribute *operator->() const; - const xml_attribute_iterator& operator++(); - xml_attribute_iterator operator++(int); + const xml_attribute_iterator &operator++(); + xml_attribute_iterator operator++(int); - const xml_attribute_iterator& operator--(); - xml_attribute_iterator operator--(int); - }; + const xml_attribute_iterator &operator--(); + xml_attribute_iterator operator--(int); +}; - // Named node range helper - class PUGIXML_CLASS xml_named_node_iterator - { - friend class xml_node; +// Named node range helper +class PUGIXML_CLASS xml_named_node_iterator +{ + friend class xml_node; + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node *pointer; + typedef xml_node &reference; + +#ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; +#endif + + // Default constructor + xml_named_node_iterator(); + + // Construct an iterator which points to the specified node + xml_named_node_iterator(const xml_node &node, const char_t *name); - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; + // Iterator operators + bool operator==(const xml_named_node_iterator &rhs) const; + bool operator!=(const xml_named_node_iterator &rhs) const; + + xml_node &operator*() const; + xml_node *operator->() const; + + const xml_named_node_iterator &operator++(); + xml_named_node_iterator operator++(int); + + const xml_named_node_iterator &operator--(); + xml_named_node_iterator operator--(int); + + private: + mutable xml_node _wrap; + xml_node _parent; + const char_t *_name; + + xml_named_node_iterator(xml_node_struct *ref, xml_node_struct *parent, const char_t *name); +}; + +// Abstract tree walker class (see xml_node::traverse) +class PUGIXML_CLASS xml_tree_walker +{ + friend class xml_node; - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif + private: + int _depth; - // Default constructor - xml_named_node_iterator(); + protected: + // Get current traversal depth + int depth() const; - // Construct an iterator which points to the specified node - xml_named_node_iterator(const xml_node& node, const char_t* name); + public: + xml_tree_walker(); + virtual ~xml_tree_walker(); - // Iterator operators - bool operator==(const xml_named_node_iterator& rhs) const; - bool operator!=(const xml_named_node_iterator& rhs) const; + // Callback that is called when traversal begins + virtual bool begin(xml_node &node); - xml_node& operator*() const; - xml_node* operator->() const; + // Callback that is called for each node traversed + virtual bool for_each(xml_node &node) = 0; - const xml_named_node_iterator& operator++(); - xml_named_node_iterator operator++(int); + // Callback that is called when traversal ends + virtual bool end(xml_node &node); +}; - const xml_named_node_iterator& operator--(); - xml_named_node_iterator operator--(int); +// Parsing status, returned as part of xml_parse_result object +enum xml_parse_status +{ + status_ok = 0, // No error + + status_file_not_found, // File was not found during load_file() + status_io_error, // Error reading from file/stream + status_out_of_memory, // Could not allocate memory + status_internal_error, // Internal error occurred + + status_unrecognized_tag, // Parser could not determine tag type + + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction + status_bad_comment, // Parsing error occurred while parsing comment + status_bad_cdata, // Parsing error occurred while parsing CDATA section + status_bad_doctype, // Parsing error occurred while parsing document type declaration + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section + status_bad_start_element, // Parsing error occurred while parsing start element tag + status_bad_attribute, // Parsing error occurred while parsing element attribute + status_bad_end_element, // Parsing error occurred while parsing end element tag + status_end_element_mismatch, // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was + // not closed or there was an excessive closing tag) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document + // (exclusive to xml_node::append_buffer) + + status_no_document_element // Parsing resulted in a document without element nodes +}; + +// Parsing result +struct PUGIXML_CLASS xml_parse_result +{ + // Parsing status (see xml_parse_status) + xml_parse_status status; - private: - mutable xml_node _wrap; - xml_node _parent; - const char_t* _name; + // Last parsed offset (in char_t units from start of input data) + ptrdiff_t offset; - xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); - }; + // Source document encoding + xml_encoding encoding; - // Abstract tree walker class (see xml_node::traverse) - class PUGIXML_CLASS xml_tree_walker - { - friend class xml_node; + // Default constructor, initializes object to failed state + xml_parse_result(); - private: - int _depth; + // Cast to bool operator + operator bool() const; - protected: - // Get current traversal depth - int depth() const; + // Get error description + const char *description() const; +}; - public: - xml_tree_walker(); - virtual ~xml_tree_walker(); +// Document class (DOM tree root) +class PUGIXML_CLASS xml_document : public xml_node +{ + private: + char_t *_buffer; - // Callback that is called when traversal begins - virtual bool begin(xml_node& node); + char _memory[192]; - // Callback that is called for each node traversed - virtual bool for_each(xml_node& node) = 0; + // Non-copyable semantics + xml_document(const xml_document &); + const xml_document &operator=(const xml_document &); - // Callback that is called when traversal ends - virtual bool end(xml_node& node); - }; + void create(); + void destroy(); - // Parsing status, returned as part of xml_parse_result object - enum xml_parse_status - { - status_ok = 0, // No error + public: + // Default constructor, makes empty document + xml_document(); - status_file_not_found, // File was not found during load_file() - status_io_error, // Error reading from file/stream - status_out_of_memory, // Could not allocate memory - status_internal_error, // Internal error occurred + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); - status_unrecognized_tag, // Parser could not determine tag type + // Removes all nodes, leaving the empty document + void reset(); - status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction - status_bad_comment, // Parsing error occurred while parsing comment - status_bad_cdata, // Parsing error occurred while parsing CDATA section - status_bad_doctype, // Parsing error occurred while parsing document type declaration - status_bad_pcdata, // Parsing error occurred while parsing PCDATA section - status_bad_start_element, // Parsing error occurred while parsing start element tag - status_bad_attribute, // Parsing error occurred while parsing element attribute - status_bad_end_element, // Parsing error occurred while parsing end element tag - status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + // Removes all nodes, then copies the entire contents of the specified document + void reset(const xml_document &proto); - status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) +#ifndef PUGIXML_NO_STL + // Load document from stream. + xml_parse_result load(std::basic_istream> &stream, + unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream> &stream, + unsigned int options = parse_default); +#endif - status_no_document_element // Parsing resulted in a document without element nodes - }; + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load(const char_t *contents, unsigned int options = parse_default); + + // Load document from file + xml_parse_result load_file(const char *path, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + xml_parse_result load_file(const wchar_t *path, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function + // returns. + xml_parse_result load_buffer(const void *contents, size_t size, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of + // document data). You should ensure that buffer data will persist throughout the document's lifetime, and free the + // buffer memory manually once document is destroyed. + xml_parse_result load_buffer_inplace(void *contents, size_t size, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of + // document data). You should allocate the buffer with pugixml allocation function; document will free the buffer + // when it is no longer needed (you can't use it anymore). + xml_parse_result load_buffer_inplace_own(void *contents, size_t size, unsigned int options = parse_default, + xml_encoding encoding = encoding_auto); + + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for + // details). + void save(xml_writer &writer, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, + xml_encoding encoding = encoding_auto) const; - // Parsing result - struct PUGIXML_CLASS xml_parse_result - { - // Parsing status (see xml_parse_status) - xml_parse_status status; +#ifndef PUGIXML_NO_STL + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for + // details). + void save(std::basic_ostream> &stream, const char_t *indent = PUGIXML_TEXT("\t"), + unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream> &stream, const char_t *indent = PUGIXML_TEXT("\t"), + unsigned int flags = format_default) const; +#endif - // Last parsed offset (in char_t units from start of input data) - ptrdiff_t offset; + // Save XML to file + bool save_file(const char *path, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, + xml_encoding encoding = encoding_auto) const; + bool save_file(const wchar_t *path, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, + xml_encoding encoding = encoding_auto) const; - // Source document encoding - xml_encoding encoding; + // Get document element + xml_node document_element() const; +}; - // Default constructor, initializes object to failed state - xml_parse_result(); +#ifndef PUGIXML_NO_XPATH +// XPath query return type +enum xpath_value_type +{ + xpath_type_none, // Unknown type (query failed to compile) + xpath_type_node_set, // Node set (xpath_node_set) + xpath_type_number, // Number + xpath_type_string, // String + xpath_type_boolean // Boolean +}; + +// XPath parsing result +struct PUGIXML_CLASS xpath_parse_result +{ + // Error message (0 if no error) + const char *error; - // Cast to bool operator - operator bool() const; + // Last parsed offset (in char_t units from string start) + ptrdiff_t offset; - // Get error description - const char* description() const; - }; + // Default constructor, initializes object to failed state + xpath_parse_result(); - // Document class (DOM tree root) - class PUGIXML_CLASS xml_document: public xml_node - { - private: - char_t* _buffer; + // Cast to bool operator + operator bool() const; - char _memory[192]; + // Get error description + const char *description() const; +}; - // Non-copyable semantics - xml_document(const xml_document&); - const xml_document& operator=(const xml_document&); +// A single XPath variable +class PUGIXML_CLASS xpath_variable +{ + friend class xpath_variable_set; - void create(); - void destroy(); + protected: + xpath_value_type _type; + xpath_variable *_next; - public: - // Default constructor, makes empty document - xml_document(); + xpath_variable(); - // Destructor, invalidates all node/attribute handles to this document - ~xml_document(); + // Non-copyable semantics + xpath_variable(const xpath_variable &); + xpath_variable &operator=(const xpath_variable &); - // Removes all nodes, leaving the empty document - void reset(); + public: + // Get variable name + const char_t *name() const; - // Removes all nodes, then copies the entire contents of the specified document - void reset(const xml_document& proto); + // Get variable type + xpath_value_type type() const; - #ifndef PUGIXML_NO_STL - // Load document from stream. - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); - #endif + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is + // returned on type mismatch error + bool get_boolean() const; + double get_number() const; + const char_t *get_string() const; + const xpath_node_set &get_node_set() const; - // Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load(const char_t* contents, unsigned int options = parse_default); + // Set variable value; no type conversion is performed, false is returned on type mismatch error + bool set(bool value); + bool set(double value); + bool set(const char_t *value); + bool set(const xpath_node_set &value); +}; - // Load document from file - xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); +// A set of XPath variables +class PUGIXML_CLASS xpath_variable_set +{ + private: + xpath_variable *_data[64]; - // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. - xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + // Non-copyable semantics + xpath_variable_set(const xpath_variable_set &); + xpath_variable_set &operator=(const xpath_variable_set &); - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. - xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xpath_variable *find(const char_t *name) const; - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). - xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); - // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). - void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + // Add a new variable or get the existing one, if the types match + xpath_variable *add(const char_t *name, xpath_value_type type); - #ifndef PUGIXML_NO_STL - // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; - #endif + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such + // variable or if types mismatch + bool set(const char_t *name, bool value); + bool set(const char_t *name, double value); + bool set(const char_t *name, const char_t *value); + bool set(const char_t *name, const xpath_node_set &value); - // Save XML to file - bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + // Get existing variable by name + xpath_variable *get(const char_t *name); + const xpath_variable *get(const char_t *name) const; +}; - // Get document element - xml_node document_element() const; - }; +// A compiled XPath query object +class PUGIXML_CLASS xpath_query +{ + private: + void *_impl; + xpath_parse_result _result; -#ifndef PUGIXML_NO_XPATH - // XPath query return type - enum xpath_value_type - { - xpath_type_none, // Unknown type (query failed to compile) - xpath_type_node_set, // Node set (xpath_node_set) - xpath_type_number, // Number - xpath_type_string, // String - xpath_type_boolean // Boolean - }; - - // XPath parsing result - struct PUGIXML_CLASS xpath_parse_result - { - // Error message (0 if no error) - const char* error; - - // Last parsed offset (in char_t units from string start) - ptrdiff_t offset; - - // Default constructor, initializes object to failed state - xpath_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // A single XPath variable - class PUGIXML_CLASS xpath_variable - { - friend class xpath_variable_set; - - protected: - xpath_value_type _type; - xpath_variable* _next; - - xpath_variable(); - - // Non-copyable semantics - xpath_variable(const xpath_variable&); - xpath_variable& operator=(const xpath_variable&); - - public: - // Get variable name - const char_t* name() const; - - // Get variable type - xpath_value_type type() const; - - // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error - bool get_boolean() const; - double get_number() const; - const char_t* get_string() const; - const xpath_node_set& get_node_set() const; - - // Set variable value; no type conversion is performed, false is returned on type mismatch error - bool set(bool value); - bool set(double value); - bool set(const char_t* value); - bool set(const xpath_node_set& value); - }; - - // A set of XPath variables - class PUGIXML_CLASS xpath_variable_set - { - private: - xpath_variable* _data[64]; - - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set&); - xpath_variable_set& operator=(const xpath_variable_set&); - - xpath_variable* find(const char_t* name) const; - - public: - // Default constructor/destructor - xpath_variable_set(); - ~xpath_variable_set(); - - // Add a new variable or get the existing one, if the types match - xpath_variable* add(const char_t* name, xpath_value_type type); - - // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch - bool set(const char_t* name, bool value); - bool set(const char_t* name, double value); - bool set(const char_t* name, const char_t* value); - bool set(const char_t* name, const xpath_node_set& value); - - // Get existing variable by name - xpath_variable* get(const char_t* name); - const xpath_variable* get(const char_t* name) const; - }; - - // A compiled XPath query object - class PUGIXML_CLASS xpath_query - { - private: - void* _impl; - xpath_parse_result _result; - - typedef void (*unspecified_bool_type)(xpath_query***); - - // Non-copyable semantics - xpath_query(const xpath_query&); - xpath_query& operator=(const xpath_query&); - - public: - // Construct a compiled object from XPath expression. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); - - // Destructor - ~xpath_query(); - - // Get query expression return type - xpath_value_type return_type() const; - - // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - bool evaluate_boolean(const xpath_node& n) const; - - // Evaluate expression as double value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - double evaluate_number(const xpath_node& n) const; - - #ifndef PUGIXML_NO_STL - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - string_t evaluate_string(const xpath_node& n) const; - #endif - - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. - size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. - xpath_node_set evaluate_node_set(const xpath_node& n) const; - - // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) - const xpath_parse_result& result() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - }; - - #ifndef PUGIXML_NO_EXCEPTIONS - // XPath exception class - class PUGIXML_CLASS xpath_exception: public std::exception - { - private: - xpath_parse_result _result; - - public: - // Construct exception from parse result - explicit xpath_exception(const xpath_parse_result& result); - - // Get error message - virtual const char* what() const throw(); - - // Get parse result - const xpath_parse_result& result() const; - }; - #endif - - // XPath node class (either xml_node or xml_attribute) - class PUGIXML_CLASS xpath_node - { - private: - xml_node _node; - xml_attribute _attribute; - - typedef void (*unspecified_bool_type)(xpath_node***); - - public: - // Default constructor; constructs empty XPath node - xpath_node(); - - // Construct XPath node from XML node/attribute - xpath_node(const xml_node& node); - xpath_node(const xml_attribute& attribute, const xml_node& parent); + typedef void (*unspecified_bool_type)(xpath_query ***); - // Get node/attribute, if any - xml_node node() const; - xml_attribute attribute() const; + // Non-copyable semantics + xpath_query(const xpath_query &); + xpath_query &operator=(const xpath_query &); - // Get parent of contained node/attribute - xml_node parent() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; + public: + // Construct a compiled object from XPath expression. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. + explicit xpath_query(const char_t *query, xpath_variable_set *variables = 0); - // Borland C++ workaround - bool operator!() const; + // Destructor + ~xpath_query(); - // Comparison operators - bool operator==(const xpath_node& n) const; - bool operator!=(const xpath_node& n) const; - }; + // Get query expression return type + xpath_value_type return_type() const; + + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + bool evaluate_boolean(const xpath_node &n) const; + + // Evaluate expression as double value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + double evaluate_number(const xpath_node &n) const; + +#ifndef PUGIXML_NO_STL + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + string_t evaluate_string(const xpath_node &n) const; +#endif + + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // At most capacity characters are written to the destination buffer, full result size is returned (includes + // terminating zero). If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. If + // PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. + size_t evaluate_string(char_t *buffer, size_t capacity, const xpath_node &n) const; + + // Evaluate expression as node set in the specified context. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of + // memory errors. If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. + xpath_node_set evaluate_node_set(const xpath_node &n) const; + + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) + const xpath_parse_result &result() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; +}; + +#ifndef PUGIXML_NO_EXCEPTIONS +// XPath exception class +class PUGIXML_CLASS xpath_exception : public std::exception +{ + private: + xpath_parse_result _result; + + public: + // Construct exception from parse result + explicit xpath_exception(const xpath_parse_result &result); + + // Get error message + virtual const char *what() const throw(); + + // Get parse result + const xpath_parse_result &result() const; +}; +#endif + +// XPath node class (either xml_node or xml_attribute) +class PUGIXML_CLASS xpath_node +{ + private: + xml_node _node; + xml_attribute _attribute; + + typedef void (*unspecified_bool_type)(xpath_node ***); + + public: + // Default constructor; constructs empty XPath node + xpath_node(); + + // Construct XPath node from XML node/attribute + xpath_node(const xml_node &node); + xpath_node(const xml_attribute &attribute, const xml_node &parent); + + // Get node/attribute, if any + xml_node node() const; + xml_attribute attribute() const; + + // Get parent of contained node/attribute + xml_node parent() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators + bool operator==(const xpath_node &n) const; + bool operator!=(const xpath_node &n) const; +}; #ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); +// Borland C++ workaround +bool PUGIXML_FUNCTION operator&&(const xpath_node &lhs, bool rhs); +bool PUGIXML_FUNCTION operator||(const xpath_node &lhs, bool rhs); #endif - // A fixed-size collection of XPath nodes - class PUGIXML_CLASS xpath_node_set - { - public: - // Collection type - enum type_t - { - type_unsorted, // Not ordered - type_sorted, // Sorted by document order (ascending) - type_sorted_reverse // Sorted by document order (descending) - }; +// A fixed-size collection of XPath nodes +class PUGIXML_CLASS xpath_node_set +{ + public: + // Collection type + enum type_t + { + type_unsorted, // Not ordered + type_sorted, // Sorted by document order (ascending) + type_sorted_reverse // Sorted by document order (descending) + }; - // Constant iterator type - typedef const xpath_node* const_iterator; + // Constant iterator type + typedef const xpath_node *const_iterator; - // Default constructor. Constructs empty set. - xpath_node_set(); + // Default constructor. Constructs empty set. + xpath_node_set(); - // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful - xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided + // type, so be careful + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); - // Destructor - ~xpath_node_set(); + // Destructor + ~xpath_node_set(); - // Copy constructor/assignment operator - xpath_node_set(const xpath_node_set& ns); - xpath_node_set& operator=(const xpath_node_set& ns); + // Copy constructor/assignment operator + xpath_node_set(const xpath_node_set &ns); + xpath_node_set &operator=(const xpath_node_set &ns); - // Get collection type - type_t type() const; + // Get collection type + type_t type() const; - // Get collection size - size_t size() const; + // Get collection size + size_t size() const; - // Indexing operator - const xpath_node& operator[](size_t index) const; + // Indexing operator + const xpath_node &operator[](size_t index) const; - // Collection iterators - const_iterator begin() const; - const_iterator end() const; + // Collection iterators + const_iterator begin() const; + const_iterator end() const; - // Sort the collection in ascending/descending order by document order - void sort(bool reverse = false); + // Sort the collection in ascending/descending order by document order + void sort(bool reverse = false); - // Get first node in the collection by document order - xpath_node first() const; + // Get first node in the collection by document order + xpath_node first() const; - // Check if collection is empty - bool empty() const; + // Check if collection is empty + bool empty() const; - private: - type_t _type; + private: + type_t _type; - xpath_node _storage; + xpath_node _storage; - xpath_node* _begin; - xpath_node* _end; + xpath_node *_begin; + xpath_node *_end; - void _assign(const_iterator begin, const_iterator end); - }; + void _assign(const_iterator begin, const_iterator end); +}; #endif #ifndef PUGIXML_NO_STL - // Convert wide string to UTF8 - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); - - // Convert UTF8 to wide string - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); +// Convert wide string to UTF8 +std::basic_string, std::allocator> PUGIXML_FUNCTION as_utf8(const wchar_t *str); +std::basic_string, std::allocator> PUGIXML_FUNCTION +as_utf8(const std::basic_string, std::allocator> &str); + +// Convert UTF8 to wide string +std::basic_string, std::allocator> PUGIXML_FUNCTION +as_wide(const char *str); +std::basic_string, std::allocator> PUGIXML_FUNCTION +as_wide(const std::basic_string, std::allocator> &str); #endif - // Memory allocation function interface; returns pointer to allocated memory or NULL on failure - typedef void* (*allocation_function)(size_t size); +// Memory allocation function interface; returns pointer to allocated memory or NULL on failure +typedef void *(*allocation_function)(size_t size); - // Memory deallocation function interface - typedef void (*deallocation_function)(void* ptr); +// Memory deallocation function interface +typedef void (*deallocation_function)(void *ptr); - // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. - void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); +// Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied +// functions. +void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - // Get current memory management functions - allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); - deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); -} +// Get current memory management functions +allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); +deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); +} // namespace pugi #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); -} +// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) +std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator &); +std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator &); +std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator &); +} // namespace std #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { - // Workarounds for (non-standard) iterator category detection - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); -} +// Workarounds for (non-standard) iterator category detection +std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator &); +std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator &); +std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator &); +} // namespace std #endif #endif diff --git a/src/IntegratedLibrary/InstructionAccurateCallback.cpp b/src/IntegratedLibrary/InstructionAccurateCallback.cpp index 71a8c5aba7..d7d98eb691 100644 --- a/src/IntegratedLibrary/InstructionAccurateCallback.cpp +++ b/src/IntegratedLibrary/InstructionAccurateCallback.cpp @@ -77,7 +77,9 @@ void InstructionAccurateCallback::finalizeInstrSet(etiss::instr::ModedInstructio i.addCallback( [this](etiss::instr::BitArray &, etiss::CodeSet &cs, etiss::instr::InstructionContext &) { etiss::CodePart &pp = cs.prepend(etiss::CodePart::INITIALREQUIRED); - pp.code() = std::string("etiss_int32 ret_iac = etiss_plugin_InstructionAccurateCallback_OnEntry(") + getPointerCode() + ");\nif(ret_iac != 0)return(ret_iac);"; + pp.code() = + std::string("etiss_int32 ret_iac = etiss_plugin_InstructionAccurateCallback_OnEntry(") + + getPointerCode() + ");\nif(ret_iac != 0)return(ret_iac);"; return true; }, 0); @@ -98,18 +100,17 @@ etiss_int32 InstructionAccurateCallback::call_on_entry() etiss::fault::Stressor::reset_event(); - trigger_fired |= plugin_core_->getStruct()->instructionAccurateCallback(time); // call instruction callback of plugin-associated core + trigger_fired |= plugin_core_->getStruct()->instructionAccurateCallback( + time); // call instruction callback of plugin-associated core plugin_core_->getStruct()->foreachStruct( // call instruction callback of all VirtualStructs mounted on core - [time,&trigger_fired](const std::string &name, VirtualStruct &vs) - { - trigger_fired |= vs.instructionAccurateCallback(time); - } - ); + [time, &trigger_fired](const std::string &name, VirtualStruct &vs) { + trigger_fired |= vs.instructionAccurateCallback(time); + }); - return(trigger_fired ? etiss::fault::Stressor::get_event() : 0); // signal that a trigger has thrown an exception back to the JIT code calling + return (trigger_fired ? etiss::fault::Stressor::get_event() + : 0); // signal that a trigger has thrown an exception back to the JIT code calling } - } // namespace plugin } // namespace etiss diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index 067ee92649..aa17d67735 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -178,7 +178,7 @@ bool VirtualStruct::Field::applyBitflip(unsigned position, uint64_t fault_id) bool VirtualStruct::Field::applyAction(const etiss::fault::Fault &f, const etiss::fault::Action &a, std::string &errormsg) { - if (!(flags_ & (A|F))) + if (!(flags_ & (A | F))) { errormsg = "field doesn't support action handling"; return false; @@ -214,40 +214,41 @@ bool VirtualStruct::Field::_applyBitflip(unsigned position, uint64_t fault_id) bool VirtualStruct::Field::_applyAction(const etiss::fault::Fault &f, const etiss::fault::Action &a, std::string &errormsg) { - if(a.getType() == etiss::fault::Action::Type::MASK) + if (a.getType() == etiss::fault::Action::Type::MASK) { uint64_t mask_value = a.getMaskValue(); uint64_t val = read(), errval; switch (a.getMaskOp()) { - case etiss::fault::Action::MaskOp::AND: + case etiss::fault::Action::MaskOp::AND: errval = (val & mask_value); break; - case etiss::fault::Action::MaskOp::OR: + case etiss::fault::Action::MaskOp::OR: errval = (val | mask_value); break; - case etiss::fault::Action::MaskOp::XOR: + case etiss::fault::Action::MaskOp::XOR: errval = (val ^ mask_value); break; - case etiss::fault::Action::MaskOp::NAND: + case etiss::fault::Action::MaskOp::NAND: errval = ~(val & mask_value); break; - case etiss::fault::Action::MaskOp::NOR: - errval = ~(val | mask_value); + case etiss::fault::Action::MaskOp::NOR: + errval = ~(val | mask_value); break; - case etiss::fault::Action::MaskOp::NOP: + case etiss::fault::Action::MaskOp::NOP: errval = val; break; } write(errval); std::stringstream ss; - ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << std::string(a.getMaskOp()) << " 0x" << mask_value << "->0x" << errval << std::dec; + ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << std::string(a.getMaskOp()) + << " 0x" << mask_value << "->0x" << errval << std::dec; etiss::log(etiss::INFO, ss.str()); return true; } - else if(a.getType() == etiss::fault::Action::Type::BITFLIP) + else if (a.getType() == etiss::fault::Action::Type::BITFLIP) { - return applyBitflip(a.getTargetBit(), f.id_); + return applyBitflip(a.getTargetBit(), f.id_); } return false; } @@ -511,7 +512,8 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } return applyCustomAction(fault, action, errormsg); } - case etiss::fault::Action::Type::MASK: [[fallthrough]]; + case etiss::fault::Action::Type::MASK: + [[fallthrough]]; case etiss::fault::Action::Type::BITFLIP: // handle bitflip { Field *f; @@ -549,9 +551,10 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } } -bool VirtualStruct::update_field_access_rights(const std::string& field, etiss::fault::Action::Type type, std::string &errormsg) +bool VirtualStruct::update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, + std::string &errormsg) { - Field* f = nullptr; + Field *f = nullptr; auto find = fieldNames_.find(field); if (find == fieldNames_.end()) { @@ -570,21 +573,23 @@ bool VirtualStruct::update_field_access_rights(const std::string& field, etiss:: f = find->second; } - if(f) + if (f) { - switch(type) + switch (type) { - case etiss::fault::Action::Type::MASK: [[fallthrough]]; - case etiss::fault::Action::Type::BITFLIP: - f->flags_ |= Field::F; - break; - default: - break; + case etiss::fault::Action::Type::MASK: + [[fallthrough]]; + case etiss::fault::Action::Type::BITFLIP: + f->flags_ |= Field::F; + break; + default: + break; } } else { - errormsg = std::string("VirtualStruct:update_field_access_rights(): Required field not a field in VirtualStruct!"); + errormsg = + std::string("VirtualStruct:update_field_access_rights(): Required field not a field in VirtualStruct!"); return false; } return true; diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index c29de124ee..8ed6ffa179 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -65,35 +65,27 @@ bool Action::is_action_on_field(void) const return ((type_ == Type::BITFLIP || type_ == Type::MASK) ? true : false); } -Action::Action() - : type_(Type::NOP) +Action::Action() : type_(Type::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action() called. ")); } #ifndef NO_ETISS -Action::Action(etiss::int32 event) - : type_(Type::EVENT) - , event_(event) +Action::Action(etiss::int32 event) : type_(Type::EVENT), event_(event) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(etiss::int32 exception) called. ")); } #endif Action::Action(const InjectorAddress &inj, const std::string &command) - : type_(Type::COMMAND) - , inj_(inj) - , command_(command) + : type_(Type::COMMAND), inj_(inj), command_(command) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", command=") + command + std::string(") called. ")); } Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bit) - : type_(Type::BITFLIP) - , inj_(inj) - , field_(field) - , bit_(bit) + : type_(Type::BITFLIP), inj_(inj), field_(field), bit_(bit) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", bit=") + std::to_string(bit) + @@ -101,11 +93,7 @@ Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bi } Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value) - : type_(Type::MASK) - , inj_(inj) - , field_(field) - , mask_op_(mask_op) - , mask_value_(mask_value) + : type_(Type::MASK), inj_(inj), field_(field), mask_op_(mask_op), mask_value_(mask_value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", mask_op=") + std::string(mask_op_) + @@ -113,84 +101,94 @@ Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask std::string(") called. ")); } -Action::Action(const Fault &fault) - : type_(Type::INJECTION) +Action::Action(const Fault &fault) : type_(Type::INJECTION) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(Fault &=") + fault.toString() + std::string(") called. ")); fault_.push_back(fault); } -const Action::type_t& Action::getType() const +const Action::type_t &Action::getType() const { return type_; } const InjectorAddress &Action::getInjectorAddress() const { - if(unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND))) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type is not Injector")); + if (unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND))) + etiss::log( + etiss::FATALERROR, + std::string( + "etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type is not Injector")); return inj_; } /// COMMAND only const std::string &Action::getCommand() const { - if(unlikely(type_ != Type::COMMAND)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getCommand(): Requested Action::Type is not Command")); + if (unlikely(type_ != Type::COMMAND)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getCommand(): Requested Action::Type is not Command")); return command_; } /// is_action_on_field only const std::string &Action::getTargetField() const { - if(unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK))) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetField(): Requested Action::Type is not TargetField")); + if (unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK))) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getTargetField(): Requested Action::Type is not TargetField")); return field_; } /// BITFLIP only unsigned Action::getTargetBit() const { - if(unlikely(type_ != Type::BITFLIP)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetBit(): Requested Action::Type is not TargetBit")); + if (unlikely(type_ != Type::BITFLIP)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getTargetBit(): Requested Action::Type is not TargetBit")); return bit_; } const Fault &Action::getFault() const { - if(unlikely(type_ != Type::INJECTION)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); + if (unlikely(type_ != Type::INJECTION)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); return fault_.front(); } -//Action::MaskOp Action::getMaskOp() const -const Action::mask_op_t& Action::getMaskOp() const +// Action::MaskOp Action::getMaskOp() const +const Action::mask_op_t &Action::getMaskOp() const { - if(unlikely(type_ != Type::MASK)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); + if (unlikely(type_ != Type::MASK)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); return mask_op_; } -//std::string Action::getMaskOpString() const +// std::string Action::getMaskOpString() const //{ // if(unlikely(type_ != Type::MASK)) -// etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); +// etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not +// Mask")); // return mask_op_; //} uint64_t Action::getMaskValue() const { - if(unlikely(type_ != Type::MASK)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskValue(): Requested Action::Type is not Mask")); + if (unlikely(type_ != Type::MASK)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getMaskValue(): Requested Action::Type is not Mask")); return mask_value_; } #ifndef NO_ETISS etiss::int32 Action::getEvent() const { - if(unlikely(type_ != Type::EVENT)) - etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getEvent(): Requested Action::Type is not Event")); + if (unlikely(type_ != Type::EVENT)) + etiss::log(etiss::FATALERROR, + std::string("etiss::fault::Action::getEvent(): Requested Action::Type is not Event")); return event_; } #endif @@ -308,7 +306,7 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D diag.unexpectedNode(node, "Failed to parse mask operation "); return false; } - if (! etiss::fault::Action::mask_op_t::fromString(op_str, op)) + if (!etiss::fault::Action::mask_op_t::fromString(op_str, op)) { diag.unexpectedNode(node, "Failed to parse mask operation "); return false; @@ -332,9 +330,10 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D diag.unexpectedNode(node, "Failed to parse node of event type: "); return false; } - if (! etiss::fault::returncode_fromstring(event, event_str)) + if (!etiss::fault::returncode_fromstring(event, event_str)) { - diag.unexpectedNode(node, "Failed to parse event . Supported values: {NOERROR, RELOADBLOCKS, RELOADCURRENTBLOCK, CPUFINISHED, EXCEPTION:[cause(hex)]}"); + diag.unexpectedNode(node, "Failed to parse event . Supported values: {NOERROR, RELOADBLOCKS, " + "RELOADCURRENTBLOCK, CPUFINISHED, EXCEPTION:[cause(hex)]}"); return false; } f = Action(event); @@ -394,73 +393,73 @@ bool write(pugi::xml_node node, const etiss::fault::Action #endif #ifndef NO_ETISS -bool returncode_fromstring(etiss::int32& out, const std::string& in) +bool returncode_fromstring(etiss::int32 &out, const std::string &in) { - if ( (in == "NOERROR") || (in == "RETURNCODE::NOERROR") ) + if ((in == "NOERROR") || (in == "RETURNCODE::NOERROR")) { out = etiss::RETURNCODE::NOERROR; } - else if ( (in == "RELOADBLOCKS") || (in == "RETURNCODE::RELOADBLOCKS") ) + else if ((in == "RELOADBLOCKS") || (in == "RETURNCODE::RELOADBLOCKS")) { - out = etiss::RETURNCODE::RELOADBLOCKS; + out = etiss::RETURNCODE::RELOADBLOCKS; } - else if ( (in == "RELOADCURRENTBLOCK") || (in == "RETURNCODE::RELOADCURRENTBLOCK") ) + else if ((in == "RELOADCURRENTBLOCK") || (in == "RETURNCODE::RELOADCURRENTBLOCK")) { - out = etiss::RETURNCODE::RELOADCURRENTBLOCK; + out = etiss::RETURNCODE::RELOADCURRENTBLOCK; } - else if ( (in == "CPUFINISHED") || (in == "RETURNCODE::CPUFINISHED") ) + else if ((in == "CPUFINISHED") || (in == "RETURNCODE::CPUFINISHED")) { - out = etiss::RETURNCODE::CPUFINISHED; + out = etiss::RETURNCODE::CPUFINISHED; } - else if ( (in == "DBUS_WRITE_ERROR") || (in == "RETURNCODE::DBUS_WRITE_ERROR") ) + else if ((in == "DBUS_WRITE_ERROR") || (in == "RETURNCODE::DBUS_WRITE_ERROR")) { out = etiss::RETURNCODE::DBUS_WRITE_ERROR; } - else if ( (in == "IBUS_READ_ERROR") || (in == "RETURNCODE::IBUS_READ_ERROR") ) + else if ((in == "IBUS_READ_ERROR") || (in == "RETURNCODE::IBUS_READ_ERROR")) { out = etiss::RETURNCODE::IBUS_READ_ERROR; } - else if ( (in == "IBUS_WRITE_ERROR") || (in == "RETURNCODE::IBUS_WRITE_ERROR") ) + else if ((in == "IBUS_WRITE_ERROR") || (in == "RETURNCODE::IBUS_WRITE_ERROR")) { out = etiss::RETURNCODE::IBUS_WRITE_ERROR; } - else if ( (in == "INTERRUPT") || (in == "RETURNCODE::INTERRUPT") ) + else if ((in == "INTERRUPT") || (in == "RETURNCODE::INTERRUPT")) { out = etiss::RETURNCODE::INTERRUPT; } - else if ( (in == "RESET") || (in == "RETURNCODE::RESET") ) + else if ((in == "RESET") || (in == "RETURNCODE::RESET")) { out = etiss::RETURNCODE::RESET; } - else if ( (in == "ILLEGALINSTRUCTION") || (in == "RETURNCODE::ILLEGALINSTRUCTION") ) + else if ((in == "ILLEGALINSTRUCTION") || (in == "RETURNCODE::ILLEGALINSTRUCTION")) { out = etiss::RETURNCODE::ILLEGALINSTRUCTION; } - else if ( (in == "ILLEGALJUMP") || (in == "RETURNCODE::ILLEGALJUMP") ) + else if ((in == "ILLEGALJUMP") || (in == "RETURNCODE::ILLEGALJUMP")) { out = etiss::RETURNCODE::ILLEGALJUMP; } - else if ( (in == "INSTR_PAGEFAULT") || in == ("RETURNCODE::INSTR_PAGEFAULT") ) + else if ((in == "INSTR_PAGEFAULT") || in == ("RETURNCODE::INSTR_PAGEFAULT")) { out = etiss::RETURNCODE::INSTR_PAGEFAULT; } - else if ( (in == "LOAD_PAGEFAULT") || (in == "RETURNCODE::LOAD_PAGEFAULT") ) + else if ((in == "LOAD_PAGEFAULT") || (in == "RETURNCODE::LOAD_PAGEFAULT")) { out = etiss::RETURNCODE::LOAD_PAGEFAULT; } - else if ( (in == "STORE_PAGEFAULT") || (in == "RETURNCODE::STORE_PAGEFAULT") ) + else if ((in == "STORE_PAGEFAULT") || (in == "RETURNCODE::STORE_PAGEFAULT")) { out = etiss::RETURNCODE::STORE_PAGEFAULT; } - else if ( (in == "SYSCALL") || (in == "RETURNCODE::SYSCALL") ) + else if ((in == "SYSCALL") || (in == "RETURNCODE::SYSCALL")) { out = etiss::RETURNCODE::SYSCALL; } - else if ( (in == "PAGEFAULT") || (in == "RETURNCODE::PAGEFAULT") ) + else if ((in == "PAGEFAULT") || (in == "RETURNCODE::PAGEFAULT")) { out = etiss::RETURNCODE::PAGEFAULT; } - else if ( (in == "BREAKPOINT") || (in == "RETURNCODE::BREAKPOINT") ) + else if ((in == "BREAKPOINT") || (in == "RETURNCODE::BREAKPOINT")) { out = etiss::RETURNCODE::BREAKPOINT; } @@ -473,43 +472,43 @@ bool returncode_fromstring(etiss::int32& out, const std::string& in) std::string returncode_tostring(etiss::int32 in) { - switch(in) + switch (in) { - case etiss::RETURNCODE::NOERROR: + case etiss::RETURNCODE::NOERROR: return "NOERROR"; - case etiss::RETURNCODE::RELOADBLOCKS: + case etiss::RETURNCODE::RELOADBLOCKS: return "RELOADBLOCKS"; - case etiss::RETURNCODE::RELOADCURRENTBLOCK: + case etiss::RETURNCODE::RELOADCURRENTBLOCK: return "RELOADCURRENTBLOCK"; - case etiss::RETURNCODE::INTERRUPT: + case etiss::RETURNCODE::INTERRUPT: return "INTERRUPT"; - case etiss::RETURNCODE::DBUS_WRITE_ERROR: + case etiss::RETURNCODE::DBUS_WRITE_ERROR: return "DBUS_WRITE_ERROR"; - case etiss::RETURNCODE::IBUS_READ_ERROR: + case etiss::RETURNCODE::IBUS_READ_ERROR: return "IBUS_READ_ERROR"; - case etiss::RETURNCODE::IBUS_WRITE_ERROR: + case etiss::RETURNCODE::IBUS_WRITE_ERROR: return "IBUS_WRITE_ERROR"; - case etiss::RETURNCODE::RESET: + case etiss::RETURNCODE::RESET: return "RESET"; - case etiss::RETURNCODE::ILLEGALINSTRUCTION: + case etiss::RETURNCODE::ILLEGALINSTRUCTION: return "ILLEGALINSTRUCTION"; - case etiss::RETURNCODE::ILLEGALJUMP: + case etiss::RETURNCODE::ILLEGALJUMP: return "ILLEGALJUMP"; - case etiss::RETURNCODE::INSTR_PAGEFAULT: + case etiss::RETURNCODE::INSTR_PAGEFAULT: return "INSTR_PAGEFAULT"; - case etiss::RETURNCODE::LOAD_PAGEFAULT: + case etiss::RETURNCODE::LOAD_PAGEFAULT: return "LOAD_PAGEFAULT"; - case etiss::RETURNCODE::STORE_PAGEFAULT: + case etiss::RETURNCODE::STORE_PAGEFAULT: return "STORE_PAGEFAULT"; - case etiss::RETURNCODE::SYSCALL: + case etiss::RETURNCODE::SYSCALL: return "SYSCALL"; - case etiss::RETURNCODE::PAGEFAULT: + case etiss::RETURNCODE::PAGEFAULT: return "PAGEFAULT"; - case etiss::RETURNCODE::BREAKPOINT: + case etiss::RETURNCODE::BREAKPOINT: return "BREAKPOINT"; - case etiss::RETURNCODE::CPUFINISHED: + case etiss::RETURNCODE::CPUFINISHED: return "CPUFINISHED"; - default: + default: std::stringstream ss; ss << "EXCEPTION:" << std::hex << in; return ss.str(); @@ -518,27 +517,22 @@ std::string returncode_tostring(etiss::int32 in) #endif -template<> -Action::type_t::map_t Action::type_t::TABLE = { - {Action::Type::NOP, "NOP"} - , {Action::Type::BITFLIP, "BITFLIP"} - , {Action::Type::MASK, "MASK"} - , {Action::Type::COMMAND, "COMMAND"} - , {Action::Type::INJECTION, "INJECTION"} +template <> +Action::type_t::map_t Action::type_t::TABLE = { { Action::Type::NOP, "NOP" }, + { Action::Type::BITFLIP, "BITFLIP" }, + { Action::Type::MASK, "MASK" }, + { Action::Type::COMMAND, "COMMAND" }, + { Action::Type::INJECTION, "INJECTION" } #ifndef NO_ETISS - , {Action::Type::EVENT, "EVENT"} + , + { Action::Type::EVENT, "EVENT" } #endif }; -template<> -Action::mask_op_t::map_t Action::mask_op_t::TABLE = { - {Action::MaskOp::AND, "AND"} - , {Action::MaskOp::OR, "OR"} - , {Action::MaskOp::XOR, "XOR"} - , {Action::MaskOp::NAND, "NAND"} - , {Action::MaskOp::NOR, "NOR"} - , {Action::MaskOp::NOP, "NOP"} -}; +template <> +Action::mask_op_t::map_t Action::mask_op_t::TABLE = { { Action::MaskOp::AND, "AND" }, { Action::MaskOp::OR, "OR" }, + { Action::MaskOp::XOR, "XOR" }, { Action::MaskOp::NAND, "NAND" }, + { Action::MaskOp::NOR, "NOR" }, { Action::MaskOp::NOP, "NOP" } }; } // namespace fault diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 2991ccc6de..03a7fd631d 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -144,7 +144,7 @@ bool Stressor::loadXML(const std::string &file, const int coreID) bool Stressor::addFault(const Fault &f, bool injected_fault) { #if CXX0X_UP_SUPPORTED - if(!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur + if (!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur std::lock_guard lock(faults_sync()); #endif @@ -175,7 +175,7 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) // Iterate through triggers of the fault for (std::vector::const_iterator iter = f.triggers.begin(); iter != f.triggers.end(); ++iter) { - if(iter->getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP + if (iter->getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP { iptr = iter->getInjector(); @@ -184,10 +184,10 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) #ifdef NO_ETISS std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << iter->toString() << std::endl; #else - etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), - *iter); + etiss::log(etiss::INFO, + std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), *iter); #endif - //TODO: iptr->enable_faulttype for requested field + // TODO: iptr->enable_faulttype for requested field iptr->addTrigger(*iter, f.id_); } else @@ -205,25 +205,25 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) } else // Trigger is of type NOP { - etiss::log(etiss::WARNING, std::string("etiss::fault::Stressor::addFault:") + std::string(" Trigger is a NOP and is not added.")); + etiss::log(etiss::WARNING, std::string("etiss::fault::Stressor::addFault:") + + std::string(" Trigger is a NOP and is not added.")); } } - if(iptr != nullptr) + if (iptr != nullptr) { - for ( const auto& it: f.actions) + for (const auto &it : f.actions) { - if(it.is_action_on_field()) + if (it.is_action_on_field()) { bool ret_update = false; std::string errormsg; - ret_update = iptr->update_field_access_rights( it.getTargetField(), it.getType(), errormsg ); - if (! ret_update) + ret_update = iptr->update_field_access_rights(it.getTargetField(), it.getType(), errormsg); + if (!ret_update) { etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::addFault:") + errormsg); } } - } } @@ -250,13 +250,14 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector /// TODO for time relative triggers resolve time must be called! addFault(iter->getFault(), true); } - else if(iter->getType() == etiss::fault::Action::Type::NOP) + else if (iter->getType() == etiss::fault::Action::Type::NOP) { - etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); + etiss::log(etiss::VERBOSE, + std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); return true; } #ifndef NO_ETISS - else if(iter->getType() == etiss::fault::Action::Type::EVENT) + else if (iter->getType() == etiss::fault::Action::Type::EVENT) { etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EVENT")); set_event(iter->getEvent()); @@ -282,7 +283,8 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #endif } std::string err; - bool ret_applyaction = iter->getInjectorAddress().getInjector()->applyAction(find->second, *iter, err); + bool ret_applyaction = + iter->getInjectorAddress().getInjector()->applyAction(find->second, *iter, err); if (!ret_applyaction) { #ifdef NO_ETISS @@ -293,7 +295,8 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector find->second, *iter, err); #endif } - ret = ret && ret_applyaction; // mask return value with ret_applyaction foreach(!) action, return false, if one fails + ret = ret && ret_applyaction; // mask return value with ret_applyaction foreach(!) action, return + // false, if one fails } else { diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index d58d9ce4e3..72ac8a2d79 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -67,48 +67,35 @@ void Trigger::ensure(Type type) const } } -Trigger::Trigger() - : type_(Type::NOP) +Trigger::Trigger() : type_(Type::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (NOP)")); } -Trigger::Trigger(const InjectorAddress &target_injector) - : type_(Type::ASAP) - , inj_(target_injector) +Trigger::Trigger(const InjectorAddress &target_injector) : type_(Type::ASAP), inj_(target_injector) { - etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger(const InjectorAddress &target_injector) : type_ (ASAP)")); + etiss::log(etiss::VERBOSE, + std::string("etiss::fault::Trigger::Trigger(const InjectorAddress &target_injector) : type_ (ASAP)")); } Trigger::Trigger(const Trigger &sub, uint64_t count) - : type_(Type::META_COUNTER) - , sub_(new Trigger(sub)) - , param1_(count) - , param2_(0) + : type_(Type::META_COUNTER), sub_(new Trigger(sub)), param1_(count), param2_(0) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (META_COUNTER)")); } Trigger::Trigger(const InjectorAddress &target_injector, const std::string &field, uint64_t value) - : type_(Type::VARIABLEVALUE) - , field_(field) - , inj_(target_injector) - , fieldptr_(0) - , param1_(value) + : type_(Type::VARIABLEVALUE), field_(field), inj_(target_injector), fieldptr_(0), param1_(value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (VARIABLEVALUE)")); } Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool relative) - : type_(relative ? Type::TIMERELATIVE : Type::TIME) - , inj_(target_injector) - , param1_(time_ps) - , param2_(0) + : type_(relative ? Type::TIMERELATIVE : Type::TIME), inj_(target_injector), param1_(time_ps), param2_(0) { relative ? etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIMERELATIVE)")) : etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIME)")); } -Trigger::Trigger(const Trigger &cpy) - : type_(Type::NOP) +Trigger::Trigger(const Trigger &cpy) : type_(Type::NOP) { *this = cpy; } @@ -137,7 +124,8 @@ Trigger &Trigger::operator=(const Trigger &cpy) fieldptr_ = 0; param1_ = cpy.param1_; break; - case Type::TIMERELATIVE: [[fallthrough]]; + case Type::TIMERELATIVE: + [[fallthrough]]; case Type::TIME: inj_ = cpy.inj_; param1_ = cpy.param1_; @@ -153,8 +141,7 @@ Trigger &Trigger::operator=(const Trigger &cpy) } #if CXX0X_UP_SUPPORTED -Trigger::Trigger(Trigger &&cpy) - : type_(Type::NOP) +Trigger::Trigger(Trigger &&cpy) : type_(Type::NOP) { operator=(cpy); } @@ -184,94 +171,94 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) std::string(", Injector*)")); switch (type_) { - case Type::META_COUNTER: - { - if (sub_->fired(time_ps, target_injector)) - { - ++param2_; // increase count - if (param1_ == param2_) - { - return true; // count reached -> fire event - } - } - break; - } - case Type::VARIABLEVALUE: - { - if (fieldptr_ == 0) - { - std::string errmsg; - fieldptr_ = target_injector->fastFieldAccessPtr(field_, errmsg); - if (fieldptr_ == 0) - { - #ifdef NO_ETISS - std::cout << "Trigger::fired: Failed to get field" << std::endl; - #else - etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this); - #endif - return false; - } - } - uint64_t val = 0; - std::string errmsg; - if (inj_.getInjector()) - { - if (!inj_.getInjector()->readField(fieldptr_, val, errmsg)) - { - #ifdef NO_ETISS - std::cout << "Trigger::fired: Failed to read field: " << errmsg << std::endl; - #else - etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this, errmsg); - #endif - return false; - } - } - else - { - #ifdef NO_ETISS - std::cout << "Trigger::fired: Failed get injector: " << inj_.getInjectorPath() << std::endl; - #else - etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, inj_); - #endif - } - return val == param1_; - } - case Type::TIMERELATIVE: - { - etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, inj_); - resolveTime(time_ps); - [[fallthrough]]; - } - case Type::TIME: - { - /* TODO: Why doing it like this ? this would always fire after time_ps has reached */ - // Possibly because might be called not exaclty but later than excact trigger time, then late triggering should - // be done - - // if (time_ps >= param1_ && param2_ <= time_ps) - if (param1_ <= time_ps && param2_ == 0) // testing alternative param2_ says if trigger has already fired - { - /// TODO decide how to handle the case of multiple trigger checks at the same simulation time - // param2_ = time_ps+1; - param2_ = 1; // testing alternative: param2_ says if trigger has already fired - return true; - } - // param2_ = time_ps; - // testing alternative param2_ says if trigger has already fired - - // DO NOT DO IT LIKE THIS, MIGHT NOT BE CALLED EXACTLY WITH TIME OF TRIGGER - // if (time_ps == param1_) - // return true; - break; - } - case Type::ASAP: - { - return true; // as soon as possible means on next check - } - case Type::NOP: - { - return true; - } + case Type::META_COUNTER: + { + if (sub_->fired(time_ps, target_injector)) + { + ++param2_; // increase count + if (param1_ == param2_) + { + return true; // count reached -> fire event + } + } + break; + } + case Type::VARIABLEVALUE: + { + if (fieldptr_ == 0) + { + std::string errmsg; + fieldptr_ = target_injector->fastFieldAccessPtr(field_, errmsg); + if (fieldptr_ == 0) + { +#ifdef NO_ETISS + std::cout << "Trigger::fired: Failed to get field" << std::endl; +#else + etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this); +#endif + return false; + } + } + uint64_t val = 0; + std::string errmsg; + if (inj_.getInjector()) + { + if (!inj_.getInjector()->readField(fieldptr_, val, errmsg)) + { +#ifdef NO_ETISS + std::cout << "Trigger::fired: Failed to read field: " << errmsg << std::endl; +#else + etiss::log(etiss::ERROR, "Trigger::fired: Failed to get field", *this, errmsg); +#endif + return false; + } + } + else + { +#ifdef NO_ETISS + std::cout << "Trigger::fired: Failed get injector: " << inj_.getInjectorPath() << std::endl; +#else + etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, inj_); +#endif + } + return val == param1_; + } + case Type::TIMERELATIVE: + { + etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, inj_); + resolveTime(time_ps); + [[fallthrough]]; + } + case Type::TIME: + { + /* TODO: Why doing it like this ? this would always fire after time_ps has reached */ + // Possibly because might be called not exaclty but later than excact trigger time, then late triggering should + // be done + + // if (time_ps >= param1_ && param2_ <= time_ps) + if (param1_ <= time_ps && param2_ == 0) // testing alternative param2_ says if trigger has already fired + { + /// TODO decide how to handle the case of multiple trigger checks at the same simulation time + // param2_ = time_ps+1; + param2_ = 1; // testing alternative: param2_ says if trigger has already fired + return true; + } + // param2_ = time_ps; + // testing alternative param2_ says if trigger has already fired + + // DO NOT DO IT LIKE THIS, MIGHT NOT BE CALLED EXACTLY WITH TIME OF TRIGGER + // if (time_ps == param1_) + // return true; + break; + } + case Type::ASAP: + { + return true; // as soon as possible means on next check + } + case Type::NOP: + { + return true; + } } return false; @@ -377,7 +364,7 @@ const uint64_t &Trigger::getTriggerFieldValue() const ensure(Type::VARIABLEVALUE); return param1_; } -const Trigger::type_t& Trigger::getType() const +const Trigger::type_t &Trigger::getType() const { return type_; } @@ -394,11 +381,13 @@ std::string Trigger::toString() const case Type::VARIABLEVALUE: ss << " field=" << field_ << " triggerValue=" << +param1_; break; - case Type::TIMERELATIVE: [[fallthrough]]; + case Type::TIMERELATIVE: + [[fallthrough]]; case Type::TIME: ss << " triggerTime=" << +param1_; break; - default: break; + default: + break; } ss << "}"; return ss.str(); @@ -420,98 +409,98 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * } etiss::fault::Trigger::type_t type(type_str); - switch(type) + switch (type) { - case etiss::fault::Trigger::Type::META_COUNTER: + case etiss::fault::Trigger::Type::META_COUNTER: + { + uint64_t count; + if (!parse(findSingleNode(node, "count", diag), count, diag)) { - uint64_t count; - if (!parse(findSingleNode(node, "count", diag), count, diag)) - { - return false; - } - etiss::fault::Trigger *sub = 0; - if ((!parse(findSingleNode(node, "trigger", diag), sub, diag)) || (sub == 0)) - { - diag.unexpectedNode(node, "Failed to parse sub trigger"); - return false; - } - f = new etiss::fault::Trigger(*sub, count); - delete sub; - return true; + return false; } - - case etiss::fault::Trigger::Type::VARIABLEVALUE: + etiss::fault::Trigger *sub = 0; + if ((!parse(findSingleNode(node, "trigger", diag), sub, diag)) || (sub == 0)) { - uint64_t value; - if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) - { - return false; - } - std::string field; - if (!parse(findSingleNode(node, "field", diag), field, diag)) - { - return false; - } - setCoreName(field); - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) - { - return false; - } - f = new Trigger(injector, field, value); - return true; + diag.unexpectedNode(node, "Failed to parse sub trigger"); + return false; } + f = new etiss::fault::Trigger(*sub, count); + delete sub; + return true; + } - case etiss::fault::Trigger::Type::TIME: + case etiss::fault::Trigger::Type::VARIABLEVALUE: + { + uint64_t value; + if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) { - uint64_t count; - if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) - { - return false; /// TODO support other time formats/better nodes e.g. - } - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) - { - return false; - } - f = new Trigger(injector, count); - return true; + return false; + } + std::string field; + if (!parse(findSingleNode(node, "field", diag), field, diag)) + { + return false; + } + setCoreName(field); + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; } + f = new Trigger(injector, field, value); + return true; + } - case etiss::fault::Trigger::Type::TIMERELATIVE: //if (type == "TIMERELATIVE") + case etiss::fault::Trigger::Type::TIME: + { + uint64_t count; + if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) { - uint64_t count; - if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) - { - return false; /// TODO support other time formats/better nodes e.g. - } - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) - { - return false; - } - f = new Trigger(injector, count, true); - etiss::log(etiss::VERBOSE, std::string("Injector2: ") + f->getInjectorAddress().getInjectorPath()); - return true; + return false; /// TODO support other time formats/better nodes e.g. + } + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; } + f = new Trigger(injector, count); + return true; + } - case etiss::fault::Trigger::Type::ASAP: + case etiss::fault::Trigger::Type::TIMERELATIVE: // if (type == "TIMERELATIVE") + { + uint64_t count; + if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) { - etiss::fault::InjectorAddress injector; - if (!parse(findSingleNode(node, "injector", diag), injector, diag)) - { - return false; - } - f = new Trigger(injector); - return true; + return false; /// TODO support other time formats/better nodes e.g. } - default: // NOP + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) { - f = new Trigger(); - return true; + return false; } + f = new Trigger(injector, count, true); + etiss::log(etiss::VERBOSE, std::string("Injector2: ") + f->getInjectorAddress().getInjectorPath()); + return true; + } + + case etiss::fault::Trigger::Type::ASAP: + { + etiss::fault::InjectorAddress injector; + if (!parse(findSingleNode(node, "injector", diag), injector, diag)) + { + return false; + } + f = new Trigger(injector); + return true; + } + default: // NOP + { + f = new Trigger(); + return true; + } } return false; } @@ -545,49 +534,51 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg switch (f.getType()) { - case etiss::fault::Trigger::Type::META_COUNTER: - { - write_attr(node, "type", std::string(f.getType()), diag); - write(node.append_child("count"), f.getTriggerCount(), diag); - write(node.append_child("trigger"), f.getSubTrigger(), diag); - } - return true; - case etiss::fault::Trigger::Type::VARIABLEVALUE: - { - write_attr(node, "type", std::string(f.getType()), diag); - write(node.append_child("field"), f.getTriggerField(), diag); - write(node.append_child("count"), f.getTriggerFieldValue(), diag); - Injector_ptr ptr = f.getInjector(); - if (!ptr) - { - diag.errors.push_back("A fault trigger has no target_injector. failed to " - "get path of trigger"); - return false; - } - write(node.append_child("injector"), ptr->getInjectorPath(), diag); - } - return true; - case etiss::fault::Trigger::Type::TIME: [[fallthrough]]; - case etiss::fault::Trigger::Type::TIMERELATIVE: + case etiss::fault::Trigger::Type::META_COUNTER: + { + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("count"), f.getTriggerCount(), diag); + write(node.append_child("trigger"), f.getSubTrigger(), diag); + } + return true; + case etiss::fault::Trigger::Type::VARIABLEVALUE: + { + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("field"), f.getTriggerField(), diag); + write(node.append_child("count"), f.getTriggerFieldValue(), diag); + Injector_ptr ptr = f.getInjector(); + if (!ptr) { - write_attr(node, "type", std::string(f.getType()), diag); - write(node.append_child("time_ps"), f.getTriggerTime(), diag); - Injector_ptr ptr = f.getInjector(); - if (!ptr) - { - diag.errors.push_back("A fault trigger has no target_injector. failed to " - "get path of trigger"); - return false; - } - write(node.append_child("injector"), ptr->getInjectorPath(), diag); - return true; + diag.errors.push_back("A fault trigger has no target_injector. failed to " + "get path of trigger"); + return false; } - case etiss::fault::Trigger::Type::ASAP: [[fallthrough]]; - case etiss::fault::Trigger::Type::NOP: + write(node.append_child("injector"), ptr->getInjectorPath(), diag); + } + return true; + case etiss::fault::Trigger::Type::TIME: + [[fallthrough]]; + case etiss::fault::Trigger::Type::TIMERELATIVE: + { + write_attr(node, "type", std::string(f.getType()), diag); + write(node.append_child("time_ps"), f.getTriggerTime(), diag); + Injector_ptr ptr = f.getInjector(); + if (!ptr) { - write_attr(node, "type", std::string(f.getType()), diag); - return true; + diag.errors.push_back("A fault trigger has no target_injector. failed to " + "get path of trigger"); + return false; } + write(node.append_child("injector"), ptr->getInjectorPath(), diag); + return true; + } + case etiss::fault::Trigger::Type::ASAP: + [[fallthrough]]; + case etiss::fault::Trigger::Type::NOP: + { + write_attr(node, "type", std::string(f.getType()), diag); + return true; + } } diag.errors.push_back("etiss::fault::xml::write " @@ -599,16 +590,13 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg } // namespace xml #endif - -template<> -Trigger::type_t::map_t Trigger::type_t::TABLE = { - {Trigger::Type::META_COUNTER, "META_COUNTER"} - , {Trigger::Type::VARIABLEVALUE, "VARIABLEVALUE"} - , {Trigger::Type::TIME, "TIME"} - , {Trigger::Type::TIMERELATIVE, "TIMERELATIVE"} - , {Trigger::Type::ASAP, "ASAP"} - , {Trigger::Type::NOP, "NOP"} -}; +template <> +Trigger::type_t::map_t Trigger::type_t::TABLE = { { Trigger::Type::META_COUNTER, "META_COUNTER" }, + { Trigger::Type::VARIABLEVALUE, "VARIABLEVALUE" }, + { Trigger::Type::TIME, "TIME" }, + { Trigger::Type::TIMERELATIVE, "TIMERELATIVE" }, + { Trigger::Type::ASAP, "ASAP" }, + { Trigger::Type::NOP, "NOP" } }; } // namespace fault From 42fb71d6d9951fba005216a03aa0838e26bc4a30 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 12:46:45 +0100 Subject: [PATCH 18/51] force default initialization of enum variable --- include/etiss/fault/Misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etiss/fault/Misc.h b/include/etiss/fault/Misc.h index c98bf86887..8d460acb60 100644 --- a/include/etiss/fault/Misc.h +++ b/include/etiss/fault/Misc.h @@ -107,7 +107,7 @@ class SmartType : public etiss::ToString //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Constructor takes \p type_str specifying action type string encoded /// \note If string does not match, default \ref enum_t() is used. Make sure to adapt - SmartType(const std::string &type_str) + SmartType(const std::string &type_str) : type_() { auto ret = fromString(type_str, type_); if (!ret) From 86bf7b55927897e5152c3bc9ffec8d8284b3a9aa Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:40:47 +0100 Subject: [PATCH 19/51] add default initializers to member variables, replace pointers with unique_ptr, add missing copy constructors. --- include/etiss/fault/Action.h | 43 +++++++++------ include/etiss/fault/Fault.h | 19 ++++--- include/etiss/fault/Trigger.h | 16 +++--- src/fault/Action.cpp | 101 +++++++++++++++++++++++++--------- src/fault/Fault.cpp | 37 ++++++++++++- src/fault/Trigger.cpp | 79 ++++++++++++++------------ 6 files changed, 200 insertions(+), 95 deletions(-) diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 299bbaa074..92431cbda1 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -56,15 +56,12 @@ #include #ifndef NO_ETISS +#include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/Fault.h" -#include "etiss/fault/InjectorAddress.h" #include "etiss/fault/XML.h" #include "etiss/fault/Misc.h" #else #include "fault/Defs.h" -#include "fault/Fault.h" -#include "fault/InjectorAddress.h" #include "fault/XML.h" #include "fault/Misc.h" #endif @@ -75,6 +72,7 @@ namespace fault { class Fault; +class InjectorAddress; class Action : public etiss::ToString { @@ -124,7 +122,7 @@ class Action : public etiss::ToString * * @brief A etiss::RETURNCODE \p exception will be injected into the etiss simulation loop */ - Action(etiss::int32 event); + Action(int32_t event); #endif /** * @note Type: Command @@ -151,6 +149,15 @@ class Action : public etiss::ToString */ Action(const Fault &fault); + // Copy Constructors + Action(const Action &cpy); + Action &operator=(const Action &cpy); + +#if CXX0X_UP_SUPPORTED + Action(Action &&cpy); + Action &operator=(Action &&cpy); +#endif + // Getters const type_t &getType() const; @@ -170,22 +177,22 @@ class Action : public etiss::ToString uint64_t getMaskValue() const; #ifndef NO_ETISS - etiss::int32 getEvent() const; + int32_t getEvent() const; #endif // Members std::string toString() const; ///< operator<< can be used. - private: // Attributes - type_t type_; ///< type of the Attribute - InjectorAddress inj_; ///< Address of Injector - std::string command_; ///< command e.g. for booting OR1KVCPU - std::string field_; ///< concerning Field (for fault injection) - unsigned bit_; ///< concerning Bit (for fault injection) - mask_op_t mask_op_; ///< mask operation (for mask injection) - uint64_t mask_value_; ///< mask value (for mask injection) - std::vector fault_; ///< for fault injection + private: // Attributes + type_t type_; ///< type of the Attribute + std::unique_ptr inj_{ nullptr }; ///< Address of Injector + std::string command_{ "" }; ///< command e.g. for booting OR1KVCPU + std::string field_{ "" }; ///< concerning Field (for fault injection) + unsigned bit_ = { 0 }; ///< concerning Bit (for fault injection) + mask_op_t mask_op_{ MaskOp::NOP }; ///< mask operation (for mask injection) + uint64_t mask_value_{ 0 }; ///< mask value (for mask injection) + std::unique_ptr fault_{ nullptr }; ///< for fault injection #ifndef NO_ETISS - etiss::int32 event_; ///< exception, or rather etiss::RETURNCODE to + int32_t event_{ 0 }; ///< exception, or rather etiss::RETURNCODE to /// to be injected into the simulation loop #endif // private Members @@ -196,11 +203,11 @@ class Action : public etiss::ToString /** * @brief decode etiss::RETURNCODE from string */ -bool returncode_fromstring(etiss::int32 &out, const std::string &in); +bool returncode_fromstring(int32_t &out, const std::string &in); /** * @brief encode etiss::RETURNCODE to string */ -std::string returncode_tostring(etiss::int32 in); +std::string returncode_tostring(int32_t in); #endif #if ETISS_FAULT_XML diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index 049c3034e6..a29625763d 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -61,14 +61,11 @@ #include #ifndef NO_ETISS -#include "etiss/fault/Action.h" +#include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/Trigger.h" #include "etiss/fault/XML.h" #else -#include "fault/Action.h" #include "fault/Defs.h" -#include "fault/Trigger.h" #include "fault/XML.h" #endif @@ -83,19 +80,27 @@ namespace fault typedef uint64_t INT; class Action; +class Trigger; class Fault : public etiss::ToString { public: - Fault(); ///< Constructor: Generates a new Fault with unique ID - std::string toString() const; ///< operator<< can be used. void resolveTime(uint64_t time); ///< Resolves time for all its Triggers. bool isResoved() const; ///< check all Triggers if they are resolved. + Fault(); ///< Constructor: Generates a new Fault with unique ID + Fault(int nullid); + Fault(const Fault &cpy); + Fault &operator=(const Fault &cpy); +#if CXX0X_UP_SUPPORTED + Fault(Fault &&cpy); + Fault &operator=(Fault &&cpy); +#endif + public: - std::string name_; + std::string name_{ "" }; int32_t id_; ///< @attention negative ids are reserved std::vector triggers; ///< contains the triggers for this fault std::vector actions; ///< contains the actions for this fault diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index a542171814..ac806ea6b1 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -61,11 +61,9 @@ #ifndef NO_ETISS #include "etiss/Misc.h" -#include "etiss/fault/InjectorAddress.h" #include "etiss/fault/XML.h" #include "etiss/fault/Misc.h" #else -#include "fault/InjectorAddress.h" #include "fault/XML.h" #include "fault/Misc.h" #endif @@ -76,6 +74,7 @@ namespace fault { class Injector; +class InjectorAddress; #if CXX0X_UP_SUPPORTED typedef std::shared_ptr Injector_ptr; @@ -142,7 +141,6 @@ class Trigger : public etiss::ToString // Getter uint64_t getTriggerCount() const; - Trigger &getSubTrigger(); const Trigger &getSubTrigger() const; uint64_t getTriggerTime() const; const InjectorAddress &getInjectorAddress() const; @@ -170,12 +168,12 @@ class Trigger : public etiss::ToString private: // Attributes type_t type_; - std::string field_; - Trigger *sub_; - InjectorAddress inj_; - void *fieldptr_; - uint64_t param1_; - uint64_t param2_; + std::string field_{ "" }; + std::unique_ptr sub_{ nullptr }; + std::unique_ptr inj_{ nullptr }; + void *fieldptr_{ nullptr }; + uint64_t param1_{ 0 }; + uint64_t param2_{ 0 }; // Private Members void ensure(Type type) const; diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 8ed6ffa179..801ea108c5 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -42,9 +42,15 @@ #ifndef NO_ETISS #include "etiss/fault/Action.h" +#include "etiss/fault/Trigger.h" +#include "etiss/fault/Fault.h" +#include "etiss/fault/InjectorAddress.h" #include "etiss/jit/ReturnCode.h" #else #include "fault/Action.h" +#include "fault/Trigger.h" +#include "fault/Fault.h" +#include "fault/InjectorAddress.h" #endif #include @@ -71,21 +77,21 @@ Action::Action() : type_(Type::NOP) } #ifndef NO_ETISS -Action::Action(etiss::int32 event) : type_(Type::EVENT), event_(event) +Action::Action(int32_t event) : type_(Type::EVENT), event_(event) { - etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(etiss::int32 exception) called. ")); + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(int32_t exception) called. ")); } #endif Action::Action(const InjectorAddress &inj, const std::string &command) - : type_(Type::COMMAND), inj_(inj), command_(command) + : type_(Type::COMMAND), inj_(std::make_unique(inj)), command_(command) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", command=") + command + std::string(") called. ")); } Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bit) - : type_(Type::BITFLIP), inj_(inj), field_(field), bit_(bit) + : type_(Type::BITFLIP), inj_(std::make_unique(inj)), field_(field), bit_(bit) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", bit=") + std::to_string(bit) + @@ -93,7 +99,11 @@ Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bi } Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value) - : type_(Type::MASK), inj_(inj), field_(field), mask_op_(mask_op), mask_value_(mask_value) + : type_(Type::MASK) + , inj_(std::make_unique(inj)) + , field_(field) + , mask_op_(mask_op) + , mask_value_(mask_value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", mask_op=") + std::string(mask_op_) + @@ -101,13 +111,63 @@ Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask std::string(") called. ")); } -Action::Action(const Fault &fault) : type_(Type::INJECTION) +Action::Action(const Fault &fault) : type_(Type::INJECTION), fault_(std::make_unique(fault)) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(Fault &=") + fault.toString() + std::string(") called. ")); - fault_.push_back(fault); } +Action::Action(const Action &cpy) +{ + *this = cpy; +} + +Action &Action::operator=(const Action &cpy) +{ + type_ = cpy.type_; + switch (type_) + { + case Type::BITFLIP: + inj_ = std::make_unique(cpy.getInjectorAddress()); + field_ = cpy.getTargetField(); + bit_ = cpy.getTargetBit(); + break; + case Type::MASK: + inj_ = std::make_unique(cpy.getInjectorAddress()); + field_ = cpy.getTargetField(); + mask_op_ = cpy.getMaskOp(); + mask_value_ = cpy.getMaskValue(); + break; + case Type::COMMAND: + inj_ = std::make_unique(cpy.getInjectorAddress()); + command_ = cpy.getCommand(); + break; + case Type::INJECTION: + fault_ = std::make_unique(cpy.getFault()); + break; +#ifndef NO_ETISS + case Type::EVENT: + event_ = cpy.getEvent(); + break; +#endif + case Type::NOP: + break; + } + return *this; +} + +#if CXX0X_UP_SUPPORTED +Action::Action(Action &&cpy) +{ + operator=(cpy); +} +Action &Action::operator=(Action &&cpy) +{ + operator=((const Action &)cpy); + return *this; +} +#endif + const Action::type_t &Action::getType() const { return type_; @@ -120,7 +180,7 @@ const InjectorAddress &Action::getInjectorAddress() const etiss::FATALERROR, std::string( "etiss::fault::Action::TypeStruct::getInjectorAddress(): Requested Action::Type is not Injector")); - return inj_; + return *inj_; } /// COMMAND only @@ -155,10 +215,9 @@ const Fault &Action::getFault() const if (unlikely(type_ != Type::INJECTION)) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); - return fault_.front(); + return *fault_; } -// Action::MaskOp Action::getMaskOp() const const Action::mask_op_t &Action::getMaskOp() const { if (unlikely(type_ != Type::MASK)) @@ -167,14 +226,6 @@ const Action::mask_op_t &Action::getMaskOp() const return mask_op_; } -// std::string Action::getMaskOpString() const -//{ -// if(unlikely(type_ != Type::MASK)) -// etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not -// Mask")); -// return mask_op_; -//} - uint64_t Action::getMaskValue() const { if (unlikely(type_ != Type::MASK)) @@ -184,7 +235,7 @@ uint64_t Action::getMaskValue() const } #ifndef NO_ETISS -etiss::int32 Action::getEvent() const +int32_t Action::getEvent() const { if (unlikely(type_ != Type::EVENT)) etiss::log(etiss::FATALERROR, @@ -275,13 +326,13 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D } else if (type == "INJECTION") { - etiss::fault::Fault inj; - if (!parse(findSingleNode(node, "fault", diag), inj, diag)) + etiss::fault::Fault fault; + if (!parse(findSingleNode(node, "fault", diag), fault, diag)) { diag.unexpectedNode(node, "Failed to parse to inject"); return false; } - f = Action(inj); + f = Action(fault); return true; } else if (type == "MASK") @@ -324,7 +375,7 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D else if (type == "EVENT") { std::string event_str; - etiss::int32 event; + int32_t event; if (!parse(findSingleNode(node, "cause", diag), event_str, diag)) { diag.unexpectedNode(node, "Failed to parse node of event type: "); @@ -393,7 +444,7 @@ bool write(pugi::xml_node node, const etiss::fault::Action #endif #ifndef NO_ETISS -bool returncode_fromstring(etiss::int32 &out, const std::string &in) +bool returncode_fromstring(int32_t &out, const std::string &in) { if ((in == "NOERROR") || (in == "RETURNCODE::NOERROR")) { @@ -470,7 +521,7 @@ bool returncode_fromstring(etiss::int32 &out, const std::string &in) return true; } -std::string returncode_tostring(etiss::int32 in) +std::string returncode_tostring(int32_t in) { switch (in) { diff --git a/src/fault/Fault.cpp b/src/fault/Fault.cpp index 5d3b31051e..94b5533c9a 100644 --- a/src/fault/Fault.cpp +++ b/src/fault/Fault.cpp @@ -43,12 +43,16 @@ #ifndef NO_ETISS #include "etiss/fault/Fault.h" #include "etiss/fault/Trigger.h" +#include "etiss/fault/Action.h" #include "etiss/fault/Injector.h" +#include "etiss/fault/InjectorAddress.h" #include "etiss/fault/xml/pugixml.hpp" #else #include "fault/Fault.h" #include "fault/Trigger.h" +#include "fault/Action.h" #include "fault/Injector.h" +#include "fault/InjectorAddress.h" #include "fault/xml/pugixml.hpp" #endif @@ -205,11 +209,40 @@ static int32_t uniqueFaultId() static int32_t cid = -1; return cid--; } -Fault::Fault() + +Fault::Fault() : id_(uniqueFaultId()) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Fault::Fault() called. ")); - id_ = uniqueFaultId(); } + +Fault::Fault(int nullid) : id_(nullid) {} + +Fault::Fault(const Fault &cpy) +{ + *this = cpy; +} + +Fault &Fault::operator=(const Fault &cpy) +{ + name_ = cpy.name_; + id_ = cpy.id_; + triggers = cpy.triggers; + actions = cpy.actions; + return *this; +} + +#if CXX0X_UP_SUPPORTED +Fault::Fault(Fault &&cpy) +{ + operator=(cpy); +} +Fault &Fault::operator=(Fault &&cpy) +{ + operator=((const Fault &)cpy); + return *this; +} +#endif + std::string Fault::toString() const { diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index 72ac8a2d79..8c4dd49046 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -42,11 +42,15 @@ #ifndef NO_ETISS #include "etiss/fault/Trigger.h" -#include "etiss/Misc.h" #include "etiss/fault/Injector.h" +#include "etiss/fault/InjectorAddress.h" +#include "etiss/fault/Fault.h" +#include "etiss/Misc.h" #else -#include "fault/Injector.h" #include "fault/Trigger.h" +#include "fault/Injector.h" +#include "fault/InjectorAddress.h" +#include "fault/Fault.h" #endif #include @@ -72,7 +76,8 @@ Trigger::Trigger() : type_(Type::NOP) etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (NOP)")); } -Trigger::Trigger(const InjectorAddress &target_injector) : type_(Type::ASAP), inj_(target_injector) +Trigger::Trigger(const InjectorAddress &target_injector) + : type_(Type::ASAP), inj_(std::make_unique(target_injector)) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger(const InjectorAddress &target_injector) : type_ (ASAP)")); @@ -84,12 +89,19 @@ Trigger::Trigger(const Trigger &sub, uint64_t count) etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (META_COUNTER)")); } Trigger::Trigger(const InjectorAddress &target_injector, const std::string &field, uint64_t value) - : type_(Type::VARIABLEVALUE), field_(field), inj_(target_injector), fieldptr_(0), param1_(value) + : type_(Type::VARIABLEVALUE) + , field_(field) + , inj_(std::make_unique(target_injector)) + , fieldptr_(0) + , param1_(value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (VARIABLEVALUE)")); } Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool relative) - : type_(relative ? Type::TIMERELATIVE : Type::TIME), inj_(target_injector), param1_(time_ps), param2_(0) + : type_(relative ? Type::TIMERELATIVE : Type::TIME) + , inj_(std::make_unique(target_injector)) + , param1_(time_ps) + , param2_(0) { relative ? etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIMERELATIVE)")) : etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIME)")); @@ -101,38 +113,36 @@ Trigger::Trigger(const Trigger &cpy) : type_(Type::NOP) } Trigger &Trigger::operator=(const Trigger &cpy) { - if (type_ == Type::META_COUNTER) - delete sub_; if (type_ == Type::VARIABLEVALUE) { - if (inj_.isResolved() && fieldptr_) + if (inj_->isResolved() && fieldptr_) { - inj_.getInjector()->freeFastFieldAccessPtr(fieldptr_); + inj_->getInjector()->freeFastFieldAccessPtr(fieldptr_); } } type_ = cpy.type_; switch (type_) { case Type::META_COUNTER: - sub_ = new Trigger(*cpy.sub_); + sub_ = std::make_unique(cpy.getSubTrigger()); param1_ = cpy.param1_; param2_ = cpy.param2_; break; case Type::VARIABLEVALUE: field_ = cpy.field_; - inj_ = cpy.inj_; + inj_ = std::make_unique(cpy.getInjectorAddress()); fieldptr_ = 0; param1_ = cpy.param1_; break; case Type::TIMERELATIVE: [[fallthrough]]; case Type::TIME: - inj_ = cpy.inj_; + inj_ = std::make_unique(cpy.getInjectorAddress()); param1_ = cpy.param1_; param2_ = cpy.param2_; break; case Type::ASAP: - inj_ = cpy.inj_; + inj_ = std::make_unique(cpy.getInjectorAddress()); break; case Type::NOP: break; @@ -154,13 +164,11 @@ Trigger &Trigger::operator=(Trigger &&cpy) Trigger::~Trigger() { - if (type_ == Type::META_COUNTER) - delete sub_; if (type_ == Type::VARIABLEVALUE) { - if (inj_.isResolved() && fieldptr_) + if (inj_->isResolved() && fieldptr_) { - inj_.getInjector()->freeFastFieldAccessPtr(fieldptr_); + inj_->getInjector()->freeFastFieldAccessPtr(fieldptr_); } } } @@ -201,9 +209,9 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) } uint64_t val = 0; std::string errmsg; - if (inj_.getInjector()) + if (inj_->getInjector()) { - if (!inj_.getInjector()->readField(fieldptr_, val, errmsg)) + if (!inj_->getInjector()->readField(fieldptr_, val, errmsg)) { #ifdef NO_ETISS std::cout << "Trigger::fired: Failed to read field: " << errmsg << std::endl; @@ -216,16 +224,16 @@ bool Trigger::fired(uint64_t time_ps, etiss::fault::Injector *target_injector) else { #ifdef NO_ETISS - std::cout << "Trigger::fired: Failed get injector: " << inj_.getInjectorPath() << std::endl; + std::cout << "Trigger::fired: Failed get injector: " << inj_->getInjectorPath() << std::endl; #else - etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, inj_); + etiss::log(etiss::ERROR, "Trigger::fired: Failed get injector", *this, *inj_); #endif } return val == param1_; } case Type::TIMERELATIVE: { - etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, inj_); + etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, *inj_); resolveTime(time_ps); [[fallthrough]]; } @@ -275,14 +283,14 @@ void Trigger::resolveTime(uint64_t time) } else if (type_ == Type::META_COUNTER) { - return getSubTrigger().resolveTime(time); + return sub_->resolveTime(time); } } bool Trigger::isResolved() const { if (type_ == Type::META_COUNTER) { - return getSubTrigger().isResolved(); + return sub_->isResolved(); } return type_ != Type::TIMERELATIVE; } @@ -292,21 +300,18 @@ uint64_t Trigger::getTriggerCount() const ensure(Type::META_COUNTER); return param1_; } -Trigger &Trigger::getSubTrigger() -{ - ensure(Type::META_COUNTER); - return *sub_; -} + const Trigger &Trigger::getSubTrigger() const { ensure(Type::META_COUNTER); return *sub_; } + uint64_t Trigger::getTriggerTime() const { if (type_ == Type::META_COUNTER) { - return getSubTrigger().getTriggerTime(); + return sub_->getTriggerTime(); } try { @@ -318,10 +323,12 @@ uint64_t Trigger::getTriggerTime() const } return param1_; } + const InjectorAddress &Trigger::getInjectorAddress() const { - return inj_; + return *inj_; } + const Injector_ptr &Trigger::getInjector() const { if (type_ == Type::META_COUNTER) @@ -330,9 +337,10 @@ const Injector_ptr &Trigger::getInjector() const } else { - return inj_.getInjector(); + return inj_->getInjector(); } } + bool Trigger::isNOP() const { if (type_ == Type::META_COUNTER) @@ -344,26 +352,29 @@ bool Trigger::isNOP() const return type_ == Type::NOP; } } + const std::string &Trigger::getTriggerField() const { // std::cout << "Trigger::getTriggerField() called" << std::endl; if (type_ == Type::META_COUNTER) { - return getSubTrigger().getTriggerField(); + return sub_->getTriggerField(); } ensure(Type::VARIABLEVALUE); return field_; } + const uint64_t &Trigger::getTriggerFieldValue() const { // std::cout << "Trigger::getTriggerFieldValue() called" << std::endl; if (type_ == Type::META_COUNTER) { - return getSubTrigger().getTriggerFieldValue(); + return sub_->getTriggerFieldValue(); } ensure(Type::VARIABLEVALUE); return param1_; } + const Trigger::type_t &Trigger::getType() const { return type_; From bf6bfe145f7076f310a67aa64fd22be7cbb507e5 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:41:16 +0100 Subject: [PATCH 20/51] replace inclusions with forward declarations in headers to avoid circulars. --- include/etiss/VirtualStruct.h | 7 +++++-- include/etiss/fault/Injector.h | 17 ++++++++++++----- include/etiss/fault/Stressor.h | 7 +++++-- src/VirtualStruct.cpp | 9 ++++----- src/fault/Injector.cpp | 6 ++++++ src/fault/InjectorAddress.cpp | 6 +++++- src/fault/Stressor.cpp | 14 +++++++++++--- 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index 11809dd4a1..3907489fd8 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -55,6 +55,10 @@ #include "etiss/Misc.h" #include "etiss/fault/Injector.h" +#include "etiss/fault/Trigger.h" +#include "etiss/fault/Action.h" +#include "etiss/fault/Fault.h" + #include #include @@ -370,8 +374,7 @@ class VirtualStruct : public std::enable_shared_from_this, public virtual bool readField(void *fastfieldaccessptr, uint64_t &val, std::string &errormsg); virtual bool applyAction(const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg); - virtual bool update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, - std::string &errormsg); + virtual bool update_field_access_rights(const etiss::fault::Action &action, std::string &errormsg); public: /// set this function to handle custom commands passed by etiss::fault::Action of the type diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index 3d8127d300..b0d219015a 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -53,9 +53,14 @@ #define ETISS_INJECTOR_H_ #ifndef NO_ETISS -#include "etiss/fault/Fault.h" +#include "etiss/Misc.h" +#include "etiss/fault/Defs.h" +#include "etiss/fault/XML.h" +#include "etiss/fault/Misc.h" #else -#include "fault/Fault.h" +#include "fault/Defs.h" +#include "fault/XML.h" +#include "fault/Misc.h" #endif #if CXX0X_UP_SUPPORTED @@ -73,6 +78,9 @@ namespace fault { class Stressor; +class Fault; +class Trigger; +class Action; class Injector { @@ -156,11 +164,10 @@ class Injector virtual bool acceleratedTrigger(const etiss::fault::Trigger &, int32_t fault_id); /** - @brief Update the \p field of injector with access rights to allow \p type actions. + @brief Update the \p field of injector with access rights to allow \p action (used to get type of action). @detail For example, if \p action is of etiss::fault::Action::BITFLIP, \p field requires F flag set */ - virtual bool update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, - std::string &errormsg) = 0; + virtual bool update_field_access_rights(const etiss::fault::Action &action, std::string &errormsg) = 0; public: // static /** diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index ece6ddf15f..eb042290c0 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -54,10 +54,8 @@ #define ETISS_STRESSOR_H_ #ifndef NO_ETISS -#include "etiss/fault/Fault.h" #include "etiss/jit/ReturnCode.h" #else -#include "fault/Fault.h" #endif namespace etiss @@ -66,6 +64,11 @@ namespace etiss namespace fault { +class Fault; +class Trigger; +class Action; +class Injector; + class Stressor { public: diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index aa17d67735..861155346b 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -551,14 +551,13 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } } -bool VirtualStruct::update_field_access_rights(const std::string &field, etiss::fault::Action::Type type, - std::string &errormsg) +bool VirtualStruct::update_field_access_rights(const etiss::fault::Action &action, std::string &errormsg) { Field *f = nullptr; - auto find = fieldNames_.find(field); + auto find = fieldNames_.find(action.getTargetField()); if (find == fieldNames_.end()) { - find = fieldPrettyNames_.find(field); + find = fieldPrettyNames_.find(action.getTargetField()); if (find == fieldPrettyNames_.end()) { f = 0; @@ -575,7 +574,7 @@ bool VirtualStruct::update_field_access_rights(const std::string &field, etiss:: if (f) { - switch (type) + switch (action.getType()) { case etiss::fault::Action::Type::MASK: [[fallthrough]]; diff --git a/src/fault/Injector.cpp b/src/fault/Injector.cpp index fce50976fd..c99fb7efe1 100644 --- a/src/fault/Injector.cpp +++ b/src/fault/Injector.cpp @@ -44,9 +44,15 @@ #include "etiss/fault/Injector.h" #include "etiss/Misc.h" #include "etiss/fault/Stressor.h" +#include "etiss/fault/Trigger.h" +#include "etiss/fault/Action.h" +#include "etiss/fault/Fault.h" #else #include "fault/Injector.h" #include "fault/Stressor.h" +#include "fault/Trigger.h" +#include "fault/Action.h" +#include "fault/Fault.h" #endif #include diff --git a/src/fault/InjectorAddress.cpp b/src/fault/InjectorAddress.cpp index aa10aeeb84..1c65652448 100644 --- a/src/fault/InjectorAddress.cpp +++ b/src/fault/InjectorAddress.cpp @@ -43,9 +43,13 @@ #ifndef NO_ETISS #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/Injector.h" +#include "etiss/fault/Trigger.h" +#include "etiss/fault/Action.h" #else -#include "fault/Injector.h" #include "fault/InjectorAddress.h" +#include "fault/Injector.h" +#include "fault/Trigger.h" +#include "fault/Action.h" #endif namespace etiss diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 03a7fd631d..f8fb41ecd0 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -42,11 +42,19 @@ #ifndef NO_ETISS #include "etiss/fault/Stressor.h" -#include "etiss/Misc.h" #include "etiss/fault/Injector.h" +#include "etiss/fault/InjectorAddress.h" +#include "etiss/fault/Trigger.h" +#include "etiss/fault/Action.h" +#include "etiss/fault/Fault.h" +#include "etiss/Misc.h" #else -#include "fault/Injector.h" #include "fault/Stressor.h" +#include "fault/Injector.h" +#include "fault/InjectorAddress.h" +#include "fault/Trigger.h" +#include "fault/Action.h" +#include "fault/Fault.h" #endif #include @@ -218,7 +226,7 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) { bool ret_update = false; std::string errormsg; - ret_update = iptr->update_field_access_rights(it.getTargetField(), it.getType(), errormsg); + ret_update = iptr->update_field_access_rights(it, errormsg); if (!ret_update) { etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::addFault:") + errormsg); From 9dfc92d3767b7bfacf976d44d3afd8fd6c407d8c Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:45:53 +0100 Subject: [PATCH 21/51] use const iterator in faults config --- src/ETISS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ETISS.cpp b/src/ETISS.cpp index e366a7b24d..4c22d3f68d 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -910,7 +910,7 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) if (!faults.empty()) { std::list ffs = etiss::split(faults, ';'); - for (auto ff : ffs) + for (const auto& ff : ffs) { auto stressor_successful = etiss::fault::Stressor::loadXML(ff, cpu_core->getID()); if (!stressor_successful) @@ -1034,4 +1034,4 @@ std::string etiss::errorMessage(etiss::int32 code, CPUArch *arch) return "Unknown CPU architecture dependent error code."; } } -} \ No newline at end of file +} From 6564349169f200333b16b6b2e7c64fae0c3a98a1 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:48:40 +0100 Subject: [PATCH 22/51] avoid new operator for shared_ptr --- src/ETISS.cpp | 2 +- src/bare_etiss_processor/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ETISS.cpp b/src/ETISS.cpp index 4c22d3f68d..71a8e07254 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -924,7 +924,7 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) } etiss::log(etiss::VERBOSE, std::string("Add InstructionAccurateCallback Plugin to ") + cpu_core->getName() + std::string(". Required for etiss::fault::Injector.")); - cpu_core->addPlugin(std::shared_ptr(new etiss::plugin::InstructionAccurateCallback())); + cpu_core->addPlugin(std::make_shared()); } } } diff --git a/src/bare_etiss_processor/main.cpp b/src/bare_etiss_processor/main.cpp index d9d1b0353a..9e93fd5463 100644 --- a/src/bare_etiss_processor/main.cpp +++ b/src/bare_etiss_processor/main.cpp @@ -122,7 +122,7 @@ int main(int argc, const char *argv[]) // here own developped plug-ins can be added with: if (etiss::cfg().get("etiss.log_pc", false)) { etiss::cfg().set("etiss.max_block_size", 1); - cpu->addPlugin(std::shared_ptr(new TracePrinter(0x88888))); + cpu->addPlugin(std::make_shared(0x88888)); } std::cout << "=== Setting up plug-ins ===" << std::endl << std::endl; From dc1acb30562809ca37744cf6263e796e4baf9137 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:53:38 +0100 Subject: [PATCH 23/51] clean-up repetitive assignment --- src/VirtualStruct.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index 861155346b..18241c312e 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -557,7 +557,6 @@ bool VirtualStruct::update_field_access_rights(const etiss::fault::Action &actio auto find = fieldNames_.find(action.getTargetField()); if (find == fieldNames_.end()) { - find = fieldPrettyNames_.find(action.getTargetField()); if (find == fieldPrettyNames_.end()) { f = 0; From cc1f2c2dd1a750aaf1e09541f5e6e9b080a56f99 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:55:36 +0100 Subject: [PATCH 24/51] remove builtin_expect macros for performance uncritical branching --- src/fault/Action.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 801ea108c5..82ba177ff9 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -175,7 +175,7 @@ const Action::type_t &Action::getType() const const InjectorAddress &Action::getInjectorAddress() const { - if (unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND))) + if (!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND)) etiss::log( etiss::FATALERROR, std::string( @@ -186,7 +186,7 @@ const InjectorAddress &Action::getInjectorAddress() const /// COMMAND only const std::string &Action::getCommand() const { - if (unlikely(type_ != Type::COMMAND)) + if (type_ != Type::COMMAND) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getCommand(): Requested Action::Type is not Command")); return command_; @@ -195,7 +195,7 @@ const std::string &Action::getCommand() const /// is_action_on_field only const std::string &Action::getTargetField() const { - if (unlikely(!(type_ == Type::BITFLIP || type_ == Type::MASK))) + if (!(type_ == Type::BITFLIP || type_ == Type::MASK)) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetField(): Requested Action::Type is not TargetField")); return field_; @@ -204,7 +204,7 @@ const std::string &Action::getTargetField() const /// BITFLIP only unsigned Action::getTargetBit() const { - if (unlikely(type_ != Type::BITFLIP)) + if (type_ != Type::BITFLIP) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetBit(): Requested Action::Type is not TargetBit")); return bit_; @@ -212,7 +212,7 @@ unsigned Action::getTargetBit() const const Fault &Action::getFault() const { - if (unlikely(type_ != Type::INJECTION)) + if (type_ != Type::INJECTION) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); return *fault_; @@ -220,7 +220,7 @@ const Fault &Action::getFault() const const Action::mask_op_t &Action::getMaskOp() const { - if (unlikely(type_ != Type::MASK)) + if (type_ != Type::MASK) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); return mask_op_; @@ -228,7 +228,7 @@ const Action::mask_op_t &Action::getMaskOp() const uint64_t Action::getMaskValue() const { - if (unlikely(type_ != Type::MASK)) + if (type_ != Type::MASK) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskValue(): Requested Action::Type is not Mask")); return mask_value_; @@ -237,7 +237,7 @@ uint64_t Action::getMaskValue() const #ifndef NO_ETISS int32_t Action::getEvent() const { - if (unlikely(type_ != Type::EVENT)) + if (type_ != Type::EVENT) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getEvent(): Requested Action::Type is not Event")); return event_; From 0053e030ab3648080c9bb28fef1e8d0b4ad8d7c4 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 2 Dec 2021 19:19:07 +0100 Subject: [PATCH 25/51] static fault list now only holds fault definitions, a fault must be explicitly added through a fault's INJECTION action or through the node in given XML, add EJECTION action that deactivates all triggers of the referenced fault, disable automatic removal of a trigger once it was fired to allow permanent faults - remove triggers (therefore the fault) through EJECTION action of a referenced fault, extend faults.xml example and readme section w.r.t. new features. --- examples/bare_etiss_processor/faults.xml | 99 ++++++--- include/etiss/fault/Action.h | 16 +- include/etiss/fault/Fault.h | 30 ++- include/etiss/fault/Injector.h | 10 +- include/etiss/fault/Stressor.h | 26 ++- src/bare_etiss_processor/README.md | 111 ++++++---- src/fault/Action.cpp | 35 +-- src/fault/Fault.cpp | 153 +++++++++++-- src/fault/Injector.cpp | 51 +++-- src/fault/Stressor.cpp | 262 +++++++++++++++++++---- 10 files changed, 622 insertions(+), 171 deletions(-) diff --git a/examples/bare_etiss_processor/faults.xml b/examples/bare_etiss_processor/faults.xml index e63c2a0398..fded0a23d1 100644 --- a/examples/bare_etiss_processor/faults.xml +++ b/examples/bare_etiss_processor/faults.xml @@ -1,37 +1,66 @@ - - - - - 1 - + + + + + + 1 + + core%i% + instructionPointer + b0 + + + + + + core%i% + some string + + + core%i% + R1 + 1 + + + + + + + + + + + + + 10 + + core%i% + + + + + + + + + + + + core%i% - instructionPointer - b0 - - - - - - core%i% - R1 - 1 - - - - - - - core%i% - instructionPointer - b8 - - - - - - - - - - + + + + + core%i% + R2 + OR + 0x55555555 + + + + + + + diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 92431cbda1..378c223a54 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -71,7 +71,7 @@ namespace etiss namespace fault { -class Fault; +class FaultRef; class InjectorAddress; class Action : public etiss::ToString @@ -89,10 +89,12 @@ class Action : public etiss::ToString /// that information must be passed within the command string , INJECTION /// an action that injects a fault definition (trigger + actions) + , + EJECTION /// an action that ejects a referenced fault (must exist) #ifndef NO_ETISS - /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE exception , - EVENT + EVENT /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE + /// exception #endif }; typedef SmartType type_t; @@ -147,7 +149,7 @@ class Action : public etiss::ToString * * @brief injects a fault. this is especially usefull with Triggers of type TIMERELATIVE */ - Action(const Fault &fault); + Action(const FaultRef &fault_ref, type_t type); // Copy Constructors Action(const Action &cpy); @@ -170,8 +172,8 @@ class Action : public etiss::ToString const std::string &getTargetField() const; unsigned getTargetBit() const; - /// INJECTION only - const Fault &getFault() const; + /// INJECTION and EJECTION only + const FaultRef &getFaultRef() const; const mask_op_t &getMaskOp() const; @@ -190,7 +192,7 @@ class Action : public etiss::ToString unsigned bit_ = { 0 }; ///< concerning Bit (for fault injection) mask_op_t mask_op_{ MaskOp::NOP }; ///< mask operation (for mask injection) uint64_t mask_value_{ 0 }; ///< mask value (for mask injection) - std::unique_ptr fault_{ nullptr }; ///< for fault injection + std::unique_ptr fault_ref_{ nullptr }; ///< for fault injection #ifndef NO_ETISS int32_t event_{ 0 }; ///< exception, or rather etiss::RETURNCODE to /// to be injected into the simulation loop diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index a29625763d..c3e8d73dca 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -106,9 +106,33 @@ class Fault : public etiss::ToString std::vector actions; ///< contains the actions for this fault }; +class FaultRef : public etiss::ToString +{ + private: + std::unique_ptr fault_{ nullptr }; ///< referenced Fault + std::string name_{ "" }; ///< string identifier, used to resolve actual reference via fault_ + + public: + std::string toString() const; ///< operator<< can be used. + + FaultRef(); + FaultRef(const FaultRef &cpy); + FaultRef &operator=(const FaultRef &cpy); +#if CXX0X_UP_SUPPORTED + FaultRef(FaultRef &&cpy); + FaultRef &operator=(FaultRef &&cpy); +#endif + bool is_set() const { return (fault_->name_ == name_); } + bool set_fault_reference(const std::string &identifier); + bool resolve_reference() const; + const Fault &get_fault() const { return *fault_; } + const std::string &get_name() const { return name_; } +}; + #if ETISS_FAULT_XML -bool parseXML(std::vector &vec, std::istream &input, std::ostream &diagnostics_out = std::cout); +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); bool writeXML(const std::vector &vec, std::ostream &out, std::ostream &diagnostics_out = std::cout); @@ -119,6 +143,10 @@ template <> bool parse(pugi::xml_node node, etiss::fault::Fault &f, Diagnostics &diag); template <> bool write(pugi::xml_node node, const etiss::fault::Fault &f, Diagnostics &diag); +template <> +bool parse(pugi::xml_node node, etiss::fault::FaultRef &fref, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::FaultRef &fref, Diagnostics &diag); } // namespace xml diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index b0d219015a..f6718973cf 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -190,12 +190,16 @@ class Injector std::mutex sync; #endif volatile bool has_pending_triggers; - std::list> pending_triggers; ///> Triggers which were just added - std::list> unknown_triggers; ///> Triggers to look at in callbacks + volatile bool has_remove_triggers; + std::list> pending_triggers; ///< Triggers which were just added + std::list> unknown_triggers; ///< Triggers to look at in callbacks + std::list> + remove_triggers; ///< Triggers to synchronously remove on next callback (prio over pending) /// TODO specialized lists. e.g. time triggers should be sorted and only the earliest time should be checked - public: // interface fot stressor + public: // interface for Stressor void addTrigger(const Trigger &t, int32_t fault_id); + void removeTrigger(const Trigger &t, int32_t fault_id); }; } // namespace fault diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index eb042290c0..3abc8afe6b 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -55,9 +55,13 @@ #ifndef NO_ETISS #include "etiss/jit/ReturnCode.h" +#include "etiss/fault/XML.h" #else +#include "fault/XML.h" #endif +#include + namespace etiss { @@ -109,10 +113,23 @@ class Stressor /** @brief adds a fault to a static map that can be accessed * by static std::map & faults(). * @param f the fault for adding to the map. - * @return false if fault already exists in map. + * @return false if fault already exists in map faults(). + */ + static bool addFaultDefinition(const Fault &f); + + /** @brief activates a fault's triggers in their injectors + * @param f the fault to activate + * @return false if refernced fault is not the static fault list faults(). */ static bool addFault(const Fault &f, bool injected_fault = false); + /** @brief removes a fault's active triggers from their injectors, thus, + * deactivating the fault. + * @param f the fault for adding to the map. + * @return false if refernced fault is not the static fault list faults(). + */ + static bool removeFault(const Fault &f, bool injected_fault = false); + /** @brief Checks if the given trigger is valid and calls applyAction. * * Is called by a VirtualStruct e.g. OR1KVCPU::vcoreState. @@ -129,8 +146,15 @@ class Stressor /** @brief clears the fault map. */ static void clear(); + /** @brief static map with all referencable faults. + */ + static std::map &faults(); }; +/** @brief parse a XML document held in \p input stream and return as \p doc + */ +bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out = std::cout); + } // namespace fault } // namespace etiss diff --git a/src/bare_etiss_processor/README.md b/src/bare_etiss_processor/README.md index ca16eb6680..ccc9afc2d4 100644 --- a/src/bare_etiss_processor/README.md +++ b/src/bare_etiss_processor/README.md @@ -189,47 +189,74 @@ Usage: - ./run_helper.sh ELFFILE --faults.xml=/path/to/faults.xml -`faults.xml` Example: - -- `[]` are optional attributes -- `#` is a comment for this example - -``` - # a faults.xml root - # a has and - - # a must have a type of {META_COUNTER, VARIABLEVALUE, TIME, TIMERELATIVE, NOP} - 1 # the META_COUNTER here will trigger once its subtrigger fired times - # a can have a sub , awesome! - core%i% # the trigger type VARIABLEVALUE needs an that supplies the triggerable object, bare_etiss_processor constructs "core0" ETISS-FI will replace %i% to the core id "0" by default - instructionPointer # the of "core%i%" should be listened for, here, the instruction pointer - b0 # the that 's value will be compared to as a hexadecimal - - - - - # an must have a type of {BITFLIP, COMMAND, NOP, INJECTION}, here, Command - core%i% # an can have a different than its s, which means, you could trigger on one VirtualStruct and inject into another - test # the COMMAND type is a custom string encoded action the has to implement, by default no custom actions are supported. Set VirtualStructs::applyCustomAction member to a std::function of your choice and handle the passed string - - # why not have more than one , an additional "BITFLIP" - core%i% # again we need the - R1 # BITFLIP takes a , here R1 aka X1 in RISC-V, with the CPUCore all ISA-GPRs and the instructionPointer are - 1 # the target number - - # now, we inject a new fault as our action. - - - - - - - - - - - - - +`faults.xml` Example and explanations: + +```xml + + + + + + 1 + + core%i% + instructionPointer + b0 + + + + + + core%i% + some string + + + core%i% + R1 + 1 + + + + + + + + + + + + + 10 + + core%i% + + + + + + + + + + + + + core%i% + + + + + core%i% + R2 + OR + 0x55555555 + + + + + + + + ``` diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 82ba177ff9..4149ecf425 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -111,10 +111,10 @@ Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask std::string(") called. ")); } -Action::Action(const Fault &fault) : type_(Type::INJECTION), fault_(std::make_unique(fault)) +Action::Action(const FaultRef &fault_ref, type_t type) : type_(type), fault_ref_(std::make_unique(fault_ref)) { - etiss::log(etiss::VERBOSE, - std::string("etiss::fault::Action::Action(Fault &=") + fault.toString() + std::string(") called. ")); + etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(FaultRef &=") + fault_ref.toString() + + std::string(") called. ")); } Action::Action(const Action &cpy) @@ -142,8 +142,10 @@ Action &Action::operator=(const Action &cpy) inj_ = std::make_unique(cpy.getInjectorAddress()); command_ = cpy.getCommand(); break; + case Type::EJECTION: + [[fallthrough]]; case Type::INJECTION: - fault_ = std::make_unique(cpy.getFault()); + fault_ref_ = std::make_unique(cpy.getFaultRef()); break; #ifndef NO_ETISS case Type::EVENT: @@ -210,12 +212,12 @@ unsigned Action::getTargetBit() const return bit_; } -const Fault &Action::getFault() const +const FaultRef &Action::getFaultRef() const { - if (type_ != Type::INJECTION) + if (!((type_ == Type::INJECTION) || (type_ == Type::EJECTION))) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); - return *fault_; + return *fault_ref_; } const Action::mask_op_t &Action::getMaskOp() const @@ -324,15 +326,15 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D f = Action(inj, command); return true; } - else if (type == "INJECTION") + else if ((type == "INJECTION") || (type == "EJECTION")) { - etiss::fault::Fault fault; - if (!parse(findSingleNode(node, "fault", diag), fault, diag)) + etiss::fault::FaultRef fault_ref; + if (!parse(findSingleNode(node, "fault_ref", diag), fault_ref, diag)) { - diag.unexpectedNode(node, "Failed to parse to inject"); + diag.unexpectedNode(node, "Failed to parse to inject"); return false; } - f = Action(fault); + f = Action(fault_ref, type); return true; } else if (type == "MASK") @@ -421,7 +423,11 @@ bool write(pugi::xml_node node, const etiss::fault::Action break; case etiss::fault::Action::Type::INJECTION: ok = ok & write_attr(node, "type", "INJECTION", diag); - ok = ok & write(node.append_child("fault"), f.getFault(), diag); + ok = ok & write(node.append_child("fault_ref"), f.getFaultRef(), diag); + break; + case etiss::fault::Action::Type::EJECTION: + ok = ok & write_attr(node, "type", "EJECTION", diag); + ok = ok & write(node.append_child("fault_ref"), f.getFaultRef(), diag); break; case etiss::fault::Action::Type::MASK: ok = ok & write_attr(node, "type", "MASK", diag); @@ -573,7 +579,8 @@ Action::type_t::map_t Action::type_t::TABLE = { { Action::Type::NOP, "NOP" }, { Action::Type::BITFLIP, "BITFLIP" }, { Action::Type::MASK, "MASK" }, { Action::Type::COMMAND, "COMMAND" }, - { Action::Type::INJECTION, "INJECTION" } + { Action::Type::INJECTION, "INJECTION" }, + { Action::Type::EJECTION, "EJECTION" } #ifndef NO_ETISS , { Action::Type::EVENT, "EVENT" } diff --git a/src/fault/Fault.cpp b/src/fault/Fault.cpp index 94b5533c9a..a5596845bf 100644 --- a/src/fault/Fault.cpp +++ b/src/fault/Fault.cpp @@ -46,6 +46,7 @@ #include "etiss/fault/Action.h" #include "etiss/fault/Injector.h" #include "etiss/fault/InjectorAddress.h" +#include "etiss/fault/Stressor.h" #include "etiss/fault/xml/pugixml.hpp" #else #include "fault/Fault.h" @@ -53,6 +54,7 @@ #include "fault/Action.h" #include "fault/Injector.h" #include "fault/InjectorAddress.h" +#include "fault/Stressor.h" #include "fault/xml/pugixml.hpp" #endif @@ -197,6 +199,51 @@ bool write>(pugi::xml_node node, const std::vec return ret; } +template <> +bool parse>(pugi::xml_node node, std::vector &dst, + Diagnostics &diag) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::xml::parse >(node, vector, Diagnostics) called. ")); + + bool ret = true; + for (pugi::xml_node cnode = node.first_child(); cnode; cnode = cnode.next_sibling()) + { + if (hasName(cnode, "fault_ref")) + { // handle fault node + FaultRef f; + if (parse(cnode, f, diag)) + { + dst.push_back(f); + } + else + { + ret = false; + } + } + else + { + diag.ignoredNode(cnode, "non \"fault_ref\" node in list."); + } + } + return ret; +} + +template <> +bool write>(pugi::xml_node node, const std::vector &src, + Diagnostics &diag) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::xml::write >(node, vector, Diagnostics) called. ")); + + bool ret = true; + for (size_t i = 0; i < src.size(); ++i) + { + ret = ret && write(node.append_child("fault_ref"), src[i], diag); + } + return ret; +} + } // namespace xml // generates a unique ID for a new Fault @@ -279,26 +326,83 @@ bool Fault::isResoved() const return true; } -#if ETISS_FAULT_XML +FaultRef::FaultRef() : fault_(std::make_unique()) {} -bool parseXML(std::vector &vec, std::istream &input, std::ostream &diagnostics_out) +FaultRef::FaultRef(const FaultRef &cpy) : FaultRef() { + *this = cpy; +} - pugi::xml_document doc; // xml document +FaultRef &FaultRef::operator=(const FaultRef &cpy) +{ + name_ = cpy.get_name(); + *fault_ = cpy.get_fault(); - pugi::xml_parse_result pr = doc.load(input); // load from stream + return *this; +} - if (!pr) - { // load failure - diagnostics_out << "failed to load xml from stream: " << pr.description() << std::endl; - return false; - } +#if CXX0X_UP_SUPPORTED +FaultRef::FaultRef(FaultRef &&cpy) +{ + operator=(cpy); +} +FaultRef &FaultRef::operator=(FaultRef &&cpy) +{ + operator=((const FaultRef &)cpy); + return *this; +} +#endif + +std::string FaultRef::toString() const +{ + + pugi::xml_document doc; + doc.load(""); etiss::fault::xml::Diagnostics diag; - bool ret = parse(doc.document_element(), vec, diag); // parse document + etiss::fault::xml::write(doc.append_child("fault_ref"), *this, diag); - diag.print(diagnostics_out); + std::stringstream ss; + + doc.save(ss); + + return ss.str(); +} + +bool FaultRef::set_fault_reference(const std::string &identifier) +{ + name_ = identifier; + + return (resolve_reference()); +} + +bool FaultRef::resolve_reference() const +{ + for (auto const &it : Stressor::faults()) + { + if (it.second.name_ == name_) + { + *fault_ = it.second; + return true; + } + } + + return false; +} + +#if ETISS_FAULT_XML + +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag) +{ + bool ret = parse(findSingleNode(doc.document_element(), "definitions", diag), vec, diag); // parse document + + return ret; +} + +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag) +{ + bool ret = parse(findSingleNode(doc.document_element(), "initial", diag), vec, diag); // parse document return ret; } @@ -329,8 +433,8 @@ bool parse(pugi::xml_node node, etiss::fault::Fault &f, Dia etiss::log(etiss::VERBOSE, std::string("etiss::fault::xml::parse") + std::string("(node, Fault, Diagnostics) called. ")); bool ret = true; - /*ret = ret &*/ getAttribute(node, "name", f.name_, diag); // optional - /*ret = ret &*/ getAttribute(node, "id_", f.id_, diag); // optional + ret = ret && getAttribute(node, "name", f.name_, diag); // non-optional + /*ret = ret &*/ getAttribute(node, "id_", f.id_, diag); // optional for (pugi::xml_node cnode = node.first_child(); cnode; cnode = cnode.next_sibling()) { if (hasName(cnode, "triggers")) @@ -406,6 +510,29 @@ bool write(pugi::xml_node node, const etiss::fault::Fault & return ok; } +template <> +bool parse(pugi::xml_node node, etiss::fault::FaultRef &fref, Diagnostics &diag) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::xml::parse") + + std::string("(node, Fault, Diagnostics) called. ")); + bool ret = true; + std::string name; + ret = ret && getAttribute(node, "name", name, diag); // non-optional + // try to resolve reference, could (and is allowed to) fail in case of injected/ejected fault_ref + fref.set_fault_reference(name); + return ret; +} + +template <> +bool write(pugi::xml_node node, const etiss::fault::FaultRef &fref, Diagnostics &diag) +{ + etiss::log(etiss::VERBOSE, std::string("etiss::fault::xml::write") + + std::string("(node, Fault, Diagnostics) called. ")); + bool ok = true; + ok = ok && setAttribute(node, "name", fref.get_name(), diag); + return ok; +} + } // namespace xml #endif diff --git a/src/fault/Injector.cpp b/src/fault/Injector.cpp index c99fb7efe1..656503c11e 100644 --- a/src/fault/Injector.cpp +++ b/src/fault/Injector.cpp @@ -90,7 +90,7 @@ bool Injector::cycleAccurateCallback(uint64_t time_ps) std::to_string(time_ps) + ")"); #endif // copy pending triggers in a threadsafe manner to unknown triggers - if (has_pending_triggers) + if (unlikely(has_pending_triggers)) { #if CXX0X_UP_SUPPORTED std::lock_guard lock(sync); @@ -98,34 +98,44 @@ bool Injector::cycleAccurateCallback(uint64_t time_ps) unknown_triggers.insert(unknown_triggers.end(), pending_triggers.begin(), pending_triggers.end()); pending_triggers.clear(); } - // check triggers - if (!unknown_triggers.empty()) + if (unlikely(has_remove_triggers)) { +#if CXX0X_UP_SUPPORTED + std::lock_guard lock(sync); +#endif for (std::list>::iterator iter = unknown_triggers.begin(); iter != unknown_triggers.end();) - { - if (iter->first.fired(time_ps, this)) - { // trigger fired - // signal fired trigger - ret = true; - if (Stressor::firedTrigger(iter->first, iter->second, this, time_ps)) - { - // remove fired trigger + for (const auto &rm : remove_triggers) + { + if (iter->second == rm.second) // remove all triggers associated with the fault ids of rm unknown_triggers.erase(iter++); - } else { ++iter; } } - else - { - ++iter; + remove_triggers.clear(); + has_remove_triggers = false; + } + // check triggers + if (!unknown_triggers.empty()) + { + for (auto &it : unknown_triggers) + { + if (it.first.fired(time_ps, this)) + { // trigger fired + // signal fired trigger + ret = true; + if (Stressor::firedTrigger(it.first, it.second, this, time_ps)) + { + // explicitly remove triggers through Stressor::removeFault via Action::Type::EJECTION + } } } } return ret; } + bool Injector::instructionAccurateCallback(uint64_t time_ps) { #if ETISS_DEBUG @@ -228,6 +238,17 @@ void Injector::addTrigger(const Trigger &t, int32_t fault_id) } } +void Injector::removeTrigger(const Trigger &t, int32_t fault_id) +{ + etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::Injector::removeTrigger(Trigger&=") + t.toString() + + ", fault_id=" + std::to_string(fault_id) + ")"); +#if CXX0X_UP_SUPPORTED + std::lock_guard lock(sync); +#endif + remove_triggers.push_back(std::pair(t, fault_id)); + has_remove_triggers = true; +} + bool Injector::acceleratedTrigger(const etiss::fault::Trigger &t, int32_t fault_id) { etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::Injector::acceleratedTrigger(Trigger&=") + diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index f8fb41ecd0..6658034f4b 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -78,8 +78,8 @@ static std::mutex &faults_sync() return mu; } #endif -// Map with all faults found in XML file -static std::map &faults() +// Map with all fault definitions found in XML file +std::map &Stressor::faults() { static std::map map; return map; @@ -103,7 +103,8 @@ bool Stressor::loadXML(const std::string &file, const int coreID) coreIDActuallXML = coreID; // Vector which gets the faults of the xml file - std::vector faults; + std::vector fault_definitions; + std::vector initial_faults; // open and parse file std::ifstream in; @@ -118,7 +119,9 @@ bool Stressor::loadXML(const std::string &file, const int coreID) #endif return false; } - if (!etiss::fault::parseXML(faults, in, std::cout)) + pugi::xml_document doc; + + if (!etiss::fault::parseXML(doc, in, std::cout)) { #ifdef NO_ETISS std::cout << "etiss::fault::Stressor::loadXML: Failed parse file " << file << std::endl; @@ -129,37 +132,79 @@ bool Stressor::loadXML(const std::string &file, const int coreID) return false; } - // add faults into a map -> access with static std::map & + xml::Diagnostics diag; + + if (!etiss::fault::parseXML(fault_definitions, doc, diag)) + { + std::stringstream ss; + diag.print(ss); +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::loadXML: Failed parse Fault definitions " << ss << std::endl; +#else + etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::loadXML:") + + std::string(" Failed parse Fault definitions ") + ss.str()); +#endif + return false; + } + + // add fault_definitions into a map -> access with static std::map & // faults() bool ok = true; - for (size_t i = 0; i < faults.size(); ++i) + for (const auto &fdef : fault_definitions) + { + if (!addFaultDefinition(fdef)) + { +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::loadXML: Failed to add Fault definitions: " << fdef.name_ + << std::endl; +#else + etiss::log(etiss::ERROR, + std::string("etiss::fault::Stressor::loadXML:") + + std::string(" Failed to add Fault definitions "), + fdef); +#endif + ok = false; + } + } + + if (!etiss::fault::parseXML(initial_faults, doc, diag)) + { + std::stringstream ss; + diag.print(ss); +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::loadXML: Failed parse initial Faults " << ss << std::endl; +#else + etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::loadXML:") + + std::string(" Failed parse initial Faults ") + ss.str()); +#endif + return false; + } + + for (const auto &fref : initial_faults) { - if (!addFault(faults[i])) + if (!addFault(fref.get_fault())) { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::loadXML: Failed to add Fault: " << faults[i].name_ << std::endl; + std::cout << "etiss::fault::Stressor::loadXML: Failed to add initial Fault: " << fref.name_ << std::endl; #else etiss::log(etiss::ERROR, - std::string("etiss::fault::Stressor::loadXML:") + std::string(" Failed to add Fault "), - faults[i]); + std::string("etiss::fault::Stressor::loadXML:") + std::string(" Failed to add initial Fault "), + fref); #endif ok = false; } } + return ok; } -bool Stressor::addFault(const Fault &f, bool injected_fault) +bool Stressor::addFaultDefinition(const Fault &f) { -#if CXX0X_UP_SUPPORTED - if (!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur - std::lock_guard lock(faults_sync()); -#endif #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault called." << std::endl; + std::cout << "etiss::fault::Stressor::addFaultDefinition called." << std::endl; #else - etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFault called. ")); + etiss::log(etiss::INFO, std::string("etiss::fault::Stressor::addFaultDefinition called. ")); #endif // check if fault already exists @@ -167,46 +212,76 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) if (find != faults().end()) { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Trigger already exists:" << f.toString() << std::endl; + std::cout << "etiss::fault::Stressor::addFaultDefinition: Fault definition already registered:" << f.toString() + << std::endl; #else etiss::log(etiss::ERROR, - std::string("etiss::fault::Stressor::addFault:") + std::string(" Trigger already exists. "), f); + std::string("etiss::fault::Stressor::addFaultDefinition:") + + std::string(" Fault definition already registered: "), + f); #endif return false; } - // insert fault into map + // insert fault into the definitions map faults().insert(std::pair(f.id_, f)); + return true; +} + +bool Stressor::addFault(const Fault &f, bool injected_fault) +{ + +#if CXX0X_UP_SUPPORTED + if (!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur + std::lock_guard lock(faults_sync()); +#endif + Injector::ptr iptr = nullptr; + // check if fault is registered in fault definitions table + std::map::iterator find = faults().find(f.id_); + if (find == faults().end()) + { +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::addFault: Fault not registered in Fault definitions:" << f.toString() + << std::endl; +#else + etiss::log(etiss::ERROR, + std::string("etiss::fault::Stressor::addFault:") + + std::string(" Fault not registered in Fault definitions. "), + f); +#endif + return false; + } + // Iterate through triggers of the fault - for (std::vector::const_iterator iter = f.triggers.begin(); iter != f.triggers.end(); ++iter) + for (const auto &trigger : f.triggers) { - if (iter->getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP + + if (trigger.getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP { - iptr = iter->getInjector(); + iptr = trigger.getInjector(); if (iptr != nullptr) { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << iter->toString() << std::endl; + std::cout << "etiss::fault::Stressor::addFault: Added trigger: " << trigger.toString() << std::endl; #else etiss::log(etiss::INFO, - std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), *iter); + std::string("etiss::fault::Stressor::addFault:") + std::string(" Added trigger: "), trigger); #endif - // TODO: iptr->enable_faulttype for requested field - iptr->addTrigger(*iter, f.id_); + iptr->addTrigger(trigger, f.id_); } else { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Error: Injector not found for: " << iter->toString() + std::cout << "etiss::fault::Stressor::addFault: Error: Injector not found for: " << trigger.toString() << std::endl; #else etiss::log(etiss::ERROR, std::string("etiss::fault::Stressor::addFault:") + std::string(" Injector not found for "), - *iter); + trigger); #endif /// TODO signal error and roll back } @@ -238,6 +313,76 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) return true; } +bool Stressor::removeFault(const Fault &f, bool injected_fault) +{ + +#if CXX0X_UP_SUPPORTED + if (!injected_fault) // otherwise a deadlock from firedTrigger->addFault would occur + std::lock_guard lock(faults_sync()); +#endif + + Injector::ptr iptr = nullptr; + + // check if fault is registered in fault definitions table + std::map::iterator find = faults().find(f.id_); + if (find == faults().end()) + { +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::addFault: Fault not registered in Fault definitions:" << f.toString() + << std::endl; +#else + etiss::log(etiss::ERROR, + std::string("etiss::fault::Stressor::addFault:") + + std::string(" Fault not registered in Fault definitions. "), + f); +#endif + return false; + } + + // Iterate through triggers of the fault + for (const auto &trigger : f.triggers) + { + + if (trigger.getType() != etiss::fault::Trigger::Type::NOP) // don't care + { + iptr = trigger.getInjector(); + + if (iptr != nullptr) + { +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::removeFault: Removed trigger: " << trigger.toString() + << std::endl; +#else + etiss::log(etiss::INFO, + std::string("etiss::fault::Stressor::removeFault:") + std::string(" Removed trigger: "), + trigger); +#endif + iptr->removeTrigger(trigger, f.id_); + } + else + { +#ifdef NO_ETISS + std::cout << "etiss::fault::Stressor::removeFault: Error: Injector not found for: " + << trigger.toString() << std::endl; +#else + etiss::log(etiss::ERROR, + std::string("etiss::fault::Stressor::removeFault:") + + std::string(" Injector not found for "), + trigger); +#endif + /// TODO signal error and roll back + } + } + else // Trigger is of type NOP + { + etiss::log(etiss::WARNING, std::string("etiss::fault::Stressor::removeFault:") + + std::string(" Trigger is a NOP and is does not need to be removed.")); + } + } + + return true; +} + bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector *injector, uint64_t time_ps) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Stressor::firedTrigger() called. ")); @@ -253,26 +398,49 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector for (std::vector::iterator iter = find->second.actions.begin(); iter != find->second.actions.end(); ++iter) { - if (iter->getType() == etiss::fault::Action::Type::INJECTION) - { - /// TODO for time relative triggers resolve time must be called! - addFault(iter->getFault(), true); - } - else if (iter->getType() == etiss::fault::Action::Type::NOP) + switch (iter->getType()) { + case etiss::fault::Action::Type::INJECTION: + if (!iter->getFaultRef().is_set()) + { + // try to resolve the reference again + if (!iter->getFaultRef().resolve_reference()) + { + etiss::log( + etiss::ERROR, + std::string( + "Stressor::firedTrigger: Injected Fault reference not found in fault definitions."), + iter->getFaultRef()); + } + } + addFault(iter->getFaultRef().get_fault(), true); + break; + case etiss::fault::Action::Type::EJECTION: + if (!iter->getFaultRef().is_set()) + { + // try to resolve the reference again + if (!iter->getFaultRef().resolve_reference()) + { + etiss::log( + etiss::ERROR, + std::string( + "Stressor::firedTrigger: Injected Fault reference not found in fault definitions."), + iter->getFaultRef()); + } + } + removeFault(iter->getFaultRef().get_fault(), true); + break; + case etiss::fault::Action::Type::NOP: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); return true; - } #ifndef NO_ETISS - else if (iter->getType() == etiss::fault::Action::Type::EVENT) - { + case etiss::fault::Action::Type::EVENT: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EVENT")); set_event(iter->getEvent()); return true; - } #endif - else + default: // on field actions { if (iter->getInjectorAddress().getInjector()) { @@ -317,6 +485,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector #endif } } + } } } else @@ -339,6 +508,19 @@ void Stressor::clear() faults().clear(); } +bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out) +{ + pugi::xml_parse_result pr = doc.load(input); // load from stream + + if (!pr) + { // load failure + diagnostics_out << "failed to load xml from stream: " << pr.description() << std::endl; + return false; + } + + return true; +} + } // namespace fault } // namespace etiss From be3c57848ae63d28c526dfd8837f2d08733f5393 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 3 Dec 2021 15:10:54 +0100 Subject: [PATCH 26/51] fix trigger removal uninitialized remove flag --- include/etiss/fault/Injector.h | 4 ++-- src/fault/Injector.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index f6718973cf..2925996f9e 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -189,8 +189,8 @@ class Injector #if CXX0X_UP_SUPPORTED std::mutex sync; #endif - volatile bool has_pending_triggers; - volatile bool has_remove_triggers; + volatile bool has_pending_triggers{false}; + volatile bool has_remove_triggers{false}; std::list> pending_triggers; ///< Triggers which were just added std::list> unknown_triggers; ///< Triggers to look at in callbacks std::list> diff --git a/src/fault/Injector.cpp b/src/fault/Injector.cpp index 656503c11e..079459a081 100644 --- a/src/fault/Injector.cpp +++ b/src/fault/Injector.cpp @@ -103,9 +103,9 @@ bool Injector::cycleAccurateCallback(uint64_t time_ps) #if CXX0X_UP_SUPPORTED std::lock_guard lock(sync); #endif - for (std::list>::iterator iter = unknown_triggers.begin(); - iter != unknown_triggers.end();) - for (const auto &rm : remove_triggers) + for (const auto &rm : remove_triggers) + for (std::list>::iterator iter = unknown_triggers.begin(); + iter != unknown_triggers.end();) { if (iter->second == rm.second) // remove all triggers associated with the fault ids of rm unknown_triggers.erase(iter++); From 8699db4f87b4abb954f512f79b9de5eec4943676 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 16:46:09 +0100 Subject: [PATCH 27/51] remove volatile for pending and remove flags --- include/etiss/fault/Injector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index 2925996f9e..7190feda90 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -189,8 +189,8 @@ class Injector #if CXX0X_UP_SUPPORTED std::mutex sync; #endif - volatile bool has_pending_triggers{false}; - volatile bool has_remove_triggers{false}; + bool has_pending_triggers{ false }; + bool has_remove_triggers{ false }; std::list> pending_triggers; ///< Triggers which were just added std::list> unknown_triggers; ///< Triggers to look at in callbacks std::list> From d64a01b6428e94cbc28687609c130d76329ead4b Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 16:48:00 +0100 Subject: [PATCH 28/51] replace nested loop with erase-remove idiom --- src/fault/Injector.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/fault/Injector.cpp b/src/fault/Injector.cpp index 079459a081..d19f51f8f9 100644 --- a/src/fault/Injector.cpp +++ b/src/fault/Injector.cpp @@ -103,17 +103,17 @@ bool Injector::cycleAccurateCallback(uint64_t time_ps) #if CXX0X_UP_SUPPORTED std::lock_guard lock(sync); #endif - for (const auto &rm : remove_triggers) - for (std::list>::iterator iter = unknown_triggers.begin(); - iter != unknown_triggers.end();) - { - if (iter->second == rm.second) // remove all triggers associated with the fault ids of rm - unknown_triggers.erase(iter++); - else - { - ++iter; - } - } + unknown_triggers.erase(std::remove_if(unknown_triggers.begin(), unknown_triggers.end(), + [&](const auto &unknown) + { + for (const auto &rm : remove_triggers) + { + if (unknown.second == rm.second) + return true; + } + return false; + }), + unknown_triggers.end()); remove_triggers.clear(); has_remove_triggers = false; } From d6e4b657d54b173845d48fd3fc8e786dd8528e9c Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 16:50:18 +0100 Subject: [PATCH 29/51] remove explicit but default equivalents --- include/etiss/fault/Fault.h | 21 +++----------- src/fault/Fault.cpp | 55 +------------------------------------ 2 files changed, 5 insertions(+), 71 deletions(-) diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index c3e8d73dca..50d756b5be 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -92,12 +92,6 @@ class Fault : public etiss::ToString Fault(); ///< Constructor: Generates a new Fault with unique ID Fault(int nullid); - Fault(const Fault &cpy); - Fault &operator=(const Fault &cpy); -#if CXX0X_UP_SUPPORTED - Fault(Fault &&cpy); - Fault &operator=(Fault &&cpy); -#endif public: std::string name_{ "" }; @@ -109,23 +103,16 @@ class Fault : public etiss::ToString class FaultRef : public etiss::ToString { private: - std::unique_ptr fault_{ nullptr }; ///< referenced Fault - std::string name_{ "" }; ///< string identifier, used to resolve actual reference via fault_ + mutable Fault fault_; ///< referenced Fault, needs to be resolved during sim. runtime + std::string name_{ "" }; ///< string identifier, used to resolve actual reference via fault_ public: std::string toString() const; ///< operator<< can be used. - FaultRef(); - FaultRef(const FaultRef &cpy); - FaultRef &operator=(const FaultRef &cpy); -#if CXX0X_UP_SUPPORTED - FaultRef(FaultRef &&cpy); - FaultRef &operator=(FaultRef &&cpy); -#endif - bool is_set() const { return (fault_->name_ == name_); } + bool is_set() const { return (fault_.name_ == name_); } bool set_fault_reference(const std::string &identifier); bool resolve_reference() const; - const Fault &get_fault() const { return *fault_; } + const Fault &get_fault() const { return fault_; } const std::string &get_name() const { return name_; } }; diff --git a/src/fault/Fault.cpp b/src/fault/Fault.cpp index a5596845bf..9dd2e7fac1 100644 --- a/src/fault/Fault.cpp +++ b/src/fault/Fault.cpp @@ -264,32 +264,6 @@ Fault::Fault() : id_(uniqueFaultId()) Fault::Fault(int nullid) : id_(nullid) {} -Fault::Fault(const Fault &cpy) -{ - *this = cpy; -} - -Fault &Fault::operator=(const Fault &cpy) -{ - name_ = cpy.name_; - id_ = cpy.id_; - triggers = cpy.triggers; - actions = cpy.actions; - return *this; -} - -#if CXX0X_UP_SUPPORTED -Fault::Fault(Fault &&cpy) -{ - operator=(cpy); -} -Fault &Fault::operator=(Fault &&cpy) -{ - operator=((const Fault &)cpy); - return *this; -} -#endif - std::string Fault::toString() const { @@ -326,33 +300,6 @@ bool Fault::isResoved() const return true; } -FaultRef::FaultRef() : fault_(std::make_unique()) {} - -FaultRef::FaultRef(const FaultRef &cpy) : FaultRef() -{ - *this = cpy; -} - -FaultRef &FaultRef::operator=(const FaultRef &cpy) -{ - name_ = cpy.get_name(); - *fault_ = cpy.get_fault(); - - return *this; -} - -#if CXX0X_UP_SUPPORTED -FaultRef::FaultRef(FaultRef &&cpy) -{ - operator=(cpy); -} -FaultRef &FaultRef::operator=(FaultRef &&cpy) -{ - operator=((const FaultRef &)cpy); - return *this; -} -#endif - std::string FaultRef::toString() const { @@ -383,7 +330,7 @@ bool FaultRef::resolve_reference() const { if (it.second.name_ == name_) { - *fault_ = it.second; + fault_ = it.second; return true; } } From ed74938063092c10d416a6ccb93582b997a6e803 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 17:09:13 +0100 Subject: [PATCH 30/51] reverse commit c8f94781a55aee42c70149b4a0f8fcf710bf34da, remove re-assignemnt of Field pointer, run clang-format --- src/VirtualStruct.cpp | 95 ++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index 18241c312e..a9bf1aed7a 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -444,10 +444,12 @@ bool VirtualStruct::isClosed() std::list VirtualStruct::listFields() { std::list ret; - foreachField([&ret](std::shared_ptr f) { - if (f) - ret.push_back(f->name_); - }); + foreachField( + [&ret](std::shared_ptr f) + { + if (f) + ret.push_back(f->name_); + }); return ret; } std::list VirtualStruct::listSubInjectors() @@ -557,11 +559,8 @@ bool VirtualStruct::update_field_access_rights(const etiss::fault::Action &actio auto find = fieldNames_.find(action.getTargetField()); if (find == fieldNames_.end()) { - if (find == fieldPrettyNames_.end()) - { - f = 0; - } - else + find = fieldPrettyNames_.find(field); + if (find != fieldPrettyNames_.end()) { f = find->second; } @@ -618,55 +617,59 @@ void copy(VirtualStruct &dst, VirtualStruct &src, std::list> dst_known; - src.foreachField([&](std::shared_ptr srcf) { - if (srcf->flags_ & VirtualStruct::Field::P) + src.foreachField( + [&](std::shared_ptr srcf) { - if (src_private) - src_private->push_back(srcf); - return; - } + if (srcf->flags_ & VirtualStruct::Field::P) + { + if (src_private) + src_private->push_back(srcf); + return; + } - auto dstf = dst.findName(srcf->name_); - if (dstf == 0) - { - notPresent.push_back(srcf); - return; - } + auto dstf = dst.findName(srcf->name_); + if (dstf == 0) + { + notPresent.push_back(srcf); + return; + } - if (dstf->flags_ & VirtualStruct::Field::P) - { - notPresent.push_back(srcf); - return; - } + if (dstf->flags_ & VirtualStruct::Field::P) + { + notPresent.push_back(srcf); + return; + } - dst_known.insert(dstf); + dst_known.insert(dstf); - if (!(dstf->flags_ & VirtualStruct::Field::W)) - { - notWriteable.push_back(dstf); - return; - } + if (!(dstf->flags_ & VirtualStruct::Field::W)) + { + notWriteable.push_back(dstf); + return; + } - if (!pretend) - dstf->write(srcf->read()); // copy value - }); + if (!pretend) + dstf->write(srcf->read()); // copy value + }); - dst.foreachField([&](std::shared_ptr dstf) { - if (dst_known.find(dstf) == dst_known.end()) + dst.foreachField( + [&](std::shared_ptr dstf) { - if (dstf->flags_ & VirtualStruct::Field::P) + if (dst_known.find(dstf) == dst_known.end()) { - if (dst_private) + if (dstf->flags_ & VirtualStruct::Field::P) { - dst_private->push_back(dstf); + if (dst_private) + { + dst_private->push_back(dstf); + } + } + else + { + unknown.push_back(dstf); } } - else - { - unknown.push_back(dstf); - } - } - }); + }); } std::shared_ptr VirtualStruct::allocate(void *structure, std::function delete_) From a6bc78ec3517033d461cc761937b8ac762e5ccab Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 17:17:30 +0100 Subject: [PATCH 31/51] fix Field pointer --- src/VirtualStruct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index a9bf1aed7a..caf44e2f76 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -559,7 +559,7 @@ bool VirtualStruct::update_field_access_rights(const etiss::fault::Action &actio auto find = fieldNames_.find(action.getTargetField()); if (find == fieldNames_.end()) { - find = fieldPrettyNames_.find(field); + find = fieldPrettyNames_.find(action.getTargetField()); if (find != fieldPrettyNames_.end()) { f = find->second; From 7526b08949893716b7242e1f9af7c32c47b9d98b Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Mon, 6 Dec 2021 17:21:20 +0100 Subject: [PATCH 32/51] remove empty default initializations --- include/etiss/fault/Action.h | 8 ++++---- include/etiss/fault/Fault.h | 4 ++-- include/etiss/fault/Trigger.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 378c223a54..d67b9d1e52 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -186,13 +186,13 @@ class Action : public etiss::ToString private: // Attributes type_t type_; ///< type of the Attribute - std::unique_ptr inj_{ nullptr }; ///< Address of Injector - std::string command_{ "" }; ///< command e.g. for booting OR1KVCPU - std::string field_{ "" }; ///< concerning Field (for fault injection) + std::unique_ptr inj_; ///< Address of Injector + std::string command_; ///< command e.g. for booting OR1KVCPU + std::string field_; ///< concerning Field (for fault injection) unsigned bit_ = { 0 }; ///< concerning Bit (for fault injection) mask_op_t mask_op_{ MaskOp::NOP }; ///< mask operation (for mask injection) uint64_t mask_value_{ 0 }; ///< mask value (for mask injection) - std::unique_ptr fault_ref_{ nullptr }; ///< for fault injection + std::unique_ptr fault_ref_; ///< for fault injection #ifndef NO_ETISS int32_t event_{ 0 }; ///< exception, or rather etiss::RETURNCODE to /// to be injected into the simulation loop diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index 50d756b5be..4cebaeb890 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -94,7 +94,7 @@ class Fault : public etiss::ToString Fault(int nullid); public: - std::string name_{ "" }; + std::string name_; int32_t id_; ///< @attention negative ids are reserved std::vector triggers; ///< contains the triggers for this fault std::vector actions; ///< contains the actions for this fault @@ -104,7 +104,7 @@ class FaultRef : public etiss::ToString { private: mutable Fault fault_; ///< referenced Fault, needs to be resolved during sim. runtime - std::string name_{ "" }; ///< string identifier, used to resolve actual reference via fault_ + std::string name_; ///< string identifier, used to resolve actual reference via fault_ public: std::string toString() const; ///< operator<< can be used. diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index ac806ea6b1..a40cc4a603 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -168,10 +168,10 @@ class Trigger : public etiss::ToString private: // Attributes type_t type_; - std::string field_{ "" }; - std::unique_ptr sub_{ nullptr }; - std::unique_ptr inj_{ nullptr }; - void *fieldptr_{ nullptr }; + std::string field_; + std::unique_ptr sub_; + std::unique_ptr inj_; + void *fieldptr_; uint64_t param1_{ 0 }; uint64_t param2_{ 0 }; From 446d7c3d04f0cd92c2dde50de38c3c87bcdc9264 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 20 Jan 2022 12:55:02 +0100 Subject: [PATCH 33/51] memory manipulation system for bare etiss processor in case of fi --- examples/bare_etiss_processor/faults.xml | 8 +- src/bare_etiss_processor/main.cpp | 131 ++++++++++++++--------- 2 files changed, 85 insertions(+), 54 deletions(-) diff --git a/examples/bare_etiss_processor/faults.xml b/examples/bare_etiss_processor/faults.xml index fded0a23d1..239305cebc 100644 --- a/examples/bare_etiss_processor/faults.xml +++ b/examples/bare_etiss_processor/faults.xml @@ -12,6 +12,10 @@ + + core%i%::system + rmw 0x80000 OR 0xFFFFFFFF + core%i% some string @@ -32,7 +36,7 @@ - 10 + 9 core%i% @@ -55,7 +59,7 @@ core%i% R2 OR - 0x55555555 + 0x1 diff --git a/src/bare_etiss_processor/main.cpp b/src/bare_etiss_processor/main.cpp index 9e93fd5463..ea9731526d 100644 --- a/src/bare_etiss_processor/main.cpp +++ b/src/bare_etiss_processor/main.cpp @@ -42,9 +42,9 @@ #include "TracePrinter.h" #include "etiss/SimpleMemSystem.h" +#include "etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h" #include "etiss/ETISS.h" - int main(int argc, const char *argv[]) { // by default etiss wil search for plugin libraries in its install path and @@ -55,20 +55,33 @@ int main(int argc, const char *argv[]) // ./main [-o core%i% - R1 + X1 1 @@ -247,7 +247,7 @@ Usage: core%i% - R2 + X2 OR 0x55555555 diff --git a/examples/bare_etiss_processor/faults.xml b/src/bare_etiss_processor/faults.xml similarity index 97% rename from examples/bare_etiss_processor/faults.xml rename to src/bare_etiss_processor/faults.xml index 239305cebc..7997884af7 100644 --- a/examples/bare_etiss_processor/faults.xml +++ b/src/bare_etiss_processor/faults.xml @@ -22,7 +22,7 @@ core%i% - R1 + X1 1 @@ -57,13 +57,13 @@ core%i% - R2 + X2 OR 0x1 - + From 6d57eb2beeeb1858f68952dca537722cf365596f Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 16 Dec 2022 10:58:07 +0100 Subject: [PATCH 36/51] move pugixml to CMake FetchContent, update pugixml to cmake supported version --- CMakeLists.txt | 11 + include/etiss/fault/XML.h | 4 +- include/etiss/fault/xml/CMakeLists.txt | 51 - include/etiss/fault/xml/pugiconfig.hpp | 72 - include/etiss/fault/xml/pugixml.hpp | 1389 --- include/etiss/fault/xml/readme_pugixml | 1 - src/fault/Action.cpp | 2 +- src/fault/Fault.cpp | 10 +- src/fault/xml/CMakeLists.txt | 49 - src/fault/xml/pugixml.cpp | 11229 ----------------------- 10 files changed, 19 insertions(+), 12799 deletions(-) delete mode 100644 include/etiss/fault/xml/CMakeLists.txt delete mode 100644 include/etiss/fault/xml/pugiconfig.hpp delete mode 100644 include/etiss/fault/xml/pugixml.hpp delete mode 100644 include/etiss/fault/xml/readme_pugixml delete mode 100644 src/fault/xml/CMakeLists.txt delete mode 100644 src/fault/xml/pugixml.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 77ac7633c6..04d1d933cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,16 @@ endif() set(ETISS_SOURCE ${ETISS_SOURCE} "${simpleini_srcs_SOURCE_DIR}/ConvertUTF.c") +FetchContent_Declare(pugixml_srcs + GIT_REPOSITORY https://github.com/zeux/pugixml.git + GIT_TAG v1.11.4 +) +if(NOT pugixml_srcs_POPULATED) + FetchContent_Populate(pugixml_srcs) + FetchContent_GetProperties(pugixml_srcs) +endif() +add_subdirectory(${pugixml_srcs_SOURCE_DIR} ${pugixml_srcs_BINARY_DIR} EXCLUDE_FROM_ALL) + ### Doxyfile set(ETISS_DOX_LOCATIONS ${ETISS_DOX_LOCATIONS} ${PROJECT_BINARY_DIR}/include) set(ETISS_DOX_LOCATIONS ${ETISS_DOX_LOCATIONS} ${PROJECT_BINARY_DIR}/include_c) @@ -373,6 +383,7 @@ TARGET_INCLUDE_DIRECTORIES(ETISS PUBLIC ${elfio_srcs_SOURCE_DIR} ${simpleini_srcs_SOURCE_DIR} ) +TARGET_LINK_LIBRARIES(ETISS PUBLIC pugixml) GENERATE_EXPORT_HEADER(ETISS BASE_NAME ETISS_PLUGIN diff --git a/include/etiss/fault/XML.h b/include/etiss/fault/XML.h index 729e00c27d..54b02c03e8 100644 --- a/include/etiss/fault/XML.h +++ b/include/etiss/fault/XML.h @@ -57,10 +57,10 @@ #ifndef NO_ETISS #include "etiss/fault/Defs.h" -#include "etiss/fault/xml/pugixml.hpp" +#include "pugixml.hpp" #else #include "fault/Defs.h" -#include "fault/xml/pugixml.hpp" +#include "pugixml.hpp" #endif #include diff --git a/include/etiss/fault/xml/CMakeLists.txt b/include/etiss/fault/xml/CMakeLists.txt deleted file mode 100644 index f8a45aea5a..0000000000 --- a/include/etiss/fault/xml/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright 2018 Infineon Technologies AG -# -# This file is part of ETISS tool, see -# -# The initial version of this software has been created with the funding support by the German Federal -# Ministry of Education and Research(BMBF) in the project EffektiV under grant 01IS13022. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted -# provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and -# the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions -# and the following disclaimer in the documentation and / or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse -# or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# -# Author: Marc Greim , Chair of Electronic Design Automation, TUM -# -# Version 0.1 -# - -### recurse into sub directories -unset(LOCAL_SUBDIRS) -FILE(GLOB LOCAL_SUBDIRS ${CMAKE_CURRENT_LIST_DIR}/*) -foreach(subdir ${LOCAL_SUBDIRS}) - if(IS_DIRECTORY ${subdir}) - add_subdirectory(${subdir}) - endif() -endforeach() - - -unset(LOCAL_SOURCE1) -unset(LOCAL_SOURCE2) -FILE(GLOB LOCAL_SOURCE1 RELATIVE ${ETISS_ROOT_DIR} *.h) -FILE(GLOB LOCAL_SOURCE2 RELATIVE ${ETISS_ROOT_DIR} *.hpp) - -set(ETISS_HEADER ${ETISS_HEADER} ${LOCAL_SOURCE1} ${LOCAL_SOURCE2} PARENT_SCOPE) diff --git a/include/etiss/fault/xml/pugiconfig.hpp b/include/etiss/fault/xml/pugiconfig.hpp deleted file mode 100644 index f74c301ccb..0000000000 --- a/include/etiss/fault/xml/pugiconfig.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * pugixml parser - version 1.4 - * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef HEADER_PUGICONFIG_HPP -#define HEADER_PUGICONFIG_HPP - -// Uncomment this to enable wchar_t mode -// #define PUGIXML_WCHAR_MODE - -// Uncomment this to disable XPath -// #define PUGIXML_NO_XPATH - -// Uncomment this to disable STL -// #define PUGIXML_NO_STL - -// Uncomment this to disable exceptions -// #define PUGIXML_NO_EXCEPTIONS - -// Set this to control attributes for public classes/functions, i.e.: -// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL -// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL -// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall -// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead - -// Tune these constants to adjust memory-related behavior -// #define PUGIXML_MEMORY_PAGE_SIZE 32768 -// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 -// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 - -// Uncomment this to switch to header-only version -// #define PUGIXML_HEADER_ONLY -// #include "pugixml.cpp" - -// Uncomment this to enable long long support -#define PUGIXML_HAS_LONG_LONG - -#endif - -/** - * Copyright (c) 2006-2014 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/include/etiss/fault/xml/pugixml.hpp b/include/etiss/fault/xml/pugixml.hpp deleted file mode 100644 index 519f7a1ad7..0000000000 --- a/include/etiss/fault/xml/pugixml.hpp +++ /dev/null @@ -1,1389 +0,0 @@ -/** - * pugixml parser - version 1.4 - * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -#define PUGIXML_VERSION 140 -#endif - -// Include user configuration file (this can define various configuration macros) -#ifndef NO_ETISS -#include "etiss/fault/xml/pugiconfig.hpp" -#else -#include "fault/xml/pugiconfig.hpp" -#endif - -#ifndef HEADER_PUGIXML_HPP -#define HEADER_PUGIXML_HPP - -// Include stddef.h for size_t and ptrdiff_t -#include - -// Include exception header for XPath -#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) -#include -#endif - -// Include STL headers -#ifndef PUGIXML_NO_STL -#include -#include -#include -#endif - -// Macro for deprecated features -#ifndef PUGIXML_DEPRECATED -#if defined(__GNUC__) -#define PUGIXML_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) && _MSC_VER >= 1300 -#define PUGIXML_DEPRECATED __declspec(deprecated) -#else -#define PUGIXML_DEPRECATED -#endif -#endif - -// If no API is defined, assume default -#ifndef PUGIXML_API -#define PUGIXML_API -#endif - -// If no API for classes is defined, assume default -#ifndef PUGIXML_CLASS -#define PUGIXML_CLASS PUGIXML_API -#endif - -// If no API for functions is defined, assume default -#ifndef PUGIXML_FUNCTION -#define PUGIXML_FUNCTION PUGIXML_API -#endif - -// If the platform is known to have long long support, enable long long functions -#ifndef PUGIXML_HAS_LONG_LONG -#if defined(__cplusplus) && __cplusplus >= 201103 -#define PUGIXML_HAS_LONG_LONG -#elif defined(_MSC_VER) && _MSC_VER >= 1400 -#define PUGIXML_HAS_LONG_LONG -#endif -#endif - -// Character interface macros -#ifdef PUGIXML_WCHAR_MODE -#define PUGIXML_TEXT(t) L##t -#define PUGIXML_CHAR wchar_t -#else -#define PUGIXML_TEXT(t) t -#define PUGIXML_CHAR char -#endif - -namespace pugi -{ -// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE -typedef PUGIXML_CHAR char_t; - -#ifndef PUGIXML_NO_STL -// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE -typedef std::basic_string, std::allocator> string_t; -#endif -} // namespace pugi - -// The PugiXML namespace -namespace pugi -{ -// Tree node types -enum xml_node_type -{ - node_null, // Empty (null) node handle - node_document, // A document tree's absolute root - node_element, // Element tag, i.e. '' - node_pcdata, // Plain character data, i.e. 'text' - node_cdata, // Character data, i.e. '' - node_comment, // Comment tag, i.e. '' - node_pi, // Processing instruction, i.e. '' - node_declaration, // Document declaration, i.e. '' - node_doctype // Document type declaration, i.e. '' -}; - -// Parsing options - -// Minimal parsing mode (equivalent to turning all other flags off). -// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. -const unsigned int parse_minimal = 0x0000; - -// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. -const unsigned int parse_pi = 0x0001; - -// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. -const unsigned int parse_comments = 0x0002; - -// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. -const unsigned int parse_cdata = 0x0004; - -// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. -// This flag is off by default; turning it on usually results in slower parsing and more memory consumption. -const unsigned int parse_ws_pcdata = 0x0008; - -// This flag determines if character and entity references are expanded during parsing. This flag is on by default. -const unsigned int parse_escapes = 0x0010; - -// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. -const unsigned int parse_eol = 0x0020; - -// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is -// on by default. -const unsigned int parse_wconv_attribute = 0x0040; - -// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag -// is off by default. -const unsigned int parse_wnorm_attribute = 0x0080; - -// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by -// default. -const unsigned int parse_declaration = 0x0100; - -// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by -// default. -const unsigned int parse_doctype = 0x0200; - -// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that -// consists only of whitespace is added to the DOM tree. This flag is off by default; turning it on may result in slower -// parsing and more memory consumption. -const unsigned int parse_ws_pcdata_single = 0x0400; - -// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off -// by default. -const unsigned int parse_trim_pcdata = 0x0800; - -// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an -// empty document is a valid document. This flag is off by default. -const unsigned int parse_fragment = 0x1000; - -// The default parsing mode. -// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, -// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. -const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; - -// The full parsing mode. -// Nodes of all types are added to the DOM tree, character/reference entities are expanded, -// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. -const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; - -// These flags determine the encoding of input data for XML document -enum xml_encoding -{ - encoding_auto, // Auto-detect input encoding using BOM or < / -class xml_object_range -{ - public: - typedef It const_iterator; - typedef It iterator; - - xml_object_range(It b, It e) : _begin(b), _end(e) {} - - It begin() const { return _begin; } - It end() const { return _end; } - - private: - It _begin, _end; -}; - -// Writer interface for node printing (see xml_node::print) -class PUGIXML_CLASS xml_writer -{ - public: - virtual ~xml_writer() {} - - // Write memory chunk into stream/file/whatever - virtual void write(const void *data, size_t size) = 0; -}; - -// xml_writer implementation for FILE* -class PUGIXML_CLASS xml_writer_file : public xml_writer -{ - public: - // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio - xml_writer_file(void *file); - - virtual void write(const void *data, size_t size); - - private: - void *file; -}; - -#ifndef PUGIXML_NO_STL -// xml_writer implementation for streams -class PUGIXML_CLASS xml_writer_stream : public xml_writer -{ - public: - // Construct writer from an output stream object - xml_writer_stream(std::basic_ostream> &stream); - xml_writer_stream(std::basic_ostream> &stream); - - virtual void write(const void *data, size_t size); - - private: - std::basic_ostream> *narrow_stream; - std::basic_ostream> *wide_stream; -}; -#endif - -// A light-weight handle for manipulating attributes in DOM tree -class PUGIXML_CLASS xml_attribute -{ - friend class xml_attribute_iterator; - friend class xml_node; - - private: - xml_attribute_struct *_attr; - - typedef void (*unspecified_bool_type)(xml_attribute ***); - - public: - // Default constructor. Constructs an empty attribute. - xml_attribute(); - - // Constructs attribute from internal pointer - explicit xml_attribute(xml_attribute_struct *attr); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped attribute pointers) - bool operator==(const xml_attribute &r) const; - bool operator!=(const xml_attribute &r) const; - bool operator<(const xml_attribute &r) const; - bool operator>(const xml_attribute &r) const; - bool operator<=(const xml_attribute &r) const; - bool operator>=(const xml_attribute &r) const; - - // Check if attribute is empty - bool empty() const; - - // Get attribute name/value, or "" if attribute is empty - const char_t *name() const; - const char_t *value() const; - - // Get attribute value, or the default value if attribute is empty - const char_t *as_string(const char_t *def = PUGIXML_TEXT("")) const; - - // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - -#ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; -#endif - - // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if - // attribute is empty - bool as_bool(bool def = false) const; - - // Set attribute name/value (returns false if attribute is empty or there is not enough memory) - bool set_name(const char_t *rhs); - bool set_value(const char_t *rhs); - - // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to - // "true"/"false") - bool set_value(int rhs); - bool set_value(unsigned int rhs); - bool set_value(double rhs); - bool set_value(bool rhs); - -#ifdef PUGIXML_HAS_LONG_LONG - bool set_value(long long rhs); - bool set_value(unsigned long long rhs); -#endif - - // Set attribute value (equivalent to set_value without error checking) - xml_attribute &operator=(const char_t *rhs); - xml_attribute &operator=(int rhs); - xml_attribute &operator=(unsigned int rhs); - xml_attribute &operator=(double rhs); - xml_attribute &operator=(bool rhs); - -#ifdef PUGIXML_HAS_LONG_LONG - xml_attribute &operator=(long long rhs); - xml_attribute &operator=(unsigned long long rhs); -#endif - - // Get next/previous attribute in the attribute list of the parent node - xml_attribute next_attribute() const; - xml_attribute previous_attribute() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_attribute_struct *internal_object() const; -}; - -#ifdef __BORLANDC__ -// Borland C++ workaround -bool PUGIXML_FUNCTION operator&&(const xml_attribute &lhs, bool rhs); -bool PUGIXML_FUNCTION operator||(const xml_attribute &lhs, bool rhs); -#endif - -// A light-weight handle for manipulating nodes in DOM tree -class PUGIXML_CLASS xml_node -{ - friend class xml_attribute_iterator; - friend class xml_node_iterator; - friend class xml_named_node_iterator; - - protected: - xml_node_struct *_root; - - typedef void (*unspecified_bool_type)(xml_node ***); - - public: - // Default constructor. Constructs an empty node. - xml_node(); - - // Constructs node from internal pointer - explicit xml_node(xml_node_struct *p); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped node pointers) - bool operator==(const xml_node &r) const; - bool operator!=(const xml_node &r) const; - bool operator<(const xml_node &r) const; - bool operator>(const xml_node &r) const; - bool operator<=(const xml_node &r) const; - bool operator>=(const xml_node &r) const; - - // Check if node is empty. - bool empty() const; - - // Get node type - xml_node_type type() const; - - // Get node name, or "" if node is empty or it has no name - const char_t *name() const; - - // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access - // text inside nodes. - const char_t *value() const; - - // Get attribute list - xml_attribute first_attribute() const; - xml_attribute last_attribute() const; - - // Get children list - xml_node first_child() const; - xml_node last_child() const; - - // Get next/previous sibling in the children list of the parent node - xml_node next_sibling() const; - xml_node previous_sibling() const; - - // Get parent node - xml_node parent() const; - - // Get root of DOM tree this node belongs to - xml_node root() const; - - // Get text object for the current node - xml_text text() const; - - // Get child, attribute or next/previous sibling with the specified name - xml_node child(const char_t *name) const; - xml_attribute attribute(const char_t *name) const; - xml_node next_sibling(const char_t *name) const; - xml_node previous_sibling(const char_t *name) const; - - // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA - const char_t *child_value() const; - - // Get child value of child with specified name. Equivalent to child(name).child_value(). - const char_t *child_value(const char_t *name) const; - - // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) - bool set_name(const char_t *rhs); - bool set_value(const char_t *rhs); - - // Add attribute with specified name. Returns added attribute, or empty attribute on errors. - xml_attribute append_attribute(const char_t *name); - xml_attribute prepend_attribute(const char_t *name); - xml_attribute insert_attribute_after(const char_t *name, const xml_attribute &attr); - xml_attribute insert_attribute_before(const char_t *name, const xml_attribute &attr); - - // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. - xml_attribute append_copy(const xml_attribute &proto); - xml_attribute prepend_copy(const xml_attribute &proto); - xml_attribute insert_copy_after(const xml_attribute &proto, const xml_attribute &attr); - xml_attribute insert_copy_before(const xml_attribute &proto, const xml_attribute &attr); - - // Add child node with specified type. Returns added node, or empty node on errors. - xml_node append_child(xml_node_type type = node_element); - xml_node prepend_child(xml_node_type type = node_element); - xml_node insert_child_after(xml_node_type type, const xml_node &node); - xml_node insert_child_before(xml_node_type type, const xml_node &node); - - // Add child element with specified name. Returns added node, or empty node on errors. - xml_node append_child(const char_t *name); - xml_node prepend_child(const char_t *name); - xml_node insert_child_after(const char_t *name, const xml_node &node); - xml_node insert_child_before(const char_t *name, const xml_node &node); - - // Add a copy of the specified node as a child. Returns added node, or empty node on errors. - xml_node append_copy(const xml_node &proto); - xml_node prepend_copy(const xml_node &proto); - xml_node insert_copy_after(const xml_node &proto, const xml_node &node); - xml_node insert_copy_before(const xml_node &proto, const xml_node &node); - - // Remove specified attribute - bool remove_attribute(const xml_attribute &a); - bool remove_attribute(const char_t *name); - - // Remove specified child - bool remove_child(const xml_node &n); - bool remove_child(const char_t *name); - - // Parses buffer as an XML document fragment and appends all nodes as children of the current node. - // Copies/converts the buffer, so it may be deleted or changed after the function returns. - // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes - // does not immediately reclaim that memory. - xml_parse_result append_buffer(const void *contents, size_t size, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - - // Find attribute using predicate. Returns first attribute for which predicate returned true. - template - xml_attribute find_attribute(Predicate pred) const - { - if (!_root) - return xml_attribute(); - - for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) - if (pred(attrib)) - return attrib; - - return xml_attribute(); - } - - // Find child node using predicate. Returns first child for which predicate returned true. - template - xml_node find_child(Predicate pred) const - { - if (!_root) - return xml_node(); - - for (xml_node node = first_child(); node; node = node.next_sibling()) - if (pred(node)) - return node; - - return xml_node(); - } - - // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate - // returned true. - template - xml_node find_node(Predicate pred) const - { - if (!_root) - return xml_node(); - - xml_node cur = first_child(); - - while (cur._root && cur._root != _root) - { - if (pred(cur)) - return cur; - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur._root != _root) - cur = cur.parent(); - - if (cur._root != _root) - cur = cur.next_sibling(); - } - } - - return xml_node(); - } - - // Find child node by attribute name/value - xml_node find_child_by_attribute(const char_t *name, const char_t *attr_name, const char_t *attr_value) const; - xml_node find_child_by_attribute(const char_t *attr_name, const char_t *attr_value) const; - -#ifndef PUGIXML_NO_STL - // Get the absolute node path from root as a text string. - string_t path(char_t delimiter = '/') const; -#endif - - // Search for a node by path consisting of node names and . or .. elements. - xml_node first_element_by_path(const char_t *path, char_t delimiter = '/') const; - - // Recursively traverse subtree with xml_tree_walker - bool traverse(xml_tree_walker &walker); - -#ifndef PUGIXML_NO_XPATH - // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_single_node(const char_t *query, xpath_variable_set *variables = 0) const; - xpath_node select_single_node(const xpath_query &query) const; - - // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t *query, xpath_variable_set *variables = 0) const; - xpath_node_set select_nodes(const xpath_query &query) const; -#endif - - // Print subtree using a writer object - void print(xml_writer &writer, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, - xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - -#ifndef PUGIXML_NO_STL - // Print subtree to stream - void print(std::basic_ostream> &os, const char_t *indent = PUGIXML_TEXT("\t"), - unsigned int flags = format_default, xml_encoding encoding = encoding_auto, - unsigned int depth = 0) const; - void print(std::basic_ostream> &os, const char_t *indent = PUGIXML_TEXT("\t"), - unsigned int flags = format_default, unsigned int depth = 0) const; -#endif - - // Child nodes iterators - typedef xml_node_iterator iterator; - - iterator begin() const; - iterator end() const; - - // Attribute iterators - typedef xml_attribute_iterator attribute_iterator; - - attribute_iterator attributes_begin() const; - attribute_iterator attributes_end() const; - - // Range-based for support - xml_object_range children() const; - xml_object_range children(const char_t *name) const; - xml_object_range attributes() const; - - // Get node offset in parsed file/string (in char_t units) for debugging purposes - ptrdiff_t offset_debug() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_node_struct *internal_object() const; -}; - -#ifdef __BORLANDC__ -// Borland C++ workaround -bool PUGIXML_FUNCTION operator&&(const xml_node &lhs, bool rhs); -bool PUGIXML_FUNCTION operator||(const xml_node &lhs, bool rhs); -#endif - -// A helper for working with text inside PCDATA nodes -class PUGIXML_CLASS xml_text -{ - friend class xml_node; - - xml_node_struct *_root; - - typedef void (*unspecified_bool_type)(xml_text ***); - - explicit xml_text(xml_node_struct *root); - - xml_node_struct *_data_new(); - xml_node_struct *_data() const; - - public: - // Default constructor. Constructs an empty object. - xml_text(); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Check if text object is empty - bool empty() const; - - // Get text, or "" if object is empty - const char_t *get() const; - - // Get text, or the default value if object is empty - const char_t *as_string(const char_t *def = PUGIXML_TEXT("")) const; - - // Get text as a number, or the default value if conversion did not succeed or object is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - -#ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; -#endif - - // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty - bool as_bool(bool def = false) const; - - // Set text (returns false if object is empty or there is not enough memory) - bool set(const char_t *rhs); - - // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set(int rhs); - bool set(unsigned int rhs); - bool set(double rhs); - bool set(bool rhs); - -#ifdef PUGIXML_HAS_LONG_LONG - bool set(long long rhs); - bool set(unsigned long long rhs); -#endif - - // Set text (equivalent to set without error checking) - xml_text &operator=(const char_t *rhs); - xml_text &operator=(int rhs); - xml_text &operator=(unsigned int rhs); - xml_text &operator=(double rhs); - xml_text &operator=(bool rhs); - -#ifdef PUGIXML_HAS_LONG_LONG - xml_text &operator=(long long rhs); - xml_text &operator=(unsigned long long rhs); -#endif - - // Get the data node (node_pcdata or node_cdata) for this object - xml_node data() const; -}; - -#ifdef __BORLANDC__ -// Borland C++ workaround -bool PUGIXML_FUNCTION operator&&(const xml_text &lhs, bool rhs); -bool PUGIXML_FUNCTION operator||(const xml_text &lhs, bool rhs); -#endif - -// Child node iterator (a bidirectional iterator over a collection of xml_node) -class PUGIXML_CLASS xml_node_iterator -{ - friend class xml_node; - - private: - mutable xml_node _wrap; - xml_node _parent; - - xml_node_iterator(xml_node_struct *ref, xml_node_struct *parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node *pointer; - typedef xml_node &reference; - -#ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; -#endif - - // Default constructor - xml_node_iterator(); - - // Construct an iterator which points to the specified node - xml_node_iterator(const xml_node &node); - - // Iterator operators - bool operator==(const xml_node_iterator &rhs) const; - bool operator!=(const xml_node_iterator &rhs) const; - - xml_node &operator*() const; - xml_node *operator->() const; - - const xml_node_iterator &operator++(); - xml_node_iterator operator++(int); - - const xml_node_iterator &operator--(); - xml_node_iterator operator--(int); -}; - -// Attribute iterator (a bidirectional iterator over a collection of xml_attribute) -class PUGIXML_CLASS xml_attribute_iterator -{ - friend class xml_node; - - private: - mutable xml_attribute _wrap; - xml_node _parent; - - xml_attribute_iterator(xml_attribute_struct *ref, xml_node_struct *parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_attribute value_type; - typedef xml_attribute *pointer; - typedef xml_attribute &reference; - -#ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; -#endif - - // Default constructor - xml_attribute_iterator(); - - // Construct an iterator which points to the specified attribute - xml_attribute_iterator(const xml_attribute &attr, const xml_node &parent); - - // Iterator operators - bool operator==(const xml_attribute_iterator &rhs) const; - bool operator!=(const xml_attribute_iterator &rhs) const; - - xml_attribute &operator*() const; - xml_attribute *operator->() const; - - const xml_attribute_iterator &operator++(); - xml_attribute_iterator operator++(int); - - const xml_attribute_iterator &operator--(); - xml_attribute_iterator operator--(int); -}; - -// Named node range helper -class PUGIXML_CLASS xml_named_node_iterator -{ - friend class xml_node; - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node *pointer; - typedef xml_node &reference; - -#ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; -#endif - - // Default constructor - xml_named_node_iterator(); - - // Construct an iterator which points to the specified node - xml_named_node_iterator(const xml_node &node, const char_t *name); - - // Iterator operators - bool operator==(const xml_named_node_iterator &rhs) const; - bool operator!=(const xml_named_node_iterator &rhs) const; - - xml_node &operator*() const; - xml_node *operator->() const; - - const xml_named_node_iterator &operator++(); - xml_named_node_iterator operator++(int); - - const xml_named_node_iterator &operator--(); - xml_named_node_iterator operator--(int); - - private: - mutable xml_node _wrap; - xml_node _parent; - const char_t *_name; - - xml_named_node_iterator(xml_node_struct *ref, xml_node_struct *parent, const char_t *name); -}; - -// Abstract tree walker class (see xml_node::traverse) -class PUGIXML_CLASS xml_tree_walker -{ - friend class xml_node; - - private: - int _depth; - - protected: - // Get current traversal depth - int depth() const; - - public: - xml_tree_walker(); - virtual ~xml_tree_walker(); - - // Callback that is called when traversal begins - virtual bool begin(xml_node &node); - - // Callback that is called for each node traversed - virtual bool for_each(xml_node &node) = 0; - - // Callback that is called when traversal ends - virtual bool end(xml_node &node); -}; - -// Parsing status, returned as part of xml_parse_result object -enum xml_parse_status -{ - status_ok = 0, // No error - - status_file_not_found, // File was not found during load_file() - status_io_error, // Error reading from file/stream - status_out_of_memory, // Could not allocate memory - status_internal_error, // Internal error occurred - - status_unrecognized_tag, // Parser could not determine tag type - - status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction - status_bad_comment, // Parsing error occurred while parsing comment - status_bad_cdata, // Parsing error occurred while parsing CDATA section - status_bad_doctype, // Parsing error occurred while parsing document type declaration - status_bad_pcdata, // Parsing error occurred while parsing PCDATA section - status_bad_start_element, // Parsing error occurred while parsing start element tag - status_bad_attribute, // Parsing error occurred while parsing element attribute - status_bad_end_element, // Parsing error occurred while parsing end element tag - status_end_element_mismatch, // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was - // not closed or there was an excessive closing tag) - - status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document - // (exclusive to xml_node::append_buffer) - - status_no_document_element // Parsing resulted in a document without element nodes -}; - -// Parsing result -struct PUGIXML_CLASS xml_parse_result -{ - // Parsing status (see xml_parse_status) - xml_parse_status status; - - // Last parsed offset (in char_t units from start of input data) - ptrdiff_t offset; - - // Source document encoding - xml_encoding encoding; - - // Default constructor, initializes object to failed state - xml_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char *description() const; -}; - -// Document class (DOM tree root) -class PUGIXML_CLASS xml_document : public xml_node -{ - private: - char_t *_buffer; - - char _memory[192]; - - // Non-copyable semantics - xml_document(const xml_document &); - const xml_document &operator=(const xml_document &); - - void create(); - void destroy(); - - public: - // Default constructor, makes empty document - xml_document(); - - // Destructor, invalidates all node/attribute handles to this document - ~xml_document(); - - // Removes all nodes, leaving the empty document - void reset(); - - // Removes all nodes, then copies the entire contents of the specified document - void reset(const xml_document &proto); - -#ifndef PUGIXML_NO_STL - // Load document from stream. - xml_parse_result load(std::basic_istream> &stream, - unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load(std::basic_istream> &stream, - unsigned int options = parse_default); -#endif - - // Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load(const char_t *contents, unsigned int options = parse_default); - - // Load document from file - xml_parse_result load_file(const char *path, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - xml_parse_result load_file(const wchar_t *path, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - - // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function - // returns. - xml_parse_result load_buffer(const void *contents, size_t size, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of - // document data). You should ensure that buffer data will persist throughout the document's lifetime, and free the - // buffer memory manually once document is destroyed. - xml_parse_result load_buffer_inplace(void *contents, size_t size, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of - // document data). You should allocate the buffer with pugixml allocation function; document will free the buffer - // when it is no longer needed (you can't use it anymore). - xml_parse_result load_buffer_inplace_own(void *contents, size_t size, unsigned int options = parse_default, - xml_encoding encoding = encoding_auto); - - // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for - // details). - void save(xml_writer &writer, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, - xml_encoding encoding = encoding_auto) const; - -#ifndef PUGIXML_NO_STL - // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for - // details). - void save(std::basic_ostream> &stream, const char_t *indent = PUGIXML_TEXT("\t"), - unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - void save(std::basic_ostream> &stream, const char_t *indent = PUGIXML_TEXT("\t"), - unsigned int flags = format_default) const; -#endif - - // Save XML to file - bool save_file(const char *path, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, - xml_encoding encoding = encoding_auto) const; - bool save_file(const wchar_t *path, const char_t *indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, - xml_encoding encoding = encoding_auto) const; - - // Get document element - xml_node document_element() const; -}; - -#ifndef PUGIXML_NO_XPATH -// XPath query return type -enum xpath_value_type -{ - xpath_type_none, // Unknown type (query failed to compile) - xpath_type_node_set, // Node set (xpath_node_set) - xpath_type_number, // Number - xpath_type_string, // String - xpath_type_boolean // Boolean -}; - -// XPath parsing result -struct PUGIXML_CLASS xpath_parse_result -{ - // Error message (0 if no error) - const char *error; - - // Last parsed offset (in char_t units from string start) - ptrdiff_t offset; - - // Default constructor, initializes object to failed state - xpath_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char *description() const; -}; - -// A single XPath variable -class PUGIXML_CLASS xpath_variable -{ - friend class xpath_variable_set; - - protected: - xpath_value_type _type; - xpath_variable *_next; - - xpath_variable(); - - // Non-copyable semantics - xpath_variable(const xpath_variable &); - xpath_variable &operator=(const xpath_variable &); - - public: - // Get variable name - const char_t *name() const; - - // Get variable type - xpath_value_type type() const; - - // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is - // returned on type mismatch error - bool get_boolean() const; - double get_number() const; - const char_t *get_string() const; - const xpath_node_set &get_node_set() const; - - // Set variable value; no type conversion is performed, false is returned on type mismatch error - bool set(bool value); - bool set(double value); - bool set(const char_t *value); - bool set(const xpath_node_set &value); -}; - -// A set of XPath variables -class PUGIXML_CLASS xpath_variable_set -{ - private: - xpath_variable *_data[64]; - - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set &); - xpath_variable_set &operator=(const xpath_variable_set &); - - xpath_variable *find(const char_t *name) const; - - public: - // Default constructor/destructor - xpath_variable_set(); - ~xpath_variable_set(); - - // Add a new variable or get the existing one, if the types match - xpath_variable *add(const char_t *name, xpath_value_type type); - - // Set value of an existing variable; no type conversion is performed, false is returned if there is no such - // variable or if types mismatch - bool set(const char_t *name, bool value); - bool set(const char_t *name, double value); - bool set(const char_t *name, const char_t *value); - bool set(const char_t *name, const xpath_node_set &value); - - // Get existing variable by name - xpath_variable *get(const char_t *name); - const xpath_variable *get(const char_t *name) const; -}; - -// A compiled XPath query object -class PUGIXML_CLASS xpath_query -{ - private: - void *_impl; - xpath_parse_result _result; - - typedef void (*unspecified_bool_type)(xpath_query ***); - - // Non-copyable semantics - xpath_query(const xpath_query &); - xpath_query &operator=(const xpath_query &); - - public: - // Construct a compiled object from XPath expression. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t *query, xpath_variable_set *variables = 0); - - // Destructor - ~xpath_query(); - - // Get query expression return type - xpath_value_type return_type() const; - - // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - bool evaluate_boolean(const xpath_node &n) const; - - // Evaluate expression as double value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - double evaluate_number(const xpath_node &n) const; - -#ifndef PUGIXML_NO_STL - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - string_t evaluate_string(const xpath_node &n) const; -#endif - - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // At most capacity characters are written to the destination buffer, full result size is returned (includes - // terminating zero). If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. If - // PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. - size_t evaluate_string(char_t *buffer, size_t capacity, const xpath_node &n) const; - - // Evaluate expression as node set in the specified context. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of - // memory errors. If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. - xpath_node_set evaluate_node_set(const xpath_node &n) const; - - // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) - const xpath_parse_result &result() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; -}; - -#ifndef PUGIXML_NO_EXCEPTIONS -// XPath exception class -class PUGIXML_CLASS xpath_exception : public std::exception -{ - private: - xpath_parse_result _result; - - public: - // Construct exception from parse result - explicit xpath_exception(const xpath_parse_result &result); - - // Get error message - virtual const char *what() const throw(); - - // Get parse result - const xpath_parse_result &result() const; -}; -#endif - -// XPath node class (either xml_node or xml_attribute) -class PUGIXML_CLASS xpath_node -{ - private: - xml_node _node; - xml_attribute _attribute; - - typedef void (*unspecified_bool_type)(xpath_node ***); - - public: - // Default constructor; constructs empty XPath node - xpath_node(); - - // Construct XPath node from XML node/attribute - xpath_node(const xml_node &node); - xpath_node(const xml_attribute &attribute, const xml_node &parent); - - // Get node/attribute, if any - xml_node node() const; - xml_attribute attribute() const; - - // Get parent of contained node/attribute - xml_node parent() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators - bool operator==(const xpath_node &n) const; - bool operator!=(const xpath_node &n) const; -}; - -#ifdef __BORLANDC__ -// Borland C++ workaround -bool PUGIXML_FUNCTION operator&&(const xpath_node &lhs, bool rhs); -bool PUGIXML_FUNCTION operator||(const xpath_node &lhs, bool rhs); -#endif - -// A fixed-size collection of XPath nodes -class PUGIXML_CLASS xpath_node_set -{ - public: - // Collection type - enum type_t - { - type_unsorted, // Not ordered - type_sorted, // Sorted by document order (ascending) - type_sorted_reverse // Sorted by document order (descending) - }; - - // Constant iterator type - typedef const xpath_node *const_iterator; - - // Default constructor. Constructs empty set. - xpath_node_set(); - - // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided - // type, so be careful - xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); - - // Destructor - ~xpath_node_set(); - - // Copy constructor/assignment operator - xpath_node_set(const xpath_node_set &ns); - xpath_node_set &operator=(const xpath_node_set &ns); - - // Get collection type - type_t type() const; - - // Get collection size - size_t size() const; - - // Indexing operator - const xpath_node &operator[](size_t index) const; - - // Collection iterators - const_iterator begin() const; - const_iterator end() const; - - // Sort the collection in ascending/descending order by document order - void sort(bool reverse = false); - - // Get first node in the collection by document order - xpath_node first() const; - - // Check if collection is empty - bool empty() const; - - private: - type_t _type; - - xpath_node _storage; - - xpath_node *_begin; - xpath_node *_end; - - void _assign(const_iterator begin, const_iterator end); -}; -#endif - -#ifndef PUGIXML_NO_STL -// Convert wide string to UTF8 -std::basic_string, std::allocator> PUGIXML_FUNCTION as_utf8(const wchar_t *str); -std::basic_string, std::allocator> PUGIXML_FUNCTION -as_utf8(const std::basic_string, std::allocator> &str); - -// Convert UTF8 to wide string -std::basic_string, std::allocator> PUGIXML_FUNCTION -as_wide(const char *str); -std::basic_string, std::allocator> PUGIXML_FUNCTION -as_wide(const std::basic_string, std::allocator> &str); -#endif - -// Memory allocation function interface; returns pointer to allocated memory or NULL on failure -typedef void *(*allocation_function)(size_t size); - -// Memory deallocation function interface -typedef void (*deallocation_function)(void *ptr); - -// Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied -// functions. -void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - -// Get current memory management functions -allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); -deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); -} // namespace pugi - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ -// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) -std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator &); -std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator &); -std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator &); -} // namespace std -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ -// Workarounds for (non-standard) iterator category detection -std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator &); -std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator &); -std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator &); -} // namespace std -#endif - -#endif - -/** - * Copyright (c) 2006-2014 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/include/etiss/fault/xml/readme_pugixml b/include/etiss/fault/xml/readme_pugixml deleted file mode 100644 index 1d28b5f2e6..0000000000 --- a/include/etiss/fault/xml/readme_pugixml +++ /dev/null @@ -1 +0,0 @@ -The files pugiconfig.hpp,pugixml.hpp and pugixml.cpp (located in src) are from the v1.4 tag of the pugixml git repository (https://github.com/zeux/pugixml.git) diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 4149ecf425..cef5b7879b 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -249,7 +249,7 @@ int32_t Action::getEvent() const std::string Action::toString() const { pugi::xml_document doc; - doc.load(""); + doc.load_string(""); etiss::fault::xml::Diagnostics diag; diff --git a/src/fault/Fault.cpp b/src/fault/Fault.cpp index 9dd2e7fac1..8efc103e40 100644 --- a/src/fault/Fault.cpp +++ b/src/fault/Fault.cpp @@ -47,7 +47,7 @@ #include "etiss/fault/Injector.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/Stressor.h" -#include "etiss/fault/xml/pugixml.hpp" +#include "pugixml.hpp" #else #include "fault/Fault.h" #include "fault/Trigger.h" @@ -55,7 +55,7 @@ #include "fault/Injector.h" #include "fault/InjectorAddress.h" #include "fault/Stressor.h" -#include "fault/xml/pugixml.hpp" +#include "pugixml.hpp" #endif #if MUTEX_SUPPORTED @@ -268,7 +268,7 @@ std::string Fault::toString() const { pugi::xml_document doc; - doc.load(""); + doc.load_string(""); etiss::fault::xml::Diagnostics diag; @@ -304,7 +304,7 @@ std::string FaultRef::toString() const { pugi::xml_document doc; - doc.load(""); + doc.load_string(""); etiss::fault::xml::Diagnostics diag; @@ -358,7 +358,7 @@ bool writeXML(const std::vector &vec, std::ostream &out, std::ostream &di { pugi::xml_document doc; - doc.load(""); + doc.load_string(""); etiss::fault::xml::Diagnostics diag; diff --git a/src/fault/xml/CMakeLists.txt b/src/fault/xml/CMakeLists.txt deleted file mode 100644 index aab0410dff..0000000000 --- a/src/fault/xml/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright 2018 Infineon Technologies AG -# -# This file is part of ETISS tool, see -# -# The initial version of this software has been created with the funding support by the German Federal -# Ministry of Education and Research(BMBF) in the project EffektiV under grant 01IS13022. -# -# Redistribution and use in source and binary forms, with or without modification, are permitted -# provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and -# the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions -# and the following disclaimer in the documentation and / or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse -# or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# -# Author: Marc Greim , Chair of Electronic Design Automation, TUM -# -# Version 0.1 -# - -### recurse into sub directories -unset(LOCAL_SUBDIRS) -FILE(GLOB LOCAL_SUBDIRS ./ *) -foreach(subdir ${LOCAL_SUBDIRS}) - if(IS_DIRECTORY ${subdir}) - add_subdirectory(${subdir}) - endif() -endforeach() - - -unset(LOCAL_SOURCE) -FILE(GLOB LOCAL_SOURCE *.cpp) - -set(ETISS_SOURCE ${ETISS_SOURCE} ${LOCAL_SOURCE} PARENT_SCOPE) diff --git a/src/fault/xml/pugixml.cpp b/src/fault/xml/pugixml.cpp deleted file mode 100644 index c9cdb8a781..0000000000 --- a/src/fault/xml/pugixml.cpp +++ /dev/null @@ -1,11229 +0,0 @@ -/** - * pugixml parser - version 1.4 - * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef SOURCE_PUGIXML_CPP -#define SOURCE_PUGIXML_CPP - -#ifndef NO_ETISS -#include "etiss/fault/xml/pugixml.hpp" -#else -#include "fault/xml/pugixml.hpp" -#endif -#include -#include -#include -#include - -#ifdef PUGIXML_WCHAR_MODE -#include -#endif - -#ifndef PUGIXML_NO_XPATH -#include -#include -#ifdef PUGIXML_NO_EXCEPTIONS -#include -#endif -#endif - -#ifndef PUGIXML_NO_STL -#include -#include -#include -#endif - -// For placement new -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4324) // structure was padded due to __declspec(align()) -#pragma warning(disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable -#pragma warning(disable : 4702) // unreachable code -#pragma warning(disable : 4996) // this function or variable may be unsafe -#pragma warning(disable : 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged -#endif - -#ifdef __INTEL_COMPILER -#pragma warning(disable : 177) // function was declared but never referenced -#pragma warning(disable : 279) // controlling expression is constant -#pragma warning(disable : 1478 1786) // function was declared "deprecated" -#pragma warning(disable : 1684) // conversion from pointer to same-sized integral type -#endif - -#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) -#pragma warn - 8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the - // warning go away -#endif - -#ifdef __BORLANDC__ -#pragma option push -#pragma warn - 8008 // condition is always false -#pragma warn - 8066 // unreachable code -#endif - -#ifdef __SNC__ -// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug -#pragma diag_suppress = 178 // function was declared but never referenced -#pragma diag_suppress = 237 // controlling expression is constant -#endif - -// Inlining controls -#if defined(_MSC_VER) && _MSC_VER >= 1300 -#define PUGI__NO_INLINE __declspec(noinline) -#elif defined(__GNUC__) -#define PUGI__NO_INLINE __attribute__((noinline)) -#else -#define PUGI__NO_INLINE -#endif - -// Simple static assertion -#define PUGI__STATIC_ASSERT(cond) \ - { \ - static const char condition_failed[(cond) ? 1 : -1] = { 0 }; \ - (void)condition_failed[0]; \ - } - -// Digital Mars C++ bug workaround for passing char loaded from memory via stack -#ifdef __DMC__ -#define PUGI__DMC_VOLATILE volatile -#else -#define PUGI__DMC_VOLATILE -#endif - -// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy -// because some compilers don't have it at all) -#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) -using std::memcpy; -using std::memmove; -#endif - -// In some environments MSVC is a compiler but the CRT lacks certain -// MSVC-specific features -#if defined(_MSC_VER) && !defined(__S3E__) -#define PUGI__MSVC_CRT_VERSION _MSC_VER -#endif - -#ifdef PUGIXML_HEADER_ONLY -#define PUGI__NS_BEGIN \ - namespace pugi \ - { \ - namespace impl \ - { -#define PUGI__NS_END \ - } \ - } -#define PUGI__FN inline -#define PUGI__FN_NO_INLINE inline -#else -#if defined(_MSC_VER) && \ - _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces -#define PUGI__NS_BEGIN \ - namespace pugi \ - { \ - namespace impl \ - { -#define PUGI__NS_END \ - } \ - } -#else -#define PUGI__NS_BEGIN \ - namespace pugi \ - { \ - namespace impl \ - { \ - namespace \ - { -#define PUGI__NS_END \ - } \ - } \ - } -#endif -#define PUGI__FN -#define PUGI__FN_NO_INLINE PUGI__NO_INLINE -#endif - -// uintptr_t -#if !defined(_MSC_VER) || _MSC_VER >= 1600 -#include -#else -#ifndef _UINTPTR_T_DEFINED -// No native uintptr_t in MSVC6 and in some WinCE versions -typedef size_t uintptr_t; -#define _UINTPTR_T_DEFINED -#endif -PUGI__NS_BEGIN -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -PUGI__NS_END -#endif - -// Memory allocation -PUGI__NS_BEGIN -PUGI__FN void *default_allocate(size_t size) -{ - return malloc(size); -} - -PUGI__FN void default_deallocate(void *ptr) -{ - free(ptr); -} - -template -struct xml_memory_management_function_storage -{ - static allocation_function allocate; - static deallocation_function deallocate; -}; - -template -allocation_function xml_memory_management_function_storage::allocate = default_allocate; -template -deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; - -typedef xml_memory_management_function_storage xml_memory; -PUGI__NS_END - -// String utilities -PUGI__NS_BEGIN -// Get string length -PUGI__FN size_t strlength(const char_t *s) -{ - assert(s); - -#ifdef PUGIXML_WCHAR_MODE - return wcslen(s); -#else - return strlen(s); -#endif -} - -// Compare two strings -PUGI__FN bool strequal(const char_t *src, const char_t *dst) -{ - assert(src && dst); - -#ifdef PUGIXML_WCHAR_MODE - return wcscmp(src, dst) == 0; -#else - return strcmp(src, dst) == 0; -#endif -} - -// Compare lhs with [rhs_begin, rhs_end) -PUGI__FN bool strequalrange(const char_t *lhs, const char_t *rhs, size_t count) -{ - for (size_t i = 0; i < count; ++i) - if (lhs[i] != rhs[i]) - return false; - - return lhs[count] == 0; -} - -// Get length of wide string, even if CRT lacks wide character support -PUGI__FN size_t strlength_wide(const wchar_t *s) -{ - assert(s); - -#ifdef PUGIXML_WCHAR_MODE - return wcslen(s); -#else - const wchar_t *end = s; - while (*end) - end++; - return static_cast(end - s); -#endif -} - -#ifdef PUGIXML_WCHAR_MODE -// Convert string to wide string, assuming all symbols are ASCII -PUGI__FN void widen_ascii(wchar_t *dest, const char *source) -{ - for (const char *i = source; *i; ++i) - *dest++ = *i; - *dest = 0; -} -#endif -PUGI__NS_END - -#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) -// auto_ptr-like buffer holder for exception recovery -PUGI__NS_BEGIN -struct buffer_holder -{ - void *data; - void (*deleter)(void *); - - buffer_holder(void *data_, void (*deleter_)(void *)) : data(data_), deleter(deleter_) {} - - ~buffer_holder() - { - if (data) - deleter(data); - } - - void *release() - { - void *result = data; - data = 0; - return result; - } -}; -PUGI__NS_END -#endif - -PUGI__NS_BEGIN -static const size_t xml_memory_page_size = -#ifdef PUGIXML_MEMORY_PAGE_SIZE - PUGIXML_MEMORY_PAGE_SIZE -#else - 32768 -#endif - ; - -static const uintptr_t xml_memory_page_alignment = 32; -static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); -static const uintptr_t xml_memory_page_name_allocated_mask = 16; -static const uintptr_t xml_memory_page_value_allocated_mask = 8; -static const uintptr_t xml_memory_page_type_mask = 7; - -struct xml_allocator; - -struct xml_memory_page -{ - static xml_memory_page *construct(void *memory) - { - if (!memory) - return 0; //$ redundant, left for performance - - xml_memory_page *result = static_cast(memory); - - result->allocator = 0; - result->memory = 0; - result->prev = 0; - result->next = 0; - result->busy_size = 0; - result->freed_size = 0; - - return result; - } - - xml_allocator *allocator; - - void *memory; - - xml_memory_page *prev; - xml_memory_page *next; - - size_t busy_size; - size_t freed_size; - - char data[1]; -}; - -struct xml_memory_string_header -{ - uint16_t page_offset; // offset from page->data - uint16_t full_size; // 0 if string occupies whole page -}; - -struct xml_allocator -{ - xml_allocator(xml_memory_page *root) : _root(root), _busy_size(root->busy_size) {} - - xml_memory_page *allocate_page(size_t data_size) - { - size_t size = offsetof(xml_memory_page, data) + data_size; - - // allocate block with some alignment, leaving memory for worst-case padding - void *memory = xml_memory::allocate(size + xml_memory_page_alignment); - if (!memory) - return 0; - - // align upwards to page boundary - void *page_memory = reinterpret_cast( - (reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); - - // prepare page structure - xml_memory_page *page = xml_memory_page::construct(page_memory); - assert(page); - - page->memory = memory; - page->allocator = _root->allocator; - - return page; - } - - static void deallocate_page(xml_memory_page *page) { xml_memory::deallocate(page->memory); } - - void *allocate_memory_oob(size_t size, xml_memory_page *&out_page); - - void *allocate_memory(size_t size, xml_memory_page *&out_page) - { - if (_busy_size + size > xml_memory_page_size) - return allocate_memory_oob(size, out_page); - - void *buf = _root->data + _busy_size; - - _busy_size += size; - - out_page = _root; - - return buf; - } - - void deallocate_memory(void *ptr, size_t size, xml_memory_page *page) - { - if (page == _root) - page->busy_size = _busy_size; - - assert(ptr >= page->data && ptr < page->data + page->busy_size); - (void)!ptr; - - page->freed_size += size; - assert(page->freed_size <= page->busy_size); - - if (page->freed_size == page->busy_size) - { - if (page->next == 0) - { - assert(_root == page); - - // top page freed, just reset sizes - page->busy_size = page->freed_size = 0; - _busy_size = 0; - } - else - { - assert(_root != page); - assert(page->prev); - - // remove from the list - page->prev->next = page->next; - page->next->prev = page->prev; - - // deallocate - deallocate_page(page); - } - } - } - - char_t *allocate_string(size_t length) - { - // allocate memory for string and header block - size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); - - // round size up to pointer alignment boundary - size_t full_size = (size + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1); - - xml_memory_page *page; - xml_memory_string_header *header = static_cast(allocate_memory(full_size, page)); - - if (!header) - return 0; - - // setup header - ptrdiff_t page_offset = reinterpret_cast(header) - page->data; - - assert(page_offset >= 0 && page_offset < (1 << 16)); - header->page_offset = static_cast(page_offset); - - // full_size == 0 for large strings that occupy the whole page - assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); - header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0); - - // round-trip through void* to avoid 'cast increases required alignment of target type' warning - // header is guaranteed a pointer-sized alignment, which should be enough for char_t - return static_cast(static_cast(header + 1)); - } - - void deallocate_string(char_t *string) - { - // this function casts pointers through void* to avoid 'cast increases required alignment of target type' - // warnings we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via - // allocate_string - - // get header - xml_memory_string_header *header = static_cast(static_cast(string)) - 1; - - // deallocate - size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; - xml_memory_page *page = - reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); - - // if full_size == 0 then this string occupies the whole page - size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size; - - deallocate_memory(header, full_size, page); - } - - xml_memory_page *_root; - size_t _busy_size; -}; - -PUGI__FN_NO_INLINE void *xml_allocator::allocate_memory_oob(size_t size, xml_memory_page *&out_page) -{ - const size_t large_allocation_threshold = xml_memory_page_size / 4; - - xml_memory_page *page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); - out_page = page; - - if (!page) - return 0; - - if (size <= large_allocation_threshold) - { - _root->busy_size = _busy_size; - - // insert page at the end of linked list - page->prev = _root; - _root->next = page; - _root = page; - - _busy_size = size; - } - else - { - // insert page before the end of linked list, so that it is deleted as soon as possible - // the last page is not deleted even if it's empty (see deallocate_memory) - assert(_root->prev); - - page->prev = _root->prev; - page->next = _root; - - _root->prev->next = page; - _root->prev = page; - } - - // allocate inside page - page->busy_size = size; - - return page->data; -} -PUGI__NS_END - -namespace pugi -{ -/// A 'name=value' XML attribute structure. -struct xml_attribute_struct -{ - /// Default ctor - xml_attribute_struct(impl::xml_memory_page *page) - : header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) - { - } - - uintptr_t header; - - char_t *name; ///< Pointer to attribute name. - char_t *value; ///< Pointer to attribute value. - - xml_attribute_struct *prev_attribute_c; ///< Previous attribute (cyclic list) - xml_attribute_struct *next_attribute; ///< Next attribute -}; - -/// An XML document tree node. -struct xml_node_struct -{ - /// Default ctor - /// \param type - node type - xml_node_struct(impl::xml_memory_page *page, xml_node_type type) - : header(reinterpret_cast(page) | (type - 1)) - , parent(0) - , name(0) - , value(0) - , first_child(0) - , prev_sibling_c(0) - , next_sibling(0) - , first_attribute(0) - { - } - - uintptr_t header; - - xml_node_struct *parent; ///< Pointer to parent - - char_t *name; ///< Pointer to element name. - char_t *value; ///< Pointer to any associated string data. - - xml_node_struct *first_child; ///< First child - - xml_node_struct *prev_sibling_c; ///< Left brother (cyclic list) - xml_node_struct *next_sibling; ///< Right brother - - xml_attribute_struct *first_attribute; ///< First attribute -}; -} // namespace pugi - -PUGI__NS_BEGIN -struct xml_extra_buffer -{ - char_t *buffer; - xml_extra_buffer *next; -}; - -struct xml_document_struct : public xml_node_struct, public xml_allocator -{ - xml_document_struct(xml_memory_page *page) - : xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) - { - } - - const char_t *buffer; - - xml_extra_buffer *extra_buffers; -}; - -inline xml_allocator &get_allocator(const xml_node_struct *node) -{ - assert(node); - - return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; -} -PUGI__NS_END - -// Low-level DOM operations -PUGI__NS_BEGIN -inline xml_attribute_struct *allocate_attribute(xml_allocator &alloc) -{ - xml_memory_page *page; - void *memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); - - return new (memory) xml_attribute_struct(page); -} - -inline xml_node_struct *allocate_node(xml_allocator &alloc, xml_node_type type) -{ - xml_memory_page *page; - void *memory = alloc.allocate_memory(sizeof(xml_node_struct), page); - - return new (memory) xml_node_struct(page, type); -} - -inline void destroy_attribute(xml_attribute_struct *a, xml_allocator &alloc) -{ - uintptr_t header = a->header; - - if (header & impl::xml_memory_page_name_allocated_mask) - alloc.deallocate_string(a->name); - if (header & impl::xml_memory_page_value_allocated_mask) - alloc.deallocate_string(a->value); - - alloc.deallocate_memory(a, sizeof(xml_attribute_struct), - reinterpret_cast(header & xml_memory_page_pointer_mask)); -} - -inline void destroy_node(xml_node_struct *n, xml_allocator &alloc) -{ - uintptr_t header = n->header; - - if (header & impl::xml_memory_page_name_allocated_mask) - alloc.deallocate_string(n->name); - if (header & impl::xml_memory_page_value_allocated_mask) - alloc.deallocate_string(n->value); - - for (xml_attribute_struct *attr = n->first_attribute; attr;) - { - xml_attribute_struct *next = attr->next_attribute; - - destroy_attribute(attr, alloc); - - attr = next; - } - - for (xml_node_struct *child = n->first_child; child;) - { - xml_node_struct *next = child->next_sibling; - - destroy_node(child, alloc); - - child = next; - } - - alloc.deallocate_memory(n, sizeof(xml_node_struct), - reinterpret_cast(header & xml_memory_page_pointer_mask)); -} - -PUGI__FN_NO_INLINE xml_node_struct *append_node(xml_node_struct *node, xml_allocator &alloc, - xml_node_type type = node_element) -{ - xml_node_struct *child = allocate_node(alloc, type); - if (!child) - return 0; - - child->parent = node; - - xml_node_struct *first_child = node->first_child; - - if (first_child) - { - xml_node_struct *last_child = first_child->prev_sibling_c; - - last_child->next_sibling = child; - child->prev_sibling_c = last_child; - first_child->prev_sibling_c = child; - } - else - { - node->first_child = child; - child->prev_sibling_c = child; - } - - return child; -} - -PUGI__FN_NO_INLINE xml_attribute_struct *append_attribute_ll(xml_node_struct *node, xml_allocator &alloc) -{ - xml_attribute_struct *a = allocate_attribute(alloc); - if (!a) - return 0; - - xml_attribute_struct *first_attribute = node->first_attribute; - - if (first_attribute) - { - xml_attribute_struct *last_attribute = first_attribute->prev_attribute_c; - - last_attribute->next_attribute = a; - a->prev_attribute_c = last_attribute; - first_attribute->prev_attribute_c = a; - } - else - { - node->first_attribute = a; - a->prev_attribute_c = a; - } - - return a; -} -PUGI__NS_END - -// Helper classes for code generation -PUGI__NS_BEGIN -struct opt_false -{ - enum - { - value = 0 - }; -}; - -struct opt_true -{ - enum - { - value = 1 - }; -}; -PUGI__NS_END - -// Unicode utilities -PUGI__NS_BEGIN -inline uint16_t endian_swap(uint16_t value) -{ - return static_cast(((value & 0xff) << 8) | (value >> 8)); -} - -inline uint32_t endian_swap(uint32_t value) -{ - return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); -} - -struct utf8_counter -{ - typedef size_t value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) - return result + 1; - // U+0080..U+07FF - else if (ch < 0x800) - return result + 2; - // U+0800..U+FFFF - else - return result + 3; - } - - static value_type high(value_type result, uint32_t) - { - // U+10000..U+10FFFF - return result + 4; - } -}; - -struct utf8_writer -{ - typedef uint8_t *value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) - { - *result = static_cast(ch); - return result + 1; - } - // U+0080..U+07FF - else if (ch < 0x800) - { - result[0] = static_cast(0xC0 | (ch >> 6)); - result[1] = static_cast(0x80 | (ch & 0x3F)); - return result + 2; - } - // U+0800..U+FFFF - else - { - result[0] = static_cast(0xE0 | (ch >> 12)); - result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[2] = static_cast(0x80 | (ch & 0x3F)); - return result + 3; - } - } - - static value_type high(value_type result, uint32_t ch) - { - // U+10000..U+10FFFF - result[0] = static_cast(0xF0 | (ch >> 18)); - result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); - result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[3] = static_cast(0x80 | (ch & 0x3F)); - return result + 4; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } -}; - -struct utf16_counter -{ - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) { return result + 1; } - - static value_type high(value_type result, uint32_t) { return result + 2; } -}; - -struct utf16_writer -{ - typedef uint16_t *value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - uint32_t msh = static_cast(ch - 0x10000) >> 10; - uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; - - result[0] = static_cast(0xD800 + msh); - result[1] = static_cast(0xDC00 + lsh); - - return result + 2; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } -}; - -struct utf32_counter -{ - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) { return result + 1; } - - static value_type high(value_type result, uint32_t) { return result + 1; } -}; - -struct utf32_writer -{ - typedef uint32_t *value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type any(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } -}; - -struct latin1_writer -{ - typedef uint8_t *value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch > 255 ? '?' : ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - (void)ch; - - *result = '?'; - - return result + 1; - } -}; - -template -struct wchar_selector; - -template <> -struct wchar_selector<2> -{ - typedef uint16_t type; - typedef utf16_counter counter; - typedef utf16_writer writer; -}; - -template <> -struct wchar_selector<4> -{ - typedef uint32_t type; - typedef utf32_counter counter; - typedef utf32_writer writer; -}; - -typedef wchar_selector::counter wchar_counter; -typedef wchar_selector::writer wchar_writer; - -template -struct utf_decoder -{ - static inline typename Traits::value_type decode_utf8_block(const uint8_t *data, size_t size, - typename Traits::value_type result) - { - const uint8_t utf8_byte_mask = 0x3f; - - while (size) - { - uint8_t lead = *data; - - // 0xxxxxxx -> U+0000..U+007F - if (lead < 0x80) - { - result = Traits::low(result, lead); - data += 1; - size -= 1; - - // process aligned single-byte (ascii) blocks - if ((reinterpret_cast(data) & 3) == 0) - { - // round-trip through void* to silence 'cast increases required alignment of target type' warnings - while (size >= 4 && - (*static_cast(static_cast(data)) & 0x80808080) == 0) - { - result = Traits::low(result, data[0]); - result = Traits::low(result, data[1]); - result = Traits::low(result, data[2]); - result = Traits::low(result, data[3]); - data += 4; - size -= 4; - } - } - } - // 110xxxxx -> U+0080..U+07FF - else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); - data += 2; - size -= 2; - } - // 1110xxxx -> U+0800-U+FFFF - else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && - (data[2] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | - (data[2] & utf8_byte_mask)); - data += 3; - size -= 3; - } - // 11110xxx -> U+10000..U+10FFFF - else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && - (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) - { - result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | - ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); - data += 4; - size -= 4; - } - // 10xxxxxx or 11111xxx -> invalid - else - { - data += 1; - size -= 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf16_block(const uint16_t *data, size_t size, - typename Traits::value_type result) - { - const uint16_t *end = data + size; - - while (data < end) - { - unsigned int lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+D7FF - if (lead < 0xD800) - { - result = Traits::low(result, lead); - data += 1; - } - // U+E000..U+FFFF - else if (static_cast(lead - 0xE000) < 0x2000) - { - result = Traits::low(result, lead); - data += 1; - } - // surrogate pair lead - else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) - { - uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; - - if (static_cast(next - 0xDC00) < 0x400) - { - result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); - data += 2; - } - else - { - data += 1; - } - } - else - { - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf32_block(const uint32_t *data, size_t size, - typename Traits::value_type result) - { - const uint32_t *end = data + size; - - while (data < end) - { - uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+FFFF - if (lead < 0x10000) - { - result = Traits::low(result, lead); - data += 1; - } - // U+10000..U+10FFFF - else - { - result = Traits::high(result, lead); - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_latin1_block(const uint8_t *data, size_t size, - typename Traits::value_type result) - { - for (size_t i = 0; i < size; ++i) - { - result = Traits::low(result, data[i]); - } - - return result; - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t *data, size_t size, - typename Traits::value_type result) - { - return decode_utf16_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t *data, size_t size, - typename Traits::value_type result) - { - return decode_utf32_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block(const wchar_t *data, size_t size, - typename Traits::value_type result) - { - return decode_wchar_block_impl(reinterpret_cast::type *>(data), size, - result); - } -}; - -template -PUGI__FN void convert_utf_endian_swap(T *result, const T *data, size_t length) -{ - for (size_t i = 0; i < length; ++i) - result[i] = endian_swap(data[i]); -} - -#ifdef PUGIXML_WCHAR_MODE -PUGI__FN void convert_wchar_endian_swap(wchar_t *result, const wchar_t *data, size_t length) -{ - for (size_t i = 0; i < length; ++i) - result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); -} -#endif -PUGI__NS_END - -PUGI__NS_BEGIN -enum chartype_t -{ - ct_parse_pcdata = 1, // \0, &, \r, < - ct_parse_attr = 2, // \0, &, \r, ', " - ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab - ct_space = 8, // \r, \n, space, tab - ct_parse_cdata = 16, // \0, ], >, \r - ct_parse_comment = 32, // \0, -, >, \r - ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . - ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : -}; - -static const unsigned char chartype_table[256] = { - 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 - - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 -}; - -enum chartypex_t -{ - ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " - ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ - ctx_digit = 8, // 0-9 - ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . -}; - -static const unsigned char chartypex_table[256] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 - - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 - - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 -}; - -#ifdef PUGIXML_WCHAR_MODE -#define PUGI__IS_CHARTYPE_IMPL(c, ct, table) \ - ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) -#else -#define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) -#endif - -#define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) -#define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) - -PUGI__FN bool is_little_endian() -{ - unsigned int ui = 1; - - return *reinterpret_cast(&ui) == 1; -} - -PUGI__FN xml_encoding get_wchar_encoding() -{ - PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); - - if (sizeof(wchar_t) == 2) - return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - else - return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; -} - -PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) -{ - // look for BOM in first few bytes - if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) - return encoding_utf32_be; - if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) - return encoding_utf32_le; - if (d0 == 0xfe && d1 == 0xff) - return encoding_utf16_be; - if (d0 == 0xff && d1 == 0xfe) - return encoding_utf16_le; - if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) - return encoding_utf8; - - // look for <, (contents); - - PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; - - return guess_buffer_encoding(d0, d1, d2, d3); -} - -PUGI__FN bool get_mutable_buffer(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, - bool is_mutable) -{ - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - out_buffer = static_cast(const_cast(contents)); - out_length = length; - } - else - { - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - memcpy(buffer, contents, length * sizeof(char_t)); - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; -} - -#ifdef PUGIXML_WCHAR_MODE -PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) -{ - return (le == encoding_utf16_be && re == encoding_utf16_le) || - (le == encoding_utf16_le && re == encoding_utf16_be) || - (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); -} - -PUGI__FN bool convert_buffer_endian_swap(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, - bool is_mutable) -{ - const char_t *data = static_cast(contents); - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - char_t *buffer = const_cast(data); - - convert_wchar_endian_swap(buffer, data, length); - - out_buffer = buffer; - out_length = length; - } - else - { - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - convert_wchar_endian_swap(buffer, data, length); - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; -} - -PUGI__FN bool convert_buffer_utf8(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size) -{ - const uint8_t *data = static_cast(contents); - size_t data_length = size; - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert utf8 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf8_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -template -PUGI__FN bool convert_buffer_utf16(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, opt_swap) -{ - const uint16_t *data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert utf16 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -template -PUGI__FN bool convert_buffer_utf32(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, opt_swap) -{ - const uint32_t *data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert utf32 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -PUGI__FN bool convert_buffer_latin1(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size) -{ - const uint8_t *data = static_cast(contents); - size_t data_length = size; - - // get length in wchar_t units - size_t length = data_length; - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // convert latin1 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_latin1_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -PUGI__FN bool convert_buffer(char_t *&out_buffer, size_t &out_length, xml_encoding encoding, const void *contents, - size_t size, bool is_mutable) -{ - // get native encoding - xml_encoding wchar_encoding = get_wchar_encoding(); - - // fast path: no conversion required - if (encoding == wchar_encoding) - return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // only endian-swapping is required - if (need_endian_swap_utf(encoding, wchar_encoding)) - return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf8 - if (encoding == encoding_utf8) - return convert_buffer_utf8(out_buffer, out_length, contents, size); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) - : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) - : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) - return convert_buffer_latin1(out_buffer, out_length, contents, size); - - assert(!"Invalid encoding"); - return false; -} -#else -template -PUGI__FN bool convert_buffer_utf16(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, opt_swap) -{ - const uint16_t *data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert utf16 input to utf8 - uint8_t *obegin = reinterpret_cast(buffer); - uint8_t *oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -template -PUGI__FN bool convert_buffer_utf32(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, opt_swap) -{ - const uint32_t *data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert utf32 input to utf8 - uint8_t *obegin = reinterpret_cast(buffer); - uint8_t *oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t *data, size_t size) -{ - for (size_t i = 0; i < size; ++i) - if (data[i] > 127) - return i; - - return size; -} - -PUGI__FN bool convert_buffer_latin1(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, - bool is_mutable) -{ - const uint8_t *data = static_cast(contents); - size_t data_length = size; - - // get size of prefix that does not need utf8 conversion - size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); - assert(prefix_length <= data_length); - - const uint8_t *postfix = data + prefix_length; - size_t postfix_length = data_length - prefix_length; - - // if no conversion is needed, just return the original buffer - if (postfix_length == 0) - return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // first pass: get length in utf8 units - size_t length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); - - // allocate buffer of suitable length - char_t *buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) - return false; - - // second pass: convert latin1 input to utf8 - memcpy(buffer, data, prefix_length); - - uint8_t *obegin = reinterpret_cast(buffer); - uint8_t *oend = utf_decoder::decode_latin1_block(postfix, postfix_length, obegin + prefix_length); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; -} - -PUGI__FN bool convert_buffer(char_t *&out_buffer, size_t &out_length, xml_encoding encoding, const void *contents, - size_t size, bool is_mutable) -{ - // fast path: no conversion required - if (encoding == encoding_utf8) - return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) - : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) - : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) - return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); - - assert(!"Invalid encoding"); - return false; -} -#endif - -PUGI__FN size_t as_utf8_begin(const wchar_t *str, size_t length) -{ - // get length in utf8 characters - return utf_decoder::decode_wchar_block(str, length, 0); -} - -PUGI__FN void as_utf8_end(char *buffer, size_t size, const wchar_t *str, size_t length) -{ - // convert to utf8 - uint8_t *begin = reinterpret_cast(buffer); - uint8_t *end = utf_decoder::decode_wchar_block(str, length, begin); - - assert(begin + size == end); - (void)!end; - - // zero-terminate - buffer[size] = 0; -} - -#ifndef PUGIXML_NO_STL -PUGI__FN std::string as_utf8_impl(const wchar_t *str, size_t length) -{ - // first pass: get length in utf8 characters - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - std::string result; - result.resize(size); - - // second pass: convert to utf8 - if (size > 0) - as_utf8_end(&result[0], size, str, length); - - return result; -} - -PUGI__FN std::basic_string as_wide_impl(const char *str, size_t size) -{ - const uint8_t *data = reinterpret_cast(str); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, size, 0); - - // allocate resulting string - std::basic_string result; - result.resize(length); - - // second pass: convert to wchar_t - if (length > 0) - { - wchar_writer::value_type begin = reinterpret_cast(&result[0]); - wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); - - assert(begin + length == end); - (void)!end; - } - - return result; -} -#endif - -inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t *target) -{ - assert(target); - size_t target_length = strlength(target); - - // always reuse document buffer memory if possible - if (!allocated) - return target_length >= length; - - // reuse heap memory if waste is not too great - const size_t reuse_threshold = 32; - - return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); -} - -PUGI__FN bool strcpy_insitu(char_t *&dest, uintptr_t &header, uintptr_t header_mask, const char_t *source) -{ - assert(header); - - size_t source_length = strlength(source); - - if (source_length == 0) - { - // empty string and null pointer are equivalent, so just deallocate old memory - xml_allocator *alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - if (header & header_mask) - alloc->deallocate_string(dest); - - // mark the string as not allocated - dest = 0; - header &= ~header_mask; - - return true; - } - else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest)) - { - // we can reuse old buffer, so just copy the new data (including zero terminator) - memcpy(dest, source, (source_length + 1) * sizeof(char_t)); - - return true; - } - else - { - xml_allocator *alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - // allocate new buffer - char_t *buf = alloc->allocate_string(source_length + 1); - if (!buf) - return false; - - // copy the string (including zero terminator) - memcpy(buf, source, (source_length + 1) * sizeof(char_t)); - - // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) - if (header & header_mask) - alloc->deallocate_string(dest); - - // the string is now allocated, so set the flag - dest = buf; - header |= header_mask; - - return true; - } -} - -struct gap -{ - char_t *end; - size_t size; - - gap() : end(0), size(0) {} - - // Push new gap, move s count bytes further (skipping the gap). - // Collapse previous gap. - void push(char_t *&s, size_t count) - { - if (end) // there was a gap already; collapse it - { - // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - } - - s += count; // end of current gap - - // "merge" two gaps - end = s; - size += count; - } - - // Collapse all gaps, return past-the-end pointer - char_t *flush(char_t *s) - { - if (end) - { - // Move [old_gap_end, current_pos) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - - return s - size; - } - else - return s; - } -}; - -PUGI__FN char_t *strconv_escape(char_t *s, gap &g) -{ - char_t *stre = s + 1; - - switch (*stre) - { - case '#': // &#... - { - unsigned int ucsc = 0; - - if (stre[1] == 'x') // &#x... (hex code) - { - stre += 2; - - char_t ch = *stre; - - if (ch == ';') - return stre; - - for (;;) - { - if (static_cast(ch - '0') <= 9) - ucsc = 16 * ucsc + (ch - '0'); - else if (static_cast((ch | ' ') - 'a') <= 5) - ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - else // &#... (dec code) - { - char_t ch = *++stre; - - if (ch == ';') - return stre; - - for (;;) - { - if (static_cast(static_cast(ch) - '0') <= 9) - ucsc = 10 * ucsc + (ch - '0'); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - -#ifdef PUGIXML_WCHAR_MODE - s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); -#else - s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); -#endif - - g.push(s, stre - s); - return stre; - } - - case 'a': // &a - { - ++stre; - - if (*stre == 'm') // &am - { - if (*++stre == 'p' && *++stre == ';') // & - { - *s++ = '&'; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - else if (*stre == 'p') // &ap - { - if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' - { - *s++ = '\''; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - break; - } - - case 'g': // &g - { - if (*++stre == 't' && *++stre == ';') // > - { - *s++ = '>'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'l': // &l - { - if (*++stre == 't' && *++stre == ';') // < - { - *s++ = '<'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'q': // &q - { - if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " - { - *s++ = '"'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - default: - break; - } - - return stre; -} - -// Utility macro for last character handling -#define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) - -PUGI__FN char_t *strconv_comment(char_t *s, char_t endch) -{ - gap g; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) - ++s; - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') - g.push(s, 1); - } - else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here - { - *g.flush(s) = 0; - - return s + (s[2] == '>' ? 3 : 2); - } - else if (*s == 0) - { - return 0; - } - else - ++s; - } -} - -PUGI__FN char_t *strconv_cdata(char_t *s, char_t endch) -{ - gap g; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) - ++s; - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') - g.push(s, 1); - } - else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == 0) - { - return 0; - } - else - ++s; - } -} - -typedef char_t *(*strconv_pcdata_t)(char_t *); - -template -struct strconv_pcdata_impl -{ - static char_t *parse(char_t *s) - { - gap g; - - char_t *begin = s; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) - ++s; - - if (*s == '<') // PCDATA ends here - { - char_t *end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s + 1; - } - else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') - g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (*s == 0) - { - char_t *end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s; - } - else - ++s; - } - } -}; - -PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) -{ - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) - { - case 0: - return strconv_pcdata_impl::parse; - case 1: - return strconv_pcdata_impl::parse; - case 2: - return strconv_pcdata_impl::parse; - case 3: - return strconv_pcdata_impl::parse; - case 4: - return strconv_pcdata_impl::parse; - case 5: - return strconv_pcdata_impl::parse; - case 6: - return strconv_pcdata_impl::parse; - case 7: - return strconv_pcdata_impl::parse; - default: - assert(false); - return 0; // should not get here - } -} - -typedef char_t *(*strconv_attribute_t)(char_t *, char_t); - -template -struct strconv_attribute_impl -{ - static char_t *parse_wnorm(char_t *s, char_t end_quote) - { - gap g; - - // trim leading whitespaces - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t *str = s; - - do - ++str; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - g.push(s, str - s); - } - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) - ++s; - - if (*s == end_quote) - { - char_t *str = g.flush(s); - - do - *str-- = 0; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - *s++ = ' '; - - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t *str = s + 1; - while (PUGI__IS_CHARTYPE(*str, ct_space)) - ++str; - - g.push(s, str - s); - } - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else - ++s; - } - } - - static char_t *parse_wconv(char_t *s, char_t end_quote) - { - gap g; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) - ++s; - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - if (*s == '\r') - { - *s++ = ' '; - - if (*s == '\n') - g.push(s, 1); - } - else - *s++ = ' '; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else - ++s; - } - } - - static char_t *parse_eol(char_t *s, char_t end_quote) - { - gap g; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) - ++s; - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == '\r') - { - *s++ = '\n'; - - if (*s == '\n') - g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else - ++s; - } - } - - static char_t *parse_simple(char_t *s, char_t end_quote) - { - gap g; - - while (true) - { - while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) - ++s; - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else - ++s; - } - } -}; - -PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) -{ - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && - parse_wnorm_attribute == 0x80); - - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) - { - case 0: - return strconv_attribute_impl::parse_simple; - case 1: - return strconv_attribute_impl::parse_simple; - case 2: - return strconv_attribute_impl::parse_eol; - case 3: - return strconv_attribute_impl::parse_eol; - case 4: - return strconv_attribute_impl::parse_wconv; - case 5: - return strconv_attribute_impl::parse_wconv; - case 6: - return strconv_attribute_impl::parse_wconv; - case 7: - return strconv_attribute_impl::parse_wconv; - case 8: - return strconv_attribute_impl::parse_wnorm; - case 9: - return strconv_attribute_impl::parse_wnorm; - case 10: - return strconv_attribute_impl::parse_wnorm; - case 11: - return strconv_attribute_impl::parse_wnorm; - case 12: - return strconv_attribute_impl::parse_wnorm; - case 13: - return strconv_attribute_impl::parse_wnorm; - case 14: - return strconv_attribute_impl::parse_wnorm; - case 15: - return strconv_attribute_impl::parse_wnorm; - default: - assert(false); - return 0; // should not get here - } -} - -inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) -{ - xml_parse_result result; - result.status = status; - result.offset = offset; - - return result; -} - -struct xml_parser -{ - xml_allocator alloc; - char_t *error_offset; - xml_parse_status error_status; - -// Parser utilities. -#define PUGI__SKIPWS() \ - { \ - while (PUGI__IS_CHARTYPE(*s, ct_space)) \ - ++s; \ - } -#define PUGI__OPTSET(OPT) (optmsk & (OPT)) -#define PUGI__PUSHNODE(TYPE) \ - { \ - cursor = append_node(cursor, alloc, TYPE); \ - if (!cursor) \ - PUGI__THROW_ERROR(status_out_of_memory, s); \ - } -#define PUGI__POPNODE() \ - { \ - cursor = cursor->parent; \ - } -#define PUGI__SCANFOR(X) \ - { \ - while (*s != 0 && !(X)) \ - ++s; \ - } -#define PUGI__SCANWHILE(X) \ - { \ - while ((X)) \ - ++s; \ - } -#define PUGI__ENDSEG() \ - { \ - ch = *s; \ - *s = 0; \ - ++s; \ - } -#define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) -#define PUGI__CHECK_ERROR(err, m) \ - { \ - if (*s == 0) \ - PUGI__THROW_ERROR(err, m); \ - } - - xml_parser(const xml_allocator &alloc_) : alloc(alloc_), error_offset(0), error_status(status_ok) {} - - // DOCTYPE consists of nested sections of the following possible types: - // , , "...", '...' - // - // - // First group can not contain nested groups - // Second group can contain nested groups of the same type - // Third group can contain all other groups - char_t *parse_doctype_primitive(char_t *s) - { - if (*s == '"' || *s == '\'') - { - // quoted string - char_t ch = *s++; - PUGI__SCANFOR(*s == ch); - if (!*s) - PUGI__THROW_ERROR(status_bad_doctype, s); - - s++; - } - else if (s[0] == '<' && s[1] == '?') - { - // - s += 2; - PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype - if (!*s) - PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 2; - } - else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') - { - s += 4; - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && - s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype - if (!*s) - PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 4; - } - else - PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t *parse_doctype_ignore(char_t *s) - { - assert(s[0] == '<' && s[1] == '!' && s[2] == '['); - s++; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] == '[') - { - // nested ignore section - s = parse_doctype_ignore(s); - if (!s) - return s; - } - else if (s[0] == ']' && s[1] == ']' && s[2] == '>') - { - // ignore section end - s += 3; - - return s; - } - else - s++; - } - - PUGI__THROW_ERROR(status_bad_doctype, s); - } - - char_t *parse_doctype_group(char_t *s, char_t endch, bool toplevel) - { - assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); - s++; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] != '-') - { - if (s[2] == '[') - { - // ignore - s = parse_doctype_ignore(s); - if (!s) - return s; - } - else - { - // some control group - s = parse_doctype_group(s, endch, false); - if (!s) - return s; - - // skip > - assert(*s == '>'); - s++; - } - } - else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') - { - // unknown tag (forbidden), or some primitive group - s = parse_doctype_primitive(s); - if (!s) - return s; - } - else if (*s == '>') - { - return s; - } - else - s++; - } - - if (!toplevel || endch != '>') - PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t *parse_exclamation(char_t *s, xml_node_struct *cursor, unsigned int optmsk, char_t endch) - { - // parse node contents, starting with exclamation mark - ++s; - - if (*s == '-') // 'value = s; // Save the offset. - } - - if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) - { - s = strconv_comment(s, endch); - - if (!s) - PUGI__THROW_ERROR(status_bad_comment, cursor->value); - } - else - { - // Scan for terminating '-->'. - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_comment, s); - - if (PUGI__OPTSET(parse_comments)) - *s = 0; // Zero-terminate this segment at the first terminating '-'. - - s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. - } - } - else - PUGI__THROW_ERROR(status_bad_comment, s); - } - else if (*s == '[') - { - // 'value = s; // Save the offset. - - if (PUGI__OPTSET(parse_eol)) - { - s = strconv_cdata(s, endch); - - if (!s) - PUGI__THROW_ERROR(status_bad_cdata, cursor->value); - } - else - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - *s++ = 0; // Zero-terminate this segment. - } - } - else // Flagged for discard, but we still have to scan for the terminator. - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - ++s; - } - - s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. - } - else - PUGI__THROW_ERROR(status_bad_cdata, s); - } - else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && - ENDSWITH(s[6], 'E')) - { - s -= 2; - - if (cursor->parent) - PUGI__THROW_ERROR(status_bad_doctype, s); - - char_t *mark = s + 9; - - s = parse_doctype_group(s, endch, true); - if (!s) - return s; - - assert((*s == 0 && endch == '>') || *s == '>'); - if (*s) - *s++ = 0; - - if (PUGI__OPTSET(parse_doctype)) - { - while (PUGI__IS_CHARTYPE(*mark, ct_space)) - ++mark; - - PUGI__PUSHNODE(node_doctype); - - cursor->value = mark; - - PUGI__POPNODE(); - } - } - else if (*s == 0 && endch == '-') - PUGI__THROW_ERROR(status_bad_comment, s); - else if (*s == 0 && endch == '[') - PUGI__THROW_ERROR(status_bad_cdata, s); - else - PUGI__THROW_ERROR(status_unrecognized_tag, s); - - return s; - } - - char_t *parse_question(char_t *s, xml_node_struct *&ref_cursor, unsigned int optmsk, char_t endch) - { - // load into registers - xml_node_struct *cursor = ref_cursor; - char_t ch = 0; - - // parse node contents, starting with question mark - ++s; - - // read PI target - char_t *target = s; - - if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) - PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); - PUGI__CHECK_ERROR(status_bad_pi, s); - - // determine node type; stricmp / strcasecmp is not portable - bool declaration = - (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; - - if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) - { - if (declaration) - { - // disallow non top-level declarations - if (cursor->parent) - PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__PUSHNODE(node_declaration); - } - else - { - PUGI__PUSHNODE(node_pi); - } - - cursor->name = target; - - PUGI__ENDSEG(); - - // parse value/attributes - if (ch == '?') - { - // empty node - if (!ENDSWITH(*s, '>')) - PUGI__THROW_ERROR(status_bad_pi, s); - s += (*s == '>'); - - PUGI__POPNODE(); - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); - - // scan for tag end - char_t *value = s; - - PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - if (declaration) - { - // replace ending ? with / so that 'element' terminates properly - *s = '/'; - - // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to - // LOC_ATTRIBUTES - s = value; - } - else - { - // store value and step over > - cursor->value = value; - PUGI__POPNODE(); - - PUGI__ENDSEG(); - - s += (*s == '>'); - } - } - else - PUGI__THROW_ERROR(status_bad_pi, s); - } - else - { - // scan for tag end - PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - s += (s[1] == '>' ? 2 : 1); - } - - // store from registers - ref_cursor = cursor; - - return s; - } - - char_t *parse_tree(char_t *s, xml_node_struct *root, unsigned int optmsk, char_t endch) - { - strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); - strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); - - char_t ch = 0; - xml_node_struct *cursor = root; - char_t *mark = s; - - while (*s != 0) - { - if (*s == '<') - { - ++s; - - LOC_TAG: - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' - { - PUGI__PUSHNODE(node_element); // Append a new node to the tree. - - cursor->name = s; - - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - - if (ch == '>') - { - // end of tag - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - LOC_ATTRIBUTES: - while (true) - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... - { - xml_attribute_struct *a = - append_attribute_ll(cursor, alloc); // Make space for this attribute. - if (!a) - PUGI__THROW_ERROR(status_out_of_memory, s); - - a->name = s; // Save the offset. - - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. - PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance - - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); // Eat any whitespace. - PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance - - ch = *s; - ++s; - } - - if (ch == '=') // '<... #=...' - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (*s == '"' || *s == '\'') // '<... #="...' - { - ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. - ++s; // Step over the quote. - a->value = s; // Save the offset. - - s = strconv_attribute(s, ch); - - if (!s) - PUGI__THROW_ERROR(status_bad_attribute, a->value); - - // After this line the loop continues from the start; - // Whitespaces, / and > are ok, symbols and EOF are wrong, - // everything else will be detected - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) - PUGI__THROW_ERROR(status_bad_attribute, s); - } - else - PUGI__THROW_ERROR(status_bad_attribute, s); - } - else - PUGI__THROW_ERROR(status_bad_attribute, s); - } - else if (*s == '/') - { - ++s; - - if (*s == '>') - { - PUGI__POPNODE(); - s++; - break; - } - else if (*s == 0 && endch == '>') - { - PUGI__POPNODE(); - break; - } - else - PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '>') - { - ++s; - - break; - } - else if (*s == 0 && endch == '>') - { - break; - } - else - PUGI__THROW_ERROR(status_bad_start_element, s); - } - - // !!! - } - else if (ch == '/') // '<#.../' - { - if (!ENDSWITH(*s, '>')) - PUGI__THROW_ERROR(status_bad_start_element, s); - - PUGI__POPNODE(); // Pop. - - s += (*s == '>'); - } - else if (ch == 0) - { - // we stepped over null terminator, backtrack & handle closing tag - --s; - - if (endch != '>') - PUGI__THROW_ERROR(status_bad_start_element, s); - } - else - PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '/') - { - ++s; - - char_t *name = cursor->name; - if (!name) - PUGI__THROW_ERROR(status_end_element_mismatch, s); - - while (PUGI__IS_CHARTYPE(*s, ct_symbol)) - { - if (*s++ != *name++) - PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - if (*name) - { - if (*s == 0 && name[0] == endch && name[1] == 0) - PUGI__THROW_ERROR(status_bad_end_element, s); - else - PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - PUGI__POPNODE(); // Pop. - - PUGI__SKIPWS(); - - if (*s == 0) - { - if (endch != '>') - PUGI__THROW_ERROR(status_bad_end_element, s); - } - else - { - if (*s != '>') - PUGI__THROW_ERROR(status_bad_end_element, s); - ++s; - } - } - else if (*s == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) - goto LOC_ATTRIBUTES; - } - else if (*s == '!') // 'first_child) - continue; - } - } - - if (!PUGI__OPTSET(parse_trim_pcdata)) - s = mark; - - if (cursor->parent || PUGI__OPTSET(parse_fragment)) - { - PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. - cursor->value = s; // Save the offset. - - s = strconv_pcdata(s); - - PUGI__POPNODE(); // Pop since this is a standalone. - - if (!*s) - break; - } - else - { - PUGI__SCANFOR(*s == '<'); // '...<' - if (!*s) - break; - - ++s; - } - - // We're after '<' - goto LOC_TAG; - } - } - - // check that last tag is closed - if (cursor != root) - PUGI__THROW_ERROR(status_end_element_mismatch, s); - - return s; - } - -#ifdef PUGIXML_WCHAR_MODE - static char_t *parse_skip_bom(char_t *s) - { - unsigned int bom = 0xfeff; - return (s[0] == static_cast(bom)) ? s + 1 : s; - } -#else - static char_t *parse_skip_bom(char_t *s) - { - return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; - } -#endif - - static bool has_element_node_siblings(xml_node_struct *node) - { - while (node) - { - xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1); - if (type == node_element) - return true; - - node = node->next_sibling; - } - - return false; - } - - static xml_parse_result parse(char_t *buffer, size_t length, xml_document_struct *xmldoc, xml_node_struct *root, - unsigned int optmsk) - { - // allocator object is a part of document object - xml_allocator &alloc = *static_cast(xmldoc); - - // early-out for empty documents - if (length == 0) - return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); - - // get last child of the root before parsing - xml_node_struct *last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0; - - // create parser on stack - xml_parser parser(alloc); - - // save last character and make buffer zero-terminated (speeds up parsing) - char_t endch = buffer[length - 1]; - buffer[length - 1] = 0; - - // skip BOM to make sure it does not end up as part of parse output - char_t *buffer_data = parse_skip_bom(buffer); - - // perform actual parsing - parser.parse_tree(buffer_data, root, optmsk, endch); - - // update allocator state - alloc = parser.alloc; - - xml_parse_result result = - make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); - assert(result.offset >= 0 && static_cast(result.offset) <= length); - - if (result) - { - // since we removed last character, we have to handle the only possible false positive (stray <) - if (endch == '<') - return make_parse_result(status_unrecognized_tag, length - 1); - - // check if there are any element nodes parsed - xml_node_struct *first_root_child_parsed = - last_root_child ? last_root_child->next_sibling : root->first_child; - - if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) - return make_parse_result(status_no_document_element, length - 1); - } - else - { - // roll back offset if it occurs on a null terminator in the source buffer - if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) - result.offset--; - } - - return result; - } -}; - -// Output facilities -PUGI__FN xml_encoding get_write_native_encoding() -{ -#ifdef PUGIXML_WCHAR_MODE - return get_wchar_encoding(); -#else - return encoding_utf8; -#endif -} - -PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) -{ - // replace wchar encoding with utf implementation - if (encoding == encoding_wchar) - return get_wchar_encoding(); - - // replace utf16 encoding with utf16 with specific endianness - if (encoding == encoding_utf16) - return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - // replace utf32 encoding with utf32 with specific endianness - if (encoding == encoding_utf32) - return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - // only do autodetection if no explicit encoding is requested - if (encoding != encoding_auto) - return encoding; - - // assume utf8 encoding - return encoding_utf8; -} - -#ifdef PUGIXML_WCHAR_MODE -PUGI__FN size_t get_valid_length(const char_t *data, size_t length) -{ - assert(length > 0); - - // discard last character if it's the lead of a surrogate pair - return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) - ? length - 1 - : length; -} - -PUGI__FN size_t convert_buffer_output(char_t *r_char, uint8_t *r_u8, uint16_t *r_u16, uint32_t *r_u32, - const char_t *data, size_t length, xml_encoding encoding) -{ - // only endian-swapping is required - if (need_endian_swap_utf(encoding, get_wchar_encoding())) - { - convert_wchar_endian_swap(r_char, data, length); - - return length * sizeof(char_t); - } - - // convert to utf8 - if (encoding == encoding_utf8) - { - uint8_t *dest = r_u8; - uint8_t *end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - // convert to utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t *dest = r_u16; - - // convert to native utf16 - uint16_t *end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) - convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - // convert to utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t *dest = r_u32; - - // convert to native utf32 - uint32_t *end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) - convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - // convert to latin1 - if (encoding == encoding_latin1) - { - uint8_t *dest = r_u8; - uint8_t *end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; -} -#else -PUGI__FN size_t get_valid_length(const char_t *data, size_t length) -{ - assert(length > 4); - - for (size_t i = 1; i <= 4; ++i) - { - uint8_t ch = static_cast(data[length - i]); - - // either a standalone character or a leading one - if ((ch & 0xc0) != 0x80) - return length - i; - } - - // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole - // chunk - return length; -} - -PUGI__FN size_t convert_buffer_output(char_t * /* r_char */, uint8_t *r_u8, uint16_t *r_u16, uint32_t *r_u32, - const char_t *data, size_t length, xml_encoding encoding) -{ - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t *dest = r_u16; - - // convert to native utf16 - uint16_t *end = - utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) - convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t *dest = r_u32; - - // convert to native utf32 - uint32_t *end = - utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) - convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - if (encoding == encoding_latin1) - { - uint8_t *dest = r_u8; - uint8_t *end = - utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; -} -#endif - -class xml_buffered_writer -{ - xml_buffered_writer(const xml_buffered_writer &); - xml_buffered_writer &operator=(const xml_buffered_writer &); - - public: - xml_buffered_writer(xml_writer &writer_, xml_encoding user_encoding) - : writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) - { - PUGI__STATIC_ASSERT(bufcapacity >= 8); - } - - ~xml_buffered_writer() { flush(); } - - void flush() - { - flush(buffer, bufsize); - bufsize = 0; - } - - void flush(const char_t *data, size_t size) - { - if (size == 0) - return; - - // fast path, just write data - if (encoding == get_write_native_encoding()) - writer.write(data, size * sizeof(char_t)); - else - { - // convert chunk - size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, - scratch.data_u32, data, size, encoding); - assert(result <= sizeof(scratch)); - - // write data - writer.write(scratch.data_u8, result); - } - } - - void write(const char_t *data, size_t length) - { - if (bufsize + length > bufcapacity) - { - // flush the remaining buffer contents - flush(); - - // handle large chunks - if (length > bufcapacity) - { - if (encoding == get_write_native_encoding()) - { - // fast path, can just write data chunk - writer.write(data, length * sizeof(char_t)); - return; - } - - // need to convert in suitable chunks - while (length > bufcapacity) - { - // get chunk size by selecting such number of characters that are guaranteed to fit into scratch - // buffer and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) - size_t chunk_size = get_valid_length(data, bufcapacity); - - // convert chunk and write - flush(data, chunk_size); - - // iterate - data += chunk_size; - length -= chunk_size; - } - - // small tail is copied below - bufsize = 0; - } - } - - memcpy(buffer + bufsize, data, length * sizeof(char_t)); - bufsize += length; - } - - void write(const char_t *data) { write(data, strlength(data)); } - - void write(char_t d0) - { - if (bufsize + 1 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - bufsize += 1; - } - - void write(char_t d0, char_t d1) - { - if (bufsize + 2 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - buffer[bufsize + 1] = d1; - bufsize += 2; - } - - void write(char_t d0, char_t d1, char_t d2) - { - if (bufsize + 3 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - buffer[bufsize + 1] = d1; - buffer[bufsize + 2] = d2; - bufsize += 3; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3) - { - if (bufsize + 4 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - buffer[bufsize + 1] = d1; - buffer[bufsize + 2] = d2; - buffer[bufsize + 3] = d3; - bufsize += 4; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) - { - if (bufsize + 5 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - buffer[bufsize + 1] = d1; - buffer[bufsize + 2] = d2; - buffer[bufsize + 3] = d3; - buffer[bufsize + 4] = d4; - bufsize += 5; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) - { - if (bufsize + 6 > bufcapacity) - flush(); - - buffer[bufsize + 0] = d0; - buffer[bufsize + 1] = d1; - buffer[bufsize + 2] = d2; - buffer[bufsize + 3] = d3; - buffer[bufsize + 4] = d4; - buffer[bufsize + 5] = d5; - bufsize += 6; - } - - // utf8 maximum expansion: x4 (-> utf32) - // utf16 maximum expansion: x2 (-> utf32) - // utf32 maximum expansion: x1 - enum - { - bufcapacitybytes = -#ifdef PUGIXML_MEMORY_OUTPUT_STACK - PUGIXML_MEMORY_OUTPUT_STACK -#else - 10240 -#endif - , - bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) - }; - - char_t buffer[bufcapacity]; - - union { - uint8_t data_u8[4 * bufcapacity]; - uint16_t data_u16[2 * bufcapacity]; - uint32_t data_u32[bufcapacity]; - char_t data_char[bufcapacity]; - } scratch; - - xml_writer &writer; - size_t bufsize; - xml_encoding encoding; -}; - -PUGI__FN void text_output_escaped(xml_buffered_writer &writer, const char_t *s, chartypex_t type) -{ - while (*s) - { - const char_t *prev = s; - - // While *s is a usual symbol - while (!PUGI__IS_CHARTYPEX(*s, type)) - ++s; - - writer.write(prev, static_cast(s - prev)); - - switch (*s) - { - case 0: - break; - case '&': - writer.write('&', 'a', 'm', 'p', ';'); - ++s; - break; - case '<': - writer.write('&', 'l', 't', ';'); - ++s; - break; - case '>': - writer.write('&', 'g', 't', ';'); - ++s; - break; - case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); - ++s; - break; - default: // s is not a usual symbol - { - unsigned int ch = static_cast(*s++); - assert(ch < 32); - - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); - } - } - } -} - -PUGI__FN void text_output(xml_buffered_writer &writer, const char_t *s, chartypex_t type, unsigned int flags) -{ - if (flags & format_no_escapes) - writer.write(s); - else - text_output_escaped(writer, s, type); -} - -PUGI__FN void text_output_cdata(xml_buffered_writer &writer, const char_t *s) -{ - do - { - writer.write('<', '!', '[', 'C', 'D'); - writer.write('A', 'T', 'A', '['); - - const char_t *prev = s; - - // look for ]]> sequence - we can't output it as is since it terminates CDATA - while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) - ++s; - - // skip ]] if we stopped at ]]>, > will go to the next CDATA section - if (*s) - s += 2; - - writer.write(prev, static_cast(s - prev)); - - writer.write(']', ']', '>'); - } while (*s); -} - -PUGI__FN void node_output_attributes(xml_buffered_writer &writer, const xml_node &node, unsigned int flags) -{ - const char_t *default_name = PUGIXML_TEXT(":anonymous"); - - for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) - { - writer.write(' '); - writer.write(a.name()[0] ? a.name() : default_name); - writer.write('=', '"'); - - text_output(writer, a.value(), ctx_special_attr, flags); - - writer.write('"'); - } -} - -PUGI__FN void node_output(xml_buffered_writer &writer, const xml_node &node, const char_t *indent, unsigned int flags, - unsigned int depth) -{ - const char_t *default_name = PUGIXML_TEXT(":anonymous"); - - if ((flags & format_indent) != 0 && (flags & format_raw) == 0) - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent); - - switch (node.type()) - { - case node_document: - { - for (xml_node n = node.first_child(); n; n = n.next_sibling()) - node_output(writer, n, indent, flags, depth); - break; - } - - case node_element: - { - const char_t *name = node.name()[0] ? node.name() : default_name; - - writer.write('<'); - writer.write(name); - - node_output_attributes(writer, node, flags); - - if (flags & format_raw) - { - if (!node.first_child()) - writer.write(' ', '/', '>'); - else - { - writer.write('>'); - - for (xml_node n = node.first_child(); n; n = n.next_sibling()) - node_output(writer, n, indent, flags, depth + 1); - - writer.write('<', '/'); - writer.write(name); - writer.write('>'); - } - } - else if (!node.first_child()) - writer.write(' ', '/', '>', '\n'); - else if (node.first_child() == node.last_child() && - (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata)) - { - writer.write('>'); - - if (node.first_child().type() == node_pcdata) - text_output(writer, node.first_child().value(), ctx_special_pcdata, flags); - else - text_output_cdata(writer, node.first_child().value()); - - writer.write('<', '/'); - writer.write(name); - writer.write('>', '\n'); - } - else - { - writer.write('>', '\n'); - - for (xml_node n = node.first_child(); n; n = n.next_sibling()) - node_output(writer, n, indent, flags, depth + 1); - - if ((flags & format_indent) != 0 && (flags & format_raw) == 0) - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent); - - writer.write('<', '/'); - writer.write(name); - writer.write('>', '\n'); - } - - break; - } - - case node_pcdata: - text_output(writer, node.value(), ctx_special_pcdata, flags); - if ((flags & format_raw) == 0) - writer.write('\n'); - break; - - case node_cdata: - text_output_cdata(writer, node.value()); - if ((flags & format_raw) == 0) - writer.write('\n'); - break; - - case node_comment: - writer.write('<', '!', '-', '-'); - writer.write(node.value()); - writer.write('-', '-', '>'); - if ((flags & format_raw) == 0) - writer.write('\n'); - break; - - case node_pi: - case node_declaration: - writer.write('<', '?'); - writer.write(node.name()[0] ? node.name() : default_name); - - if (node.type() == node_declaration) - { - node_output_attributes(writer, node, flags); - } - else if (node.value()[0]) - { - writer.write(' '); - writer.write(node.value()); - } - - writer.write('?', '>'); - if ((flags & format_raw) == 0) - writer.write('\n'); - break; - - case node_doctype: - writer.write('<', '!', 'D', 'O', 'C'); - writer.write('T', 'Y', 'P', 'E'); - - if (node.value()[0]) - { - writer.write(' '); - writer.write(node.value()); - } - - writer.write('>'); - if ((flags & format_raw) == 0) - writer.write('\n'); - break; - - default: - assert(!"Invalid node type"); - } -} - -inline bool has_declaration(const xml_node &node) -{ - for (xml_node child = node.first_child(); child; child = child.next_sibling()) - { - xml_node_type type = child.type(); - - if (type == node_declaration) - return true; - if (type == node_element) - return false; - } - - return false; -} - -inline bool allow_insert_child(xml_node_type parent, xml_node_type child) -{ - if (parent != node_document && parent != node_element) - return false; - if (child == node_document || child == node_null) - return false; - if (parent != node_document && (child == node_declaration || child == node_doctype)) - return false; - - return true; -} - -PUGI__FN void recursive_copy_skip(xml_node &dest, const xml_node &source, const xml_node &skip) -{ - assert(dest.type() == source.type()); - - switch (source.type()) - { - case node_element: - { - dest.set_name(source.name()); - - for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) - dest.append_attribute(a.name()).set_value(a.value()); - - for (xml_node c = source.first_child(); c; c = c.next_sibling()) - { - if (c == skip) - continue; - - xml_node cc = dest.append_child(c.type()); - assert(cc); - - recursive_copy_skip(cc, c, skip); - } - - break; - } - - case node_pcdata: - case node_cdata: - case node_comment: - case node_doctype: - dest.set_value(source.value()); - break; - - case node_pi: - dest.set_name(source.name()); - dest.set_value(source.value()); - break; - - case node_declaration: - { - dest.set_name(source.name()); - - for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) - dest.append_attribute(a.name()).set_value(a.value()); - - break; - } - - default: - assert(!"Invalid node type"); - } -} - -inline bool is_text_node(xml_node_struct *node) -{ - xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1); - - return type == node_pcdata || type == node_cdata; -} - -// get value with conversion functions -PUGI__FN int get_integer_base(const char_t *value) -{ - const char_t *s = value; - - while (PUGI__IS_CHARTYPE(*s, ct_space)) - s++; - - if (*s == '-') - s++; - - return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; -} - -PUGI__FN int get_value_int(const char_t *value, int def) -{ - if (!value) - return def; - - int base = get_integer_base(value); - -#ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstol(value, 0, base)); -#else - return static_cast(strtol(value, 0, base)); -#endif -} - -PUGI__FN unsigned int get_value_uint(const char_t *value, unsigned int def) -{ - if (!value) - return def; - - int base = get_integer_base(value); - -#ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstoul(value, 0, base)); -#else - return static_cast(strtoul(value, 0, base)); -#endif -} - -PUGI__FN double get_value_double(const char_t *value, double def) -{ - if (!value) - return def; - -#ifdef PUGIXML_WCHAR_MODE - return wcstod(value, 0); -#else - return strtod(value, 0); -#endif -} - -PUGI__FN float get_value_float(const char_t *value, float def) -{ - if (!value) - return def; - -#ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstod(value, 0)); -#else - return static_cast(strtod(value, 0)); -#endif -} - -PUGI__FN bool get_value_bool(const char_t *value, bool def) -{ - if (!value) - return def; - - // only look at first char - char_t first = *value; - - // 1*, t* (true), T* (True), y* (yes), Y* (YES) - return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN long long get_value_llong(const char_t *value, long long def) -{ - if (!value) - return def; - - int base = get_integer_base(value); - -#ifdef PUGIXML_WCHAR_MODE -#ifdef PUGI__MSVC_CRT_VERSION - return _wcstoi64(value, 0, base); -#else - return wcstoll(value, 0, base); -#endif -#else -#ifdef PUGI__MSVC_CRT_VERSION - return _strtoi64(value, 0, base); -#else - return strtoll(value, 0, base); -#endif -#endif -} - -PUGI__FN unsigned long long get_value_ullong(const char_t *value, unsigned long long def) -{ - if (!value) - return def; - - int base = get_integer_base(value); - -#ifdef PUGIXML_WCHAR_MODE -#ifdef PUGI__MSVC_CRT_VERSION - return _wcstoui64(value, 0, base); -#else - return wcstoull(value, 0, base); -#endif -#else -#ifdef PUGI__MSVC_CRT_VERSION - return _strtoui64(value, 0, base); -#else - return strtoull(value, 0, base); -#endif -#endif -} -#endif - -// set value with conversion functions -PUGI__FN bool set_value_buffer(char_t *&dest, uintptr_t &header, uintptr_t header_mask, char (&buf)[128]) -{ -#ifdef PUGIXML_WCHAR_MODE - char_t wbuf[128]; - impl::widen_ascii(wbuf, buf); - - return strcpy_insitu(dest, header, header_mask, wbuf); -#else - return strcpy_insitu(dest, header, header_mask, buf); -#endif -} - -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, int value) -{ - char buf[128]; - sprintf(buf, "%d", value); - - return set_value_buffer(dest, header, header_mask, buf); -} - -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, unsigned int value) -{ - char buf[128]; - sprintf(buf, "%u", value); - - return set_value_buffer(dest, header, header_mask, buf); -} - -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, double value) -{ - char buf[128]; - sprintf(buf, "%g", value); - - return set_value_buffer(dest, header, header_mask, buf); -} - -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, bool value) -{ - return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, long long value) -{ - char buf[128]; - sprintf(buf, "%lld", value); - - return set_value_buffer(dest, header, header_mask, buf); -} - -PUGI__FN bool set_value_convert(char_t *&dest, uintptr_t &header, uintptr_t header_mask, unsigned long long value) -{ - char buf[128]; - sprintf(buf, "%llu", value); - - return set_value_buffer(dest, header, header_mask, buf); -} -#endif - -// we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell -// trick -PUGI__FN xml_parse_status get_file_size(FILE *file, size_t &out_result) -{ -#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - // there are 64-bit versions of fseek/ftell, let's use them - typedef __int64 length_type; - - _fseeki64(file, 0, SEEK_END); - length_type length = _ftelli64(file); - _fseeki64(file, 0, SEEK_SET); -#elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__) - // there are 64-bit versions of fseek/ftell, let's use them - typedef off64_t length_type; - - fseeko64(file, 0, SEEK_END); - length_type length = ftello64(file); - fseeko64(file, 0, SEEK_SET); -#else - // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we - // can't do anything anyway. - typedef long length_type; - - fseek(file, 0, SEEK_END); - length_type length = ftell(file); - fseek(file, 0, SEEK_SET); -#endif - - // check for I/O errors - if (length < 0) - return status_io_error; - - // check for overflow - size_t result = static_cast(length); - - if (static_cast(result) != length) - return status_out_of_memory; - - // finalize - out_result = result; - - return status_ok; -} - -PUGI__FN size_t zero_terminate_buffer(void *buffer, size_t size, xml_encoding encoding) -{ - // We only need to zero-terminate if encoding conversion does not do it for us -#ifdef PUGIXML_WCHAR_MODE - xml_encoding wchar_encoding = get_wchar_encoding(); - - if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) - { - size_t length = size / sizeof(char_t); - - static_cast(buffer)[length] = 0; - return (length + 1) * sizeof(char_t); - } -#else - if (encoding == encoding_utf8) - { - static_cast(buffer)[size] = 0; - return size + 1; - } -#endif - - return size; -} - -PUGI__FN xml_parse_result load_file_impl(xml_document &doc, FILE *file, unsigned int options, xml_encoding encoding) -{ - if (!file) - return make_parse_result(status_file_not_found); - - // get file size (can result in I/O errors) - size_t size = 0; - xml_parse_status size_status = get_file_size(file, size); - - if (size_status != status_ok) - { - fclose(file); - return make_parse_result(size_status); - } - - size_t max_suffix_size = sizeof(char_t); - - // allocate buffer for the whole file - char *contents = static_cast(xml_memory::allocate(size + max_suffix_size)); - - if (!contents) - { - fclose(file); - return make_parse_result(status_out_of_memory); - } - - // read file in memory - size_t read_size = fread(contents, 1, size, file); - fclose(file); - - if (read_size != size) - { - xml_memory::deallocate(contents); - return make_parse_result(status_io_error); - } - - xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); - - return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, - real_encoding); -} - -#ifndef PUGIXML_NO_STL -template -struct xml_stream_chunk -{ - static xml_stream_chunk *create() - { - void *memory = xml_memory::allocate(sizeof(xml_stream_chunk)); - - return new (memory) xml_stream_chunk(); - } - - static void destroy(void *ptr) - { - xml_stream_chunk *chunk = static_cast(ptr); - - // free chunk chain - while (chunk) - { - xml_stream_chunk *next = chunk->next; - xml_memory::deallocate(chunk); - chunk = next; - } - } - - xml_stream_chunk() : next(0), size(0) {} - - xml_stream_chunk *next; - size_t size; - - T data[xml_memory_page_size / sizeof(T)]; -}; - -template -PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream &stream, void **out_buffer, size_t *out_size) -{ - buffer_holder chunks(0, xml_stream_chunk::destroy); - - // read file to a chunk list - size_t total = 0; - xml_stream_chunk *last = 0; - - while (!stream.eof()) - { - // allocate new chunk - xml_stream_chunk *chunk = xml_stream_chunk::create(); - if (!chunk) - return status_out_of_memory; - - // append chunk to list - if (last) - last = last->next = chunk; - else - chunks.data = last = chunk; - - // read data to chunk - stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); - chunk->size = static_cast(stream.gcount()) * sizeof(T); - - // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) - return status_io_error; - - // guard against huge files (chunk size is small enough to make this overflow check work) - if (total + chunk->size < total) - return status_out_of_memory; - total += chunk->size; - } - - size_t max_suffix_size = sizeof(char_t); - - // copy chunk list to a contiguous buffer - char *buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); - if (!buffer) - return status_out_of_memory; - - char *write = buffer; - - for (xml_stream_chunk *chunk = static_cast *>(chunks.data); chunk; chunk = chunk->next) - { - assert(write + chunk->size <= buffer + total); - memcpy(write, chunk->data, chunk->size); - write += chunk->size; - } - - assert(write == buffer + total); - - // return buffer - *out_buffer = buffer; - *out_size = total; - - return status_ok; -} - -template -PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream &stream, void **out_buffer, size_t *out_size) -{ - // get length of remaining data in stream - typename std::basic_istream::pos_type pos = stream.tellg(); - stream.seekg(0, std::ios::end); - std::streamoff length = stream.tellg() - pos; - stream.seekg(pos); - - if (stream.fail() || pos < 0) - return status_io_error; - - // guard against huge files - size_t read_length = static_cast(length); - - if (static_cast(read_length) != length || length < 0) - return status_out_of_memory; - - size_t max_suffix_size = sizeof(char_t); - - // read stream data into memory (guard against stream exceptions with buffer holder) - buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); - if (!buffer.data) - return status_out_of_memory; - - stream.read(static_cast(buffer.data), static_cast(read_length)); - - // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check - // for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) - return status_io_error; - - // return buffer - size_t actual_length = static_cast(stream.gcount()); - assert(actual_length <= read_length); - - *out_buffer = buffer.release(); - *out_size = actual_length * sizeof(T); - - return status_ok; -} - -template -PUGI__FN xml_parse_result load_stream_impl(xml_document &doc, std::basic_istream &stream, unsigned int options, - xml_encoding encoding) -{ - void *buffer = 0; - size_t size = 0; - xml_parse_status status = status_ok; - - // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) - if (stream.fail()) - return make_parse_result(status_io_error); - - // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) - if (stream.tellg() < 0) - { - stream.clear(); // clear error flags that could be set by a failing tellg - status = load_stream_data_noseek(stream, &buffer, &size); - } - else - status = load_stream_data_seek(stream, &buffer, &size); - - if (status != status_ok) - return make_parse_result(status); - - xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); - - return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, - real_encoding); -} -#endif - -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__)) -PUGI__FN FILE *open_file_wide(const wchar_t *path, const wchar_t *mode) -{ - return _wfopen(path, mode); -} -#else -PUGI__FN char *convert_path_heap(const wchar_t *str) -{ - assert(str); - - // first pass: get length in utf8 characters - size_t length = strlength_wide(str); - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - char *result = static_cast(xml_memory::allocate(size + 1)); - if (!result) - return 0; - - // second pass: convert to utf8 - as_utf8_end(result, size, str, length); - - return result; -} - -PUGI__FN FILE *open_file_wide(const wchar_t *path, const wchar_t *mode) -{ - // there is no standard function to open wide paths, so our best bet is to try utf8 path - char *path_utf8 = convert_path_heap(path); - if (!path_utf8) - return 0; - - // convert mode to ASCII (we mirror _wfopen interface) - char mode_ascii[4] = { 0 }; - for (size_t i = 0; mode[i]; ++i) - mode_ascii[i] = static_cast(mode[i]); - - // try to open the utf8 path - FILE *result = fopen(path_utf8, mode_ascii); - - // free dummy buffer - xml_memory::deallocate(path_utf8); - - return result; -} -#endif - -PUGI__FN bool save_file_impl(const xml_document &doc, FILE *file, const char_t *indent, unsigned int flags, - xml_encoding encoding) -{ - if (!file) - return false; - - xml_writer_file writer(file); - doc.save(writer, indent, flags, encoding); - - int result = ferror(file); - - fclose(file); - - return result == 0; -} - -PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct *doc, xml_node_struct *root, void *contents, size_t size, - unsigned int options, xml_encoding encoding, bool is_mutable, bool own, - char_t **out_buffer) -{ - // check input buffer - assert(contents || size == 0); - - // get actual encoding - xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); - - // get private buffer - char_t *buffer = 0; - size_t length = 0; - - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) - return impl::make_parse_result(status_out_of_memory); - - // delete original buffer if we performed a conversion - if (own && buffer != contents && contents) - impl::xml_memory::deallocate(contents); - - // store buffer for offset_debug - doc->buffer = buffer; - - // parse - xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); - - // remember encoding - res.encoding = buffer_encoding; - - // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself - if (own || buffer != contents) - *out_buffer = buffer; - - return res; -} -PUGI__NS_END - -namespace pugi -{ -PUGI__FN xml_writer_file::xml_writer_file(void *file_) : file(file_) {} - -PUGI__FN void xml_writer_file::write(const void *data, size_t size) -{ - size_t result = fwrite(data, 1, size, static_cast(file)); - (void)!result; // unfortunately we can't do proper error handling here -} - -#ifndef PUGIXML_NO_STL -PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream> &stream) - : narrow_stream(&stream), wide_stream(0) -{ -} - -PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream> &stream) - : narrow_stream(0), wide_stream(&stream) -{ -} - -PUGI__FN void xml_writer_stream::write(const void *data, size_t size) -{ - if (narrow_stream) - { - assert(!wide_stream); - narrow_stream->write(reinterpret_cast(data), static_cast(size)); - } - else - { - assert(wide_stream); - assert(size % sizeof(wchar_t) == 0); - - wide_stream->write(reinterpret_cast(data), - static_cast(size / sizeof(wchar_t))); - } -} -#endif - -PUGI__FN xml_tree_walker::xml_tree_walker() : _depth(0) {} - -PUGI__FN xml_tree_walker::~xml_tree_walker() {} - -PUGI__FN int xml_tree_walker::depth() const -{ - return _depth; -} - -PUGI__FN bool xml_tree_walker::begin(xml_node &) -{ - return true; -} - -PUGI__FN bool xml_tree_walker::end(xml_node &) -{ - return true; -} - -PUGI__FN xml_attribute::xml_attribute() : _attr(0) {} - -PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct *attr) : _attr(attr) {} - -PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute ***) {} - -PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const -{ - return _attr ? unspecified_bool_xml_attribute : 0; -} - -PUGI__FN bool xml_attribute::operator!() const -{ - return !_attr; -} - -PUGI__FN bool xml_attribute::operator==(const xml_attribute &r) const -{ - return (_attr == r._attr); -} - -PUGI__FN bool xml_attribute::operator!=(const xml_attribute &r) const -{ - return (_attr != r._attr); -} - -PUGI__FN bool xml_attribute::operator<(const xml_attribute &r) const -{ - return (_attr < r._attr); -} - -PUGI__FN bool xml_attribute::operator>(const xml_attribute &r) const -{ - return (_attr > r._attr); -} - -PUGI__FN bool xml_attribute::operator<=(const xml_attribute &r) const -{ - return (_attr <= r._attr); -} - -PUGI__FN bool xml_attribute::operator>=(const xml_attribute &r) const -{ - return (_attr >= r._attr); -} - -PUGI__FN xml_attribute xml_attribute::next_attribute() const -{ - return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); -} - -PUGI__FN xml_attribute xml_attribute::previous_attribute() const -{ - return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); -} - -PUGI__FN const char_t *xml_attribute::as_string(const char_t *def) const -{ - return (_attr && _attr->value) ? _attr->value : def; -} - -PUGI__FN int xml_attribute::as_int(int def) const -{ - return impl::get_value_int(_attr ? _attr->value : 0, def); -} - -PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const -{ - return impl::get_value_uint(_attr ? _attr->value : 0, def); -} - -PUGI__FN double xml_attribute::as_double(double def) const -{ - return impl::get_value_double(_attr ? _attr->value : 0, def); -} - -PUGI__FN float xml_attribute::as_float(float def) const -{ - return impl::get_value_float(_attr ? _attr->value : 0, def); -} - -PUGI__FN bool xml_attribute::as_bool(bool def) const -{ - return impl::get_value_bool(_attr ? _attr->value : 0, def); -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN long long xml_attribute::as_llong(long long def) const -{ - return impl::get_value_llong(_attr ? _attr->value : 0, def); -} - -PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const -{ - return impl::get_value_ullong(_attr ? _attr->value : 0, def); -} -#endif - -PUGI__FN bool xml_attribute::empty() const -{ - return !_attr; -} - -PUGI__FN const char_t *xml_attribute::name() const -{ - return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); -} - -PUGI__FN const char_t *xml_attribute::value() const -{ - return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); -} - -PUGI__FN size_t xml_attribute::hash_value() const -{ - return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); -} - -PUGI__FN xml_attribute_struct *xml_attribute::internal_object() const -{ - return _attr; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(const char_t *rhs) -{ - set_value(rhs); - return *this; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(int rhs) -{ - set_value(rhs); - return *this; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(unsigned int rhs) -{ - set_value(rhs); - return *this; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(double rhs) -{ - set_value(rhs); - return *this; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(bool rhs) -{ - set_value(rhs); - return *this; -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN xml_attribute &xml_attribute::operator=(long long rhs) -{ - set_value(rhs); - return *this; -} - -PUGI__FN xml_attribute &xml_attribute::operator=(unsigned long long rhs) -{ - set_value(rhs); - return *this; -} -#endif - -PUGI__FN bool xml_attribute::set_name(const char_t *rhs) -{ - if (!_attr) - return false; - - return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(const char_t *rhs) -{ - if (!_attr) - return false; - - return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(int rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(unsigned int rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(double rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(bool rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN bool xml_attribute::set_value(long long rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} - -PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) -{ - if (!_attr) - return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); -} -#endif - -#ifdef __BORLANDC__ -PUGI__FN bool operator&&(const xml_attribute &lhs, bool rhs) -{ - return (bool)lhs && rhs; -} - -PUGI__FN bool operator||(const xml_attribute &lhs, bool rhs) -{ - return (bool)lhs || rhs; -} -#endif - -PUGI__FN xml_node::xml_node() : _root(0) {} - -PUGI__FN xml_node::xml_node(xml_node_struct *p) : _root(p) {} - -PUGI__FN static void unspecified_bool_xml_node(xml_node ***) {} - -PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const -{ - return _root ? unspecified_bool_xml_node : 0; -} - -PUGI__FN bool xml_node::operator!() const -{ - return !_root; -} - -PUGI__FN xml_node::iterator xml_node::begin() const -{ - return iterator(_root ? _root->first_child : 0, _root); -} - -PUGI__FN xml_node::iterator xml_node::end() const -{ - return iterator(0, _root); -} - -PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const -{ - return attribute_iterator(_root ? _root->first_attribute : 0, _root); -} - -PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const -{ - return attribute_iterator(0, _root); -} - -PUGI__FN xml_object_range xml_node::children() const -{ - return xml_object_range(begin(), end()); -} - -PUGI__FN xml_object_range xml_node::children(const char_t *name_) const -{ - return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), - xml_named_node_iterator(0, _root, name_)); -} - -PUGI__FN xml_object_range xml_node::attributes() const -{ - return xml_object_range(attributes_begin(), attributes_end()); -} - -PUGI__FN bool xml_node::operator==(const xml_node &r) const -{ - return (_root == r._root); -} - -PUGI__FN bool xml_node::operator!=(const xml_node &r) const -{ - return (_root != r._root); -} - -PUGI__FN bool xml_node::operator<(const xml_node &r) const -{ - return (_root < r._root); -} - -PUGI__FN bool xml_node::operator>(const xml_node &r) const -{ - return (_root > r._root); -} - -PUGI__FN bool xml_node::operator<=(const xml_node &r) const -{ - return (_root <= r._root); -} - -PUGI__FN bool xml_node::operator>=(const xml_node &r) const -{ - return (_root >= r._root); -} - -PUGI__FN bool xml_node::empty() const -{ - return !_root; -} - -PUGI__FN const char_t *xml_node::name() const -{ - return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); -} - -PUGI__FN xml_node_type xml_node::type() const -{ - return _root ? static_cast((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null; -} - -PUGI__FN const char_t *xml_node::value() const -{ - return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); -} - -PUGI__FN xml_node xml_node::child(const char_t *name_) const -{ - if (!_root) - return xml_node(); - - for (xml_node_struct *i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) - return xml_node(i); - - return xml_node(); -} - -PUGI__FN xml_attribute xml_node::attribute(const char_t *name_) const -{ - if (!_root) - return xml_attribute(); - - for (xml_attribute_struct *i = _root->first_attribute; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) - return xml_attribute(i); - - return xml_attribute(); -} - -PUGI__FN xml_node xml_node::next_sibling(const char_t *name_) const -{ - if (!_root) - return xml_node(); - - for (xml_node_struct *i = _root->next_sibling; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) - return xml_node(i); - - return xml_node(); -} - -PUGI__FN xml_node xml_node::next_sibling() const -{ - if (!_root) - return xml_node(); - - if (_root->next_sibling) - return xml_node(_root->next_sibling); - else - return xml_node(); -} - -PUGI__FN xml_node xml_node::previous_sibling(const char_t *name_) const -{ - if (!_root) - return xml_node(); - - for (xml_node_struct *i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) - if (i->name && impl::strequal(name_, i->name)) - return xml_node(i); - - return xml_node(); -} - -PUGI__FN xml_node xml_node::previous_sibling() const -{ - if (!_root) - return xml_node(); - - if (_root->prev_sibling_c->next_sibling) - return xml_node(_root->prev_sibling_c); - else - return xml_node(); -} - -PUGI__FN xml_node xml_node::parent() const -{ - return _root ? xml_node(_root->parent) : xml_node(); -} - -PUGI__FN xml_node xml_node::root() const -{ - if (!_root) - return xml_node(); - - impl::xml_memory_page *page = - reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); - - return xml_node(static_cast(page->allocator)); -} - -PUGI__FN xml_text xml_node::text() const -{ - return xml_text(_root); -} - -PUGI__FN const char_t *xml_node::child_value() const -{ - if (!_root) - return PUGIXML_TEXT(""); - - for (xml_node_struct *i = _root->first_child; i; i = i->next_sibling) - if (i->value && impl::is_text_node(i)) - return i->value; - - return PUGIXML_TEXT(""); -} - -PUGI__FN const char_t *xml_node::child_value(const char_t *name_) const -{ - return child(name_).child_value(); -} - -PUGI__FN xml_attribute xml_node::first_attribute() const -{ - return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); -} - -PUGI__FN xml_attribute xml_node::last_attribute() const -{ - return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); -} - -PUGI__FN xml_node xml_node::first_child() const -{ - return _root ? xml_node(_root->first_child) : xml_node(); -} - -PUGI__FN xml_node xml_node::last_child() const -{ - return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); -} - -PUGI__FN bool xml_node::set_name(const char_t *rhs) -{ - switch (type()) - { - case node_pi: - case node_declaration: - case node_element: - return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs); - - default: - return false; - } -} - -PUGI__FN bool xml_node::set_value(const char_t *rhs) -{ - switch (type()) - { - case node_pi: - case node_cdata: - case node_pcdata: - case node_comment: - case node_doctype: - return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs); - - default: - return false; - } -} - -PUGI__FN xml_attribute xml_node::append_attribute(const char_t *name_) -{ - if (type() != node_element && type() != node_declaration) - return xml_attribute(); - - xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root))); - a.set_name(name_); - - return a; -} - -PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t *name_) -{ - if (type() != node_element && type() != node_declaration) - return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) - return xml_attribute(); - - a.set_name(name_); - - xml_attribute_struct *head = _root->first_attribute; - - if (head) - { - a._attr->prev_attribute_c = head->prev_attribute_c; - head->prev_attribute_c = a._attr; - } - else - a._attr->prev_attribute_c = a._attr; - - a._attr->next_attribute = head; - _root->first_attribute = a._attr; - - return a; -} - -PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t *name_, const xml_attribute &attr) -{ - if ((type() != node_element && type() != node_declaration) || attr.empty()) - return xml_attribute(); - - // check that attribute belongs to *this - xml_attribute_struct *cur = attr._attr; - - while (cur->prev_attribute_c->next_attribute) - cur = cur->prev_attribute_c; - - if (cur != _root->first_attribute) - return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) - return xml_attribute(); - - a.set_name(name_); - - if (attr._attr->prev_attribute_c->next_attribute) - attr._attr->prev_attribute_c->next_attribute = a._attr; - else - _root->first_attribute = a._attr; - - a._attr->prev_attribute_c = attr._attr->prev_attribute_c; - a._attr->next_attribute = attr._attr; - attr._attr->prev_attribute_c = a._attr; - - return a; -} - -PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t *name_, const xml_attribute &attr) -{ - if ((type() != node_element && type() != node_declaration) || attr.empty()) - return xml_attribute(); - - // check that attribute belongs to *this - xml_attribute_struct *cur = attr._attr; - - while (cur->prev_attribute_c->next_attribute) - cur = cur->prev_attribute_c; - - if (cur != _root->first_attribute) - return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) - return xml_attribute(); - - a.set_name(name_); - - if (attr._attr->next_attribute) - attr._attr->next_attribute->prev_attribute_c = a._attr; - else - _root->first_attribute->prev_attribute_c = a._attr; - - a._attr->next_attribute = attr._attr->next_attribute; - a._attr->prev_attribute_c = attr._attr; - attr._attr->next_attribute = a._attr; - - return a; -} - -PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute &proto) -{ - if (!proto) - return xml_attribute(); - - xml_attribute result = append_attribute(proto.name()); - result.set_value(proto.value()); - - return result; -} - -PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute &proto) -{ - if (!proto) - return xml_attribute(); - - xml_attribute result = prepend_attribute(proto.name()); - result.set_value(proto.value()); - - return result; -} - -PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute &proto, const xml_attribute &attr) -{ - if (!proto) - return xml_attribute(); - - xml_attribute result = insert_attribute_after(proto.name(), attr); - result.set_value(proto.value()); - - return result; -} - -PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute &proto, const xml_attribute &attr) -{ - if (!proto) - return xml_attribute(); - - xml_attribute result = insert_attribute_before(proto.name(), attr); - result.set_value(proto.value()); - - return result; -} - -PUGI__FN xml_node xml_node::append_child(xml_node_type type_) -{ - if (!impl::allow_insert_child(this->type(), type_)) - return xml_node(); - - xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_)); - - if (type_ == node_declaration) - n.set_name(PUGIXML_TEXT("xml")); - - return n; -} - -PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) -{ - if (!impl::allow_insert_child(this->type(), type_)) - return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) - return xml_node(); - - n._root->parent = _root; - - xml_node_struct *head = _root->first_child; - - if (head) - { - n._root->prev_sibling_c = head->prev_sibling_c; - head->prev_sibling_c = n._root; - } - else - n._root->prev_sibling_c = n._root; - - n._root->next_sibling = head; - _root->first_child = n._root; - - if (type_ == node_declaration) - n.set_name(PUGIXML_TEXT("xml")); - - return n; -} - -PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node &node) -{ - if (!impl::allow_insert_child(this->type(), type_)) - return xml_node(); - if (!node._root || node._root->parent != _root) - return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) - return xml_node(); - - n._root->parent = _root; - - if (node._root->prev_sibling_c->next_sibling) - node._root->prev_sibling_c->next_sibling = n._root; - else - _root->first_child = n._root; - - n._root->prev_sibling_c = node._root->prev_sibling_c; - n._root->next_sibling = node._root; - node._root->prev_sibling_c = n._root; - - if (type_ == node_declaration) - n.set_name(PUGIXML_TEXT("xml")); - - return n; -} - -PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node &node) -{ - if (!impl::allow_insert_child(this->type(), type_)) - return xml_node(); - if (!node._root || node._root->parent != _root) - return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) - return xml_node(); - - n._root->parent = _root; - - if (node._root->next_sibling) - node._root->next_sibling->prev_sibling_c = n._root; - else - _root->first_child->prev_sibling_c = n._root; - - n._root->next_sibling = node._root->next_sibling; - n._root->prev_sibling_c = node._root; - node._root->next_sibling = n._root; - - if (type_ == node_declaration) - n.set_name(PUGIXML_TEXT("xml")); - - return n; -} - -PUGI__FN xml_node xml_node::append_child(const char_t *name_) -{ - xml_node result = append_child(node_element); - - result.set_name(name_); - - return result; -} - -PUGI__FN xml_node xml_node::prepend_child(const char_t *name_) -{ - xml_node result = prepend_child(node_element); - - result.set_name(name_); - - return result; -} - -PUGI__FN xml_node xml_node::insert_child_after(const char_t *name_, const xml_node &node) -{ - xml_node result = insert_child_after(node_element, node); - - result.set_name(name_); - - return result; -} - -PUGI__FN xml_node xml_node::insert_child_before(const char_t *name_, const xml_node &node) -{ - xml_node result = insert_child_before(node_element, node); - - result.set_name(name_); - - return result; -} - -PUGI__FN xml_node xml_node::append_copy(const xml_node &proto) -{ - xml_node result = append_child(proto.type()); - - if (result) - impl::recursive_copy_skip(result, proto, result); - - return result; -} - -PUGI__FN xml_node xml_node::prepend_copy(const xml_node &proto) -{ - xml_node result = prepend_child(proto.type()); - - if (result) - impl::recursive_copy_skip(result, proto, result); - - return result; -} - -PUGI__FN xml_node xml_node::insert_copy_after(const xml_node &proto, const xml_node &node) -{ - xml_node result = insert_child_after(proto.type(), node); - - if (result) - impl::recursive_copy_skip(result, proto, result); - - return result; -} - -PUGI__FN xml_node xml_node::insert_copy_before(const xml_node &proto, const xml_node &node) -{ - xml_node result = insert_child_before(proto.type(), node); - - if (result) - impl::recursive_copy_skip(result, proto, result); - - return result; -} - -PUGI__FN bool xml_node::remove_attribute(const char_t *name_) -{ - return remove_attribute(attribute(name_)); -} - -PUGI__FN bool xml_node::remove_attribute(const xml_attribute &a) -{ - if (!_root || !a._attr) - return false; - - // check that attribute belongs to *this - xml_attribute_struct *attr = a._attr; - - while (attr->prev_attribute_c->next_attribute) - attr = attr->prev_attribute_c; - - if (attr != _root->first_attribute) - return false; - - if (a._attr->next_attribute) - a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c; - else if (_root->first_attribute) - _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c; - - if (a._attr->prev_attribute_c->next_attribute) - a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute; - else - _root->first_attribute = a._attr->next_attribute; - - impl::destroy_attribute(a._attr, impl::get_allocator(_root)); - - return true; -} - -PUGI__FN bool xml_node::remove_child(const char_t *name_) -{ - return remove_child(child(name_)); -} - -PUGI__FN bool xml_node::remove_child(const xml_node &n) -{ - if (!_root || !n._root || n._root->parent != _root) - return false; - - if (n._root->next_sibling) - n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c; - else if (_root->first_child) - _root->first_child->prev_sibling_c = n._root->prev_sibling_c; - - if (n._root->prev_sibling_c->next_sibling) - n._root->prev_sibling_c->next_sibling = n._root->next_sibling; - else - _root->first_child = n._root->next_sibling; - - impl::destroy_node(n._root, impl::get_allocator(_root)); - - return true; -} - -PUGI__FN xml_parse_result xml_node::append_buffer(const void *contents, size_t size, unsigned int options, - xml_encoding encoding) -{ - // append_buffer is only valid for elements/documents - if (!impl::allow_insert_child(type(), node_element)) - return impl::make_parse_result(status_append_invalid_root); - - // get document node - impl::xml_document_struct *doc = static_cast(root()._root); - assert(doc); - - // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) - impl::xml_memory_page *page = 0; - impl::xml_extra_buffer *extra = - static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); - (void)page; - - if (!extra) - return impl::make_parse_result(status_out_of_memory); - - // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be - // detected at the top level - char_t *rootname = _root->name; - _root->name = 0; - - // parse - char_t *buffer = 0; - xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, - false, false, &buffer); - - // restore name - _root->name = rootname; - - // add extra buffer to the list - extra->buffer = buffer; - extra->next = doc->extra_buffers; - doc->extra_buffers = extra; - - return res; -} - -PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t *name_, const char_t *attr_name, - const char_t *attr_value) const -{ - if (!_root) - return xml_node(); - - for (xml_node_struct *i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) - { - for (xml_attribute_struct *a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && - impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - } - - return xml_node(); -} - -PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t *attr_name, const char_t *attr_value) const -{ - if (!_root) - return xml_node(); - - for (xml_node_struct *i = _root->first_child; i; i = i->next_sibling) - for (xml_attribute_struct *a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && - impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - - return xml_node(); -} - -#ifndef PUGIXML_NO_STL -PUGI__FN string_t xml_node::path(char_t delimiter) const -{ - xml_node cursor = *this; // Make a copy. - - string_t result = cursor.name(); - - while (cursor.parent()) - { - cursor = cursor.parent(); - - string_t temp = cursor.name(); - temp += delimiter; - temp += result; - result.swap(temp); - } - - return result; -} -#endif - -PUGI__FN xml_node xml_node::first_element_by_path(const char_t *path_, char_t delimiter) const -{ - xml_node found = *this; // Current search context. - - if (!_root || !path_ || !path_[0]) - return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } - - const char_t *path_segment = path_; - - while (*path_segment == delimiter) - ++path_segment; - - const char_t *path_segment_end = path_segment; - - while (*path_segment_end && *path_segment_end != delimiter) - ++path_segment_end; - - if (path_segment == path_segment_end) - return found; - - const char_t *next_segment = path_segment_end; - - while (*next_segment == delimiter) - ++next_segment; - - if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); - else if (*path_segment == '.' && *(path_segment + 1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); - else - { - for (xml_node_struct *j = found._root->first_child; j; j = j->next_sibling) - { - if (j->name && - impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) - { - xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); - - if (subsearch) - return subsearch; - } - } - - return xml_node(); - } -} - -PUGI__FN bool xml_node::traverse(xml_tree_walker &walker) -{ - walker._depth = -1; - - xml_node arg_begin = *this; - if (!walker.begin(arg_begin)) - return false; - - xml_node cur = first_child(); - - if (cur) - { - ++walker._depth; - - do - { - xml_node arg_for_each = cur; - if (!walker.for_each(arg_for_each)) - return false; - - if (cur.first_child()) - { - ++walker._depth; - cur = cur.first_child(); - } - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - // Borland C++ workaround - while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) - { - --walker._depth; - cur = cur.parent(); - } - - if (cur != *this) - cur = cur.next_sibling(); - } - } while (cur && cur != *this); - } - - assert(walker._depth == -1); - - xml_node arg_end = *this; - return walker.end(arg_end); -} - -PUGI__FN size_t xml_node::hash_value() const -{ - return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); -} - -PUGI__FN xml_node_struct *xml_node::internal_object() const -{ - return _root; -} - -PUGI__FN void xml_node::print(xml_writer &writer, const char_t *indent, unsigned int flags, xml_encoding encoding, - unsigned int depth) const -{ - if (!_root) - return; - - impl::xml_buffered_writer buffered_writer(writer, encoding); - - impl::node_output(buffered_writer, *this, indent, flags, depth); -} - -#ifndef PUGIXML_NO_STL -PUGI__FN void xml_node::print(std::basic_ostream> &stream, const char_t *indent, - unsigned int flags, xml_encoding encoding, unsigned int depth) const -{ - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding, depth); -} - -PUGI__FN void xml_node::print(std::basic_ostream> &stream, const char_t *indent, - unsigned int flags, unsigned int depth) const -{ - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding_wchar, depth); -} -#endif - -PUGI__FN ptrdiff_t xml_node::offset_debug() const -{ - xml_node_struct *r = root()._root; - - if (!r) - return -1; - - const char_t *buffer = static_cast(r)->buffer; - - if (!buffer) - return -1; - - switch (type()) - { - case node_document: - return 0; - - case node_element: - case node_declaration: - case node_pi: - return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer; - - case node_pcdata: - case node_cdata: - case node_comment: - case node_doctype: - return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer; - - default: - return -1; - } -} - -#ifdef __BORLANDC__ -PUGI__FN bool operator&&(const xml_node &lhs, bool rhs) -{ - return (bool)lhs && rhs; -} - -PUGI__FN bool operator||(const xml_node &lhs, bool rhs) -{ - return (bool)lhs || rhs; -} -#endif - -PUGI__FN xml_text::xml_text(xml_node_struct *root) : _root(root) {} - -PUGI__FN xml_node_struct *xml_text::_data() const -{ - if (!_root || impl::is_text_node(_root)) - return _root; - - for (xml_node_struct *node = _root->first_child; node; node = node->next_sibling) - if (impl::is_text_node(node)) - return node; - - return 0; -} - -PUGI__FN xml_node_struct *xml_text::_data_new() -{ - xml_node_struct *d = _data(); - if (d) - return d; - - return xml_node(_root).append_child(node_pcdata).internal_object(); -} - -PUGI__FN xml_text::xml_text() : _root(0) {} - -PUGI__FN static void unspecified_bool_xml_text(xml_text ***) {} - -PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const -{ - return _data() ? unspecified_bool_xml_text : 0; -} - -PUGI__FN bool xml_text::operator!() const -{ - return !_data(); -} - -PUGI__FN bool xml_text::empty() const -{ - return _data() == 0; -} - -PUGI__FN const char_t *xml_text::get() const -{ - xml_node_struct *d = _data(); - - return (d && d->value) ? d->value : PUGIXML_TEXT(""); -} - -PUGI__FN const char_t *xml_text::as_string(const char_t *def) const -{ - xml_node_struct *d = _data(); - - return (d && d->value) ? d->value : def; -} - -PUGI__FN int xml_text::as_int(int def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_int(d ? d->value : 0, def); -} - -PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_uint(d ? d->value : 0, def); -} - -PUGI__FN double xml_text::as_double(double def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_double(d ? d->value : 0, def); -} - -PUGI__FN float xml_text::as_float(float def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_float(d ? d->value : 0, def); -} - -PUGI__FN bool xml_text::as_bool(bool def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_bool(d ? d->value : 0, def); -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN long long xml_text::as_llong(long long def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_llong(d ? d->value : 0, def); -} - -PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const -{ - xml_node_struct *d = _data(); - - return impl::get_value_ullong(d ? d->value : 0, def); -} -#endif - -PUGI__FN bool xml_text::set(const char_t *rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -PUGI__FN bool xml_text::set(int rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -PUGI__FN bool xml_text::set(unsigned int rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -PUGI__FN bool xml_text::set(double rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -PUGI__FN bool xml_text::set(bool rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN bool xml_text::set(long long rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} - -PUGI__FN bool xml_text::set(unsigned long long rhs) -{ - xml_node_struct *dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; -} -#endif - -PUGI__FN xml_text &xml_text::operator=(const char_t *rhs) -{ - set(rhs); - return *this; -} - -PUGI__FN xml_text &xml_text::operator=(int rhs) -{ - set(rhs); - return *this; -} - -PUGI__FN xml_text &xml_text::operator=(unsigned int rhs) -{ - set(rhs); - return *this; -} - -PUGI__FN xml_text &xml_text::operator=(double rhs) -{ - set(rhs); - return *this; -} - -PUGI__FN xml_text &xml_text::operator=(bool rhs) -{ - set(rhs); - return *this; -} - -#ifdef PUGIXML_HAS_LONG_LONG -PUGI__FN xml_text &xml_text::operator=(long long rhs) -{ - set(rhs); - return *this; -} - -PUGI__FN xml_text &xml_text::operator=(unsigned long long rhs) -{ - set(rhs); - return *this; -} -#endif - -PUGI__FN xml_node xml_text::data() const -{ - return xml_node(_data()); -} - -#ifdef __BORLANDC__ -PUGI__FN bool operator&&(const xml_text &lhs, bool rhs) -{ - return (bool)lhs && rhs; -} - -PUGI__FN bool operator||(const xml_text &lhs, bool rhs) -{ - return (bool)lhs || rhs; -} -#endif - -PUGI__FN xml_node_iterator::xml_node_iterator() {} - -PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node &node) : _wrap(node), _parent(node.parent()) {} - -PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct *ref, xml_node_struct *parent) - : _wrap(ref), _parent(parent) -{ -} - -PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator &rhs) const -{ - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; -} - -PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator &rhs) const -{ - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; -} - -PUGI__FN xml_node &xml_node_iterator::operator*() const -{ - assert(_wrap._root); - return _wrap; -} - -PUGI__FN xml_node *xml_node_iterator::operator->() const -{ - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround -} - -PUGI__FN const xml_node_iterator &xml_node_iterator::operator++() -{ - assert(_wrap._root); - _wrap._root = _wrap._root->next_sibling; - return *this; -} - -PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) -{ - xml_node_iterator temp = *this; - ++*this; - return temp; -} - -PUGI__FN const xml_node_iterator &xml_node_iterator::operator--() -{ - _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); - return *this; -} - -PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) -{ - xml_node_iterator temp = *this; - --*this; - return temp; -} - -PUGI__FN xml_attribute_iterator::xml_attribute_iterator() {} - -PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute &attr, const xml_node &parent) - : _wrap(attr), _parent(parent) -{ -} - -PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct *ref, xml_node_struct *parent) - : _wrap(ref), _parent(parent) -{ -} - -PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator &rhs) const -{ - return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; -} - -PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator &rhs) const -{ - return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; -} - -PUGI__FN xml_attribute &xml_attribute_iterator::operator*() const -{ - assert(_wrap._attr); - return _wrap; -} - -PUGI__FN xml_attribute *xml_attribute_iterator::operator->() const -{ - assert(_wrap._attr); - return const_cast(&_wrap); // BCC32 workaround -} - -PUGI__FN const xml_attribute_iterator &xml_attribute_iterator::operator++() -{ - assert(_wrap._attr); - _wrap._attr = _wrap._attr->next_attribute; - return *this; -} - -PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) -{ - xml_attribute_iterator temp = *this; - ++*this; - return temp; -} - -PUGI__FN const xml_attribute_iterator &xml_attribute_iterator::operator--() -{ - _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); - return *this; -} - -PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) -{ - xml_attribute_iterator temp = *this; - --*this; - return temp; -} - -PUGI__FN xml_named_node_iterator::xml_named_node_iterator() : _name(0) {} - -PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node &node, const char_t *name) - : _wrap(node), _parent(node.parent()), _name(name) -{ -} - -PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct *ref, xml_node_struct *parent, - const char_t *name) - : _wrap(ref), _parent(parent), _name(name) -{ -} - -PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator &rhs) const -{ - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; -} - -PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator &rhs) const -{ - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; -} - -PUGI__FN xml_node &xml_named_node_iterator::operator*() const -{ - assert(_wrap._root); - return _wrap; -} - -PUGI__FN xml_node *xml_named_node_iterator::operator->() const -{ - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround -} - -PUGI__FN const xml_named_node_iterator &xml_named_node_iterator::operator++() -{ - assert(_wrap._root); - _wrap = _wrap.next_sibling(_name); - return *this; -} - -PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) -{ - xml_named_node_iterator temp = *this; - ++*this; - return temp; -} - -PUGI__FN const xml_named_node_iterator &xml_named_node_iterator::operator--() -{ - if (_wrap._root) - _wrap = _wrap.previous_sibling(_name); - else - { - _wrap = _parent.last_child(); - - if (!impl::strequal(_wrap.name(), _name)) - _wrap = _wrap.previous_sibling(_name); - } - - return *this; -} - -PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) -{ - xml_named_node_iterator temp = *this; - --*this; - return temp; -} - -PUGI__FN xml_parse_result::xml_parse_result() : status(status_internal_error), offset(0), encoding(encoding_auto) {} - -PUGI__FN xml_parse_result::operator bool() const -{ - return status == status_ok; -} - -PUGI__FN const char *xml_parse_result::description() const -{ - switch (status) - { - case status_ok: - return "No error"; - - case status_file_not_found: - return "File was not found"; - case status_io_error: - return "Error reading from file/stream"; - case status_out_of_memory: - return "Could not allocate memory"; - case status_internal_error: - return "Internal error occurred"; - - case status_unrecognized_tag: - return "Could not determine tag type"; - - case status_bad_pi: - return "Error parsing document declaration/processing instruction"; - case status_bad_comment: - return "Error parsing comment"; - case status_bad_cdata: - return "Error parsing CDATA section"; - case status_bad_doctype: - return "Error parsing document type declaration"; - case status_bad_pcdata: - return "Error parsing PCDATA section"; - case status_bad_start_element: - return "Error parsing start element tag"; - case status_bad_attribute: - return "Error parsing element attribute"; - case status_bad_end_element: - return "Error parsing end element tag"; - case status_end_element_mismatch: - return "Start-end tags mismatch"; - - case status_append_invalid_root: - return "Unable to append nodes: root is not an element or document"; - - case status_no_document_element: - return "No document element found"; - - default: - return "Unknown error"; - } -} - -PUGI__FN xml_document::xml_document() : _buffer(0) -{ - create(); -} - -PUGI__FN xml_document::~xml_document() -{ - destroy(); -} - -PUGI__FN void xml_document::reset() -{ - destroy(); - create(); -} - -PUGI__FN void xml_document::reset(const xml_document &proto) -{ - reset(); - - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); -} - -PUGI__FN void xml_document::create() -{ - assert(!_root); - - // initialize sentinel page - PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + - impl::xml_memory_page_alignment <= - sizeof(_memory)); - - // align upwards to page boundary - void *page_memory = - reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & - ~(impl::xml_memory_page_alignment - 1)); - - // prepare page structure - impl::xml_memory_page *page = impl::xml_memory_page::construct(page_memory); - assert(page); - - page->busy_size = impl::xml_memory_page_size; - - // allocate new root - _root = new (page->data) impl::xml_document_struct(page); - _root->prev_sibling_c = _root; - - // setup sentinel page - page->allocator = static_cast(_root); -} - -PUGI__FN void xml_document::destroy() -{ - assert(_root); - - // destroy static storage - if (_buffer) - { - impl::xml_memory::deallocate(_buffer); - _buffer = 0; - } - - // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) - for (impl::xml_extra_buffer *extra = static_cast(_root)->extra_buffers; extra; - extra = extra->next) - { - if (extra->buffer) - impl::xml_memory::deallocate(extra->buffer); - } - - // destroy dynamic storage, leave sentinel page (it's in static memory) - impl::xml_memory_page *root_page = - reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); - assert(root_page && !root_page->prev && !root_page->memory); - - for (impl::xml_memory_page *page = root_page->next; page;) - { - impl::xml_memory_page *next = page->next; - - impl::xml_allocator::deallocate_page(page); - - page = next; - } - - _root = 0; -} - -#ifndef PUGIXML_NO_STL -PUGI__FN xml_parse_result xml_document::load(std::basic_istream> &stream, - unsigned int options, xml_encoding encoding) -{ - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding); -} - -PUGI__FN xml_parse_result xml_document::load(std::basic_istream> &stream, - unsigned int options) -{ - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding_wchar); -} -#endif - -PUGI__FN xml_parse_result xml_document::load(const char_t *contents, unsigned int options) -{ - // Force native encoding (skip autodetection) -#ifdef PUGIXML_WCHAR_MODE - xml_encoding encoding = encoding_wchar; -#else - xml_encoding encoding = encoding_utf8; -#endif - - return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); -} - -PUGI__FN xml_parse_result xml_document::load_file(const char *path_, unsigned int options, xml_encoding encoding) -{ - reset(); - - FILE *file = fopen(path_, "rb"); - - return impl::load_file_impl(*this, file, options, encoding); -} - -PUGI__FN xml_parse_result xml_document::load_file(const wchar_t *path_, unsigned int options, xml_encoding encoding) -{ - reset(); - - FILE *file = impl::open_file_wide(path_, L"rb"); - - return impl::load_file_impl(*this, file, options, encoding); -} - -PUGI__FN xml_parse_result xml_document::load_buffer(const void *contents, size_t size, unsigned int options, - xml_encoding encoding) -{ - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), - size, options, encoding, false, false, &_buffer); -} - -PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void *contents, size_t size, unsigned int options, - xml_encoding encoding) -{ - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, - encoding, true, false, &_buffer); -} - -PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void *contents, size_t size, unsigned int options, - xml_encoding encoding) -{ - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, - encoding, true, true, &_buffer); -} - -PUGI__FN void xml_document::save(xml_writer &writer, const char_t *indent, unsigned int flags, - xml_encoding encoding) const -{ - impl::xml_buffered_writer buffered_writer(writer, encoding); - - if ((flags & format_write_bom) && encoding != encoding_latin1) - { - // BOM always represents the codepoint U+FEFF, so just write it in native encoding -#ifdef PUGIXML_WCHAR_MODE - unsigned int bom = 0xfeff; - buffered_writer.write(static_cast(bom)); -#else - buffered_writer.write('\xef', '\xbb', '\xbf'); -#endif - } - - if (!(flags & format_no_declaration) && !impl::has_declaration(*this)) - { - buffered_writer.write(PUGIXML_TEXT("'); - if (!(flags & format_raw)) - buffered_writer.write('\n'); - } - - impl::node_output(buffered_writer, *this, indent, flags, 0); -} - -#ifndef PUGIXML_NO_STL -PUGI__FN void xml_document::save(std::basic_ostream> &stream, const char_t *indent, - unsigned int flags, xml_encoding encoding) const -{ - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding); -} - -PUGI__FN void xml_document::save(std::basic_ostream> &stream, const char_t *indent, - unsigned int flags) const -{ - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding_wchar); -} -#endif - -PUGI__FN bool xml_document::save_file(const char *path_, const char_t *indent, unsigned int flags, - xml_encoding encoding) const -{ - FILE *file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); -} - -PUGI__FN bool xml_document::save_file(const wchar_t *path_, const char_t *indent, unsigned int flags, - xml_encoding encoding) const -{ - FILE *file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); -} - -PUGI__FN xml_node xml_document::document_element() const -{ - assert(_root); - - for (xml_node_struct *i = _root->first_child; i; i = i->next_sibling) - if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element) - return xml_node(i); - - return xml_node(); -} - -#ifndef PUGIXML_NO_STL -PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t *str) -{ - assert(str); - - return impl::as_utf8_impl(str, impl::strlength_wide(str)); -} - -PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string &str) -{ - return impl::as_utf8_impl(str.c_str(), str.size()); -} - -PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char *str) -{ - assert(str); - - return impl::as_wide_impl(str, strlen(str)); -} - -PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string &str) -{ - return impl::as_wide_impl(str.c_str(), str.size()); -} -#endif - -PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, - deallocation_function deallocate) -{ - impl::xml_memory::allocate = allocate; - impl::xml_memory::deallocate = deallocate; -} - -PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() -{ - return impl::xml_memory::allocate; -} - -PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() -{ - return impl::xml_memory::deallocate; -} -} // namespace pugi - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ -// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) -PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator &) -{ - return std::bidirectional_iterator_tag(); -} - -PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator &) -{ - return std::bidirectional_iterator_tag(); -} - -PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator &) -{ - return std::bidirectional_iterator_tag(); -} -} // namespace std -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ -// Workarounds for (non-standard) iterator category detection -PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator &) -{ - return std::bidirectional_iterator_tag(); -} - -PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator &) -{ - return std::bidirectional_iterator_tag(); -} - -PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator &) -{ - return std::bidirectional_iterator_tag(); -} -} // namespace std -#endif - -#ifndef PUGIXML_NO_XPATH - -// STL replacements -PUGI__NS_BEGIN -struct equal_to -{ - template - bool operator()(const T &lhs, const T &rhs) const - { - return lhs == rhs; - } -}; - -struct not_equal_to -{ - template - bool operator()(const T &lhs, const T &rhs) const - { - return lhs != rhs; - } -}; - -struct less -{ - template - bool operator()(const T &lhs, const T &rhs) const - { - return lhs < rhs; - } -}; - -struct less_equal -{ - template - bool operator()(const T &lhs, const T &rhs) const - { - return lhs <= rhs; - } -}; - -template -void swap(T &lhs, T &rhs) -{ - T temp = lhs; - lhs = rhs; - rhs = temp; -} - -template -I min_element(I begin, I end, const Pred &pred) -{ - I result = begin; - - for (I it = begin + 1; it != end; ++it) - if (pred(*it, *result)) - result = it; - - return result; -} - -template -void reverse(I begin, I end) -{ - while (end - begin > 1) - swap(*begin++, *--end); -} - -template -I unique(I begin, I end) -{ - // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) - begin++; - - if (begin == end) - return begin; - - // last written element - I write = begin++; - - // merge unique elements - while (begin != end) - { - if (*begin != *write) - *++write = *begin++; - else - begin++; - } - - // past-the-end (write points to live element) - return write + 1; -} - -template -void copy_backwards(I begin, I end, I target) -{ - while (begin != end) - *--target = *--end; -} - -template -void insertion_sort(I begin, I end, const Pred &pred, T *) -{ - assert(begin != end); - - for (I it = begin + 1; it != end; ++it) - { - T val = *it; - - if (pred(val, *begin)) - { - // move to front - copy_backwards(begin, it, it + 1); - *begin = val; - } - else - { - I hole = it; - - // move hole backwards - while (pred(val, *(hole - 1))) - { - *hole = *(hole - 1); - hole--; - } - - // fill hole with element - *hole = val; - } - } -} - -// std variant for elements with == -template -void partition(I begin, I middle, I end, const Pred &pred, I *out_eqbeg, I *out_eqend) -{ - I eqbeg = middle, eqend = middle + 1; - - // expand equal range - while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) - --eqbeg; - while (eqend != end && *eqend == *eqbeg) - ++eqend; - - // process outer elements - I ltend = eqbeg, gtbeg = eqend; - - for (;;) - { - // find the element from the right side that belongs to the left one - for (; gtbeg != end; ++gtbeg) - if (!pred(*eqbeg, *gtbeg)) - { - if (*gtbeg == *eqbeg) - swap(*gtbeg, *eqend++); - else - break; - } - - // find the element from the left side that belongs to the right one - for (; ltend != begin; --ltend) - if (!pred(*(ltend - 1), *eqbeg)) - { - if (*eqbeg == *(ltend - 1)) - swap(*(ltend - 1), *--eqbeg); - else - break; - } - - // scanned all elements - if (gtbeg == end && ltend == begin) - { - *out_eqbeg = eqbeg; - *out_eqend = eqend; - return; - } - - // make room for elements by moving equal area - if (gtbeg == end) - { - if (--ltend != --eqbeg) - swap(*ltend, *eqbeg); - swap(*eqbeg, *--eqend); - } - else if (ltend == begin) - { - if (eqend != gtbeg) - swap(*eqbeg, *eqend); - ++eqend; - swap(*gtbeg++, *eqbeg++); - } - else - swap(*gtbeg++, *--ltend); - } -} - -template -void median3(I first, I middle, I last, const Pred &pred) -{ - if (pred(*middle, *first)) - swap(*middle, *first); - if (pred(*last, *middle)) - swap(*last, *middle); - if (pred(*middle, *first)) - swap(*middle, *first); -} - -template -void median(I first, I middle, I last, const Pred &pred) -{ - if (last - first <= 40) - { - // median of three for small chunks - median3(first, middle, last, pred); - } - else - { - // median of nine - size_t step = (last - first + 1) / 8; - - median3(first, first + step, first + 2 * step, pred); - median3(middle - step, middle, middle + step, pred); - median3(last - 2 * step, last - step, last, pred); - median3(first + step, middle, last - step, pred); - } -} - -template -void sort(I begin, I end, const Pred &pred) -{ - // sort large chunks - while (end - begin > 32) - { - // find median element - I middle = begin + (end - begin) / 2; - median(begin, middle, end - 1, pred); - - // partition in three chunks (< = >) - I eqbeg, eqend; - partition(begin, middle, end, pred, &eqbeg, &eqend); - - // loop on larger half - if (eqbeg - begin > end - eqend) - { - sort(eqend, end, pred); - end = eqbeg; - } - else - { - sort(begin, eqbeg, pred); - begin = eqend; - } - } - - // insertion sort small chunk - if (begin != end) - insertion_sort(begin, end, pred, &*begin); -} -PUGI__NS_END - -// Allocator used for AST and evaluation stacks -PUGI__NS_BEGIN -struct xpath_memory_block -{ - xpath_memory_block *next; - - char data[ -#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE - PUGIXML_MEMORY_XPATH_PAGE_SIZE -#else - 4096 -#endif - ]; -}; - -class xpath_allocator -{ - xpath_memory_block *_root; - size_t _root_size; - - public: -#ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf *error_handler; -#endif - - xpath_allocator(xpath_memory_block *root, size_t root_size = 0) : _root(root), _root_size(root_size) - { -#ifdef PUGIXML_NO_EXCEPTIONS - error_handler = 0; -#endif - } - - void *allocate_nothrow(size_t size) - { - const size_t block_capacity = sizeof(_root->data); - - // align size so that we're able to store pointers in subsequent blocks - size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1); - - if (_root_size + size <= block_capacity) - { - void *buf = _root->data + _root_size; - _root_size += size; - return buf; - } - else - { - size_t block_data_size = (size > block_capacity) ? size : block_capacity; - size_t block_size = block_data_size + offsetof(xpath_memory_block, data); - - xpath_memory_block *block = static_cast(xml_memory::allocate(block_size)); - if (!block) - return 0; - - block->next = _root; - - _root = block; - _root_size = size; - - return block->data; - } - } - - void *allocate(size_t size) - { - void *result = allocate_nothrow(size); - - if (!result) - { -#ifdef PUGIXML_NO_EXCEPTIONS - assert(error_handler); - longjmp(*error_handler, 1); -#else - throw std::bad_alloc(); -#endif - } - - return result; - } - - void *reallocate(void *ptr, size_t old_size, size_t new_size) - { - // align size so that we're able to store pointers in subsequent blocks - old_size = (old_size + sizeof(void *) - 1) & ~(sizeof(void *) - 1); - new_size = (new_size + sizeof(void *) - 1) & ~(sizeof(void *) - 1); - - // we can only reallocate the last object - assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); - - // adjust root size so that we have not allocated the object at all - bool only_object = (_root_size == old_size); - - if (ptr) - _root_size -= old_size; - - // allocate a new version (this will obviously reuse the memory if possible) - void *result = allocate(new_size); - assert(result); - - // we have a new block - if (result != ptr && ptr) - { - // copy old data - assert(new_size >= old_size); - memcpy(result, ptr, old_size); - - // free the previous page if it had no other objects - if (only_object) - { - assert(_root->data == result); - assert(_root->next); - - xpath_memory_block *next = _root->next->next; - - if (next) - { - // deallocate the whole page, unless it was the first one - xml_memory::deallocate(_root->next); - _root->next = next; - } - } - } - - return result; - } - - void revert(const xpath_allocator &state) - { - // free all new pages - xpath_memory_block *cur = _root; - - while (cur != state._root) - { - xpath_memory_block *next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - - // restore state - _root = state._root; - _root_size = state._root_size; - } - - void release() - { - xpath_memory_block *cur = _root; - assert(cur); - - while (cur->next) - { - xpath_memory_block *next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - } -}; - -struct xpath_allocator_capture -{ - xpath_allocator_capture(xpath_allocator *alloc) : _target(alloc), _state(*alloc) {} - - ~xpath_allocator_capture() { _target->revert(_state); } - - xpath_allocator *_target; - xpath_allocator _state; -}; - -struct xpath_stack -{ - xpath_allocator *result; - xpath_allocator *temp; -}; - -struct xpath_stack_data -{ - xpath_memory_block blocks[2]; - xpath_allocator result; - xpath_allocator temp; - xpath_stack stack; - -#ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf error_handler; -#endif - - xpath_stack_data() : result(blocks + 0), temp(blocks + 1) - { - blocks[0].next = blocks[1].next = 0; - - stack.result = &result; - stack.temp = &temp; - -#ifdef PUGIXML_NO_EXCEPTIONS - result.error_handler = temp.error_handler = &error_handler; -#endif - } - - ~xpath_stack_data() - { - result.release(); - temp.release(); - } -}; -PUGI__NS_END - -// String class -PUGI__NS_BEGIN -class xpath_string -{ - const char_t *_buffer; - bool _uses_heap; - - static char_t *duplicate_string(const char_t *string, size_t length, xpath_allocator *alloc) - { - char_t *result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); - assert(result); - - memcpy(result, string, length * sizeof(char_t)); - result[length] = 0; - - return result; - } - - static char_t *duplicate_string(const char_t *string, xpath_allocator *alloc) - { - return duplicate_string(string, strlength(string), alloc); - } - - public: - xpath_string() : _buffer(PUGIXML_TEXT("")), _uses_heap(false) {} - - explicit xpath_string(const char_t *str, xpath_allocator *alloc) - { - bool empty_ = (*str == 0); - - _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc); - _uses_heap = !empty_; - } - - explicit xpath_string(const char_t *str, bool use_heap) : _buffer(str), _uses_heap(use_heap) {} - - xpath_string(const char_t *begin, const char_t *end, xpath_allocator *alloc) - { - assert(begin <= end); - - bool empty_ = (begin == end); - - _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast(end - begin), alloc); - _uses_heap = !empty_; - } - - void append(const xpath_string &o, xpath_allocator *alloc) - { - // skip empty sources - if (!*o._buffer) - return; - - // fast append for constant empty target and constant source - if (!*_buffer && !_uses_heap && !o._uses_heap) - { - _buffer = o._buffer; - } - else - { - // need to make heap copy - size_t target_length = strlength(_buffer); - size_t source_length = strlength(o._buffer); - size_t result_length = target_length + source_length; - - // allocate new buffer - char_t *result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, - (target_length + 1) * sizeof(char_t), - (result_length + 1) * sizeof(char_t))); - assert(result); - - // append first string to the new buffer in case there was no reallocation - if (!_uses_heap) - memcpy(result, _buffer, target_length * sizeof(char_t)); - - // append second string to the new buffer - memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); - result[result_length] = 0; - - // finalize - _buffer = result; - _uses_heap = true; - } - } - - const char_t *c_str() const { return _buffer; } - - size_t length() const { return strlength(_buffer); } - - char_t *data(xpath_allocator *alloc) - { - // make private heap copy - if (!_uses_heap) - { - _buffer = duplicate_string(_buffer, alloc); - _uses_heap = true; - } - - return const_cast(_buffer); - } - - bool empty() const { return *_buffer == 0; } - - bool operator==(const xpath_string &o) const { return strequal(_buffer, o._buffer); } - - bool operator!=(const xpath_string &o) const { return !strequal(_buffer, o._buffer); } - - bool uses_heap() const { return _uses_heap; } -}; - -PUGI__FN xpath_string xpath_string_const(const char_t *str) -{ - return xpath_string(str, false); -} -PUGI__NS_END - -PUGI__NS_BEGIN -PUGI__FN bool starts_with(const char_t *string, const char_t *pattern) -{ - while (*pattern && *string == *pattern) - { - string++; - pattern++; - } - - return *pattern == 0; -} - -PUGI__FN const char_t *find_char(const char_t *s, char_t c) -{ -#ifdef PUGIXML_WCHAR_MODE - return wcschr(s, c); -#else - return strchr(s, c); -#endif -} - -PUGI__FN const char_t *find_substring(const char_t *s, const char_t *p) -{ -#ifdef PUGIXML_WCHAR_MODE - // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) - return (*p == 0) ? s : wcsstr(s, p); -#else - return strstr(s, p); -#endif -} - -// Converts symbol to lower case, if it is an ASCII one -PUGI__FN char_t tolower_ascii(char_t ch) -{ - return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; -} - -PUGI__FN xpath_string string_value(const xpath_node &na, xpath_allocator *alloc) -{ - if (na.attribute()) - return xpath_string_const(na.attribute().value()); - else - { - const xml_node &n = na.node(); - - switch (n.type()) - { - case node_pcdata: - case node_cdata: - case node_comment: - case node_pi: - return xpath_string_const(n.value()); - - case node_document: - case node_element: - { - xpath_string result; - - xml_node cur = n.first_child(); - - while (cur && cur != n) - { - if (cur.type() == node_pcdata || cur.type() == node_cdata) - result.append(xpath_string_const(cur.value()), alloc); - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur != n) - cur = cur.parent(); - - if (cur != n) - cur = cur.next_sibling(); - } - } - - return result; - } - - default: - return xpath_string(); - } - } -} - -PUGI__FN unsigned int node_height(xml_node n) -{ - unsigned int result = 0; - - while (n) - { - ++result; - n = n.parent(); - } - - return result; -} - -PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh) -{ - // normalize heights - for (unsigned int i = rh; i < lh; i++) - ln = ln.parent(); - for (unsigned int j = lh; j < rh; j++) - rn = rn.parent(); - - // one node is the ancestor of the other - if (ln == rn) - return lh < rh; - - // find common ancestor - while (ln.parent() != rn.parent()) - { - ln = ln.parent(); - rn = rn.parent(); - } - - // there is no common ancestor (the shared parent is null), nodes are from different documents - if (!ln.parent()) - return ln < rn; - - // determine sibling order - for (; ln; ln = ln.next_sibling()) - if (ln == rn) - return true; - - return false; -} - -PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node) -{ - while (node && node != parent) - node = node.parent(); - - return parent && node == parent; -} - -PUGI__FN const void *document_order(const xpath_node &xnode) -{ - xml_node_struct *node = xnode.node().internal_object(); - - if (node) - { - if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) - return node->name; - if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) - return node->value; - return 0; - } - - xml_attribute_struct *attr = xnode.attribute().internal_object(); - - if (attr) - { - if ((attr->header & xml_memory_page_name_allocated_mask) == 0) - return attr->name; - if ((attr->header & xml_memory_page_value_allocated_mask) == 0) - return attr->value; - return 0; - } - - return 0; -} - -struct document_order_comparator -{ - bool operator()(const xpath_node &lhs, const xpath_node &rhs) const - { - // optimized document order based check - const void *lo = document_order(lhs); - const void *ro = document_order(rhs); - - if (lo && ro) - return lo < ro; - - // slow comparison - xml_node ln = lhs.node(), rn = rhs.node(); - - // compare attributes - if (lhs.attribute() && rhs.attribute()) - { - // shared parent - if (lhs.parent() == rhs.parent()) - { - // determine sibling order - for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) - if (a == rhs.attribute()) - return true; - - return false; - } - - // compare attribute parents - ln = lhs.parent(); - rn = rhs.parent(); - } - else if (lhs.attribute()) - { - // attributes go after the parent element - if (lhs.parent() == rhs.node()) - return false; - - ln = lhs.parent(); - } - else if (rhs.attribute()) - { - // attributes go after the parent element - if (rhs.parent() == lhs.node()) - return true; - - rn = rhs.parent(); - } - - if (ln == rn) - return false; - - unsigned int lh = node_height(ln); - unsigned int rh = node_height(rn); - - return node_is_before(ln, lh, rn, rh); - } -}; - -struct duplicate_comparator -{ - bool operator()(const xpath_node &lhs, const xpath_node &rhs) const - { - if (lhs.attribute()) - return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else - return rhs.attribute() ? false : lhs.node() < rhs.node(); - } -}; - -PUGI__FN double gen_nan() -{ -#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) - union { - float f; - uint32_t i; - } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; - u[0].i = 0x7fc00000; - return u[0].f; -#else - // fallback - const volatile double zero = 0.0; - return zero / zero; -#endif -} - -PUGI__FN bool is_nan(double value) -{ -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - return !!_isnan(value); -#elif defined(fpclassify) && defined(FP_NAN) - return fpclassify(value) == FP_NAN; -#else - // fallback - const volatile double v = value; - return v != v; -#endif -} - -PUGI__FN const char_t *convert_number_to_string_special(double value) -{ -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - if (_finite(value)) - return (value == 0) ? PUGIXML_TEXT("0") : 0; - if (_isnan(value)) - return PUGIXML_TEXT("NaN"); - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); -#elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) - switch (fpclassify(value)) - { - case FP_NAN: - return PUGIXML_TEXT("NaN"); - - case FP_INFINITE: - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - - case FP_ZERO: - return PUGIXML_TEXT("0"); - - default: - return 0; - } -#else - // fallback - const volatile double v = value; - - if (v == 0) - return PUGIXML_TEXT("0"); - if (v != v) - return PUGIXML_TEXT("NaN"); - if (v * 2 == v) - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - return 0; -#endif -} - -PUGI__FN bool convert_number_to_boolean(double value) -{ - return (value != 0 && !is_nan(value)); -} - -PUGI__FN void truncate_zeros(char *begin, char *end) -{ - while (begin != end && end[-1] == '0') - end--; - - *end = 0; -} - -// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent -#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) -PUGI__FN void convert_number_to_mantissa_exponent(double value, char *buffer, size_t buffer_size, char **out_mantissa, - int *out_exponent) -{ - // get base values - int sign, exponent; - _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); - - // truncate redundant zeros - truncate_zeros(buffer, buffer + strlen(buffer)); - - // fill results - *out_mantissa = buffer; - *out_exponent = exponent; -} -#else -PUGI__FN void convert_number_to_mantissa_exponent(double value, char *buffer, size_t buffer_size, char **out_mantissa, - int *out_exponent) -{ - // get a scientific notation value with IEEE DBL_DIG decimals - sprintf(buffer, "%.*e", DBL_DIG, value); - assert(strlen(buffer) < buffer_size); - (void)!buffer_size; - - // get the exponent (possibly negative) - char *exponent_string = strchr(buffer, 'e'); - assert(exponent_string); - - int exponent = atoi(exponent_string + 1); - - // extract mantissa string: skip sign - char *mantissa = buffer[0] == '-' ? buffer + 1 : buffer; - assert(mantissa[0] != '0' && mantissa[1] == '.'); - - // divide mantissa by 10 to eliminate integer part - mantissa[1] = mantissa[0]; - mantissa++; - exponent++; - - // remove extra mantissa digits and zero-terminate mantissa - truncate_zeros(mantissa, exponent_string); - - // fill results - *out_mantissa = mantissa; - *out_exponent = exponent; -} -#endif - -PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator *alloc) -{ - // try special number conversion - const char_t *special = convert_number_to_string_special(value); - if (special) - return xpath_string_const(special); - - // get mantissa + exponent form - char mantissa_buffer[32]; - - char *mantissa; - int exponent; - convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); - - // allocate a buffer of suitable length for the number - size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; - char_t *result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); - assert(result); - - // make the number! - char_t *s = result; - - // sign - if (value < 0) - *s++ = '-'; - - // integer part - if (exponent <= 0) - { - *s++ = '0'; - } - else - { - while (exponent > 0) - { - assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); - *s++ = *mantissa ? *mantissa++ : '0'; - exponent--; - } - } - - // fractional part - if (*mantissa) - { - // decimal point - *s++ = '.'; - - // extra zeroes from negative exponent - while (exponent < 0) - { - *s++ = '0'; - exponent++; - } - - // extra mantissa digits - while (*mantissa) - { - assert(static_cast(*mantissa - '0') <= 9); - *s++ = *mantissa++; - } - } - - // zero-terminate - assert(s < result + result_size); - *s = 0; - - return xpath_string(result, true); -} - -PUGI__FN bool check_string_to_number_format(const char_t *string) -{ - // parse leading whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) - ++string; - - // parse sign - if (*string == '-') - ++string; - - if (!*string) - return false; - - // if there is no integer part, there should be a decimal part with at least one digit - if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) - return false; - - // parse integer part - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) - ++string; - - // parse decimal part - if (*string == '.') - { - ++string; - - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) - ++string; - } - - // parse trailing whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) - ++string; - - return *string == 0; -} - -PUGI__FN double convert_string_to_number(const char_t *string) -{ - // check string format - if (!check_string_to_number_format(string)) - return gen_nan(); - - // parse string -#ifdef PUGIXML_WCHAR_MODE - return wcstod(string, 0); -#else - return atof(string); -#endif -} - -PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t *begin, const char_t *end, - double *out_result) -{ - size_t length = static_cast(end - begin); - char_t *scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) - return false; - } - - // copy string to zero-terminated buffer and perform conversion - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - *out_result = convert_string_to_number(scratch); - - // free dummy buffer - if (scratch != buffer) - xml_memory::deallocate(scratch); - - return true; -} - -PUGI__FN double round_nearest(double value) -{ - return floor(value + 0.5); -} - -PUGI__FN double round_nearest_nzero(double value) -{ - // same as round_nearest, but returns -0 for [-0.5, -0] - // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) - return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); -} - -PUGI__FN const char_t *qualified_name(const xpath_node &node) -{ - return node.attribute() ? node.attribute().name() : node.node().name(); -} - -PUGI__FN const char_t *local_name(const xpath_node &node) -{ - const char_t *name = qualified_name(node); - const char_t *p = find_char(name, ':'); - - return p ? p + 1 : name; -} - -struct namespace_uri_predicate -{ - const char_t *prefix; - size_t prefix_length; - - namespace_uri_predicate(const char_t *name) - { - const char_t *pos = find_char(name, ':'); - - prefix = pos ? name : 0; - prefix_length = pos ? static_cast(pos - name) : 0; - } - - bool operator()(const xml_attribute &a) const - { - const char_t *name = a.name(); - - if (!starts_with(name, PUGIXML_TEXT("xmlns"))) - return false; - - return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; - } -}; - -PUGI__FN const char_t *namespace_uri(const xml_node &node) -{ - namespace_uri_predicate pred = node.name(); - - xml_node p = node; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) - return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); -} - -PUGI__FN const char_t *namespace_uri(const xml_attribute &attr, const xml_node &parent) -{ - namespace_uri_predicate pred = attr.name(); - - // Default namespace does not apply to attributes - if (!pred.prefix) - return PUGIXML_TEXT(""); - - xml_node p = parent; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) - return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); -} - -PUGI__FN const char_t *namespace_uri(const xpath_node &node) -{ - return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); -} - -PUGI__FN void normalize_space(char_t *buffer) -{ - char_t *write = buffer; - - for (char_t *it = buffer; *it;) - { - char_t ch = *it++; - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - // replace whitespace sequence with single space - while (PUGI__IS_CHARTYPE(*it, ct_space)) - it++; - - // avoid leading spaces - if (write != buffer) - *write++ = ' '; - } - else - *write++ = ch; - } - - // remove trailing space - if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) - write--; - - // zero-terminate - *write = 0; -} - -PUGI__FN void translate(char_t *buffer, const char_t *from, const char_t *to) -{ - size_t to_length = strlength(to); - - char_t *write = buffer; - - while (*buffer) - { - PUGI__DMC_VOLATILE char_t ch = *buffer++; - - const char_t *pos = find_char(from, ch); - - if (!pos) - *write++ = ch; // do not process - else if (static_cast(pos - from) < to_length) - *write++ = to[pos - from]; // replace - } - - // zero-terminate - *write = 0; -} - -struct xpath_variable_boolean : xpath_variable -{ - xpath_variable_boolean() : value(false) {} - - bool value; - char_t name[1]; -}; - -struct xpath_variable_number : xpath_variable -{ - xpath_variable_number() : value(0) {} - - double value; - char_t name[1]; -}; - -struct xpath_variable_string : xpath_variable -{ - xpath_variable_string() : value(0) {} - - ~xpath_variable_string() - { - if (value) - xml_memory::deallocate(value); - } - - char_t *value; - char_t name[1]; -}; - -struct xpath_variable_node_set : xpath_variable -{ - xpath_node_set value; - char_t name[1]; -}; - -static const xpath_node_set dummy_node_set; - -PUGI__FN unsigned int hash_string(const char_t *str) -{ - // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) - unsigned int result = 0; - - while (*str) - { - result += static_cast(*str++); - result += result << 10; - result ^= result >> 6; - } - - result += result << 3; - result ^= result >> 11; - result += result << 15; - - return result; -} - -template -PUGI__FN T *new_xpath_variable(const char_t *name) -{ - size_t length = strlength(name); - if (length == 0) - return 0; // empty variable names are invalid - - // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters - void *memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); - if (!memory) - return 0; - - T *result = new (memory) T(); - - memcpy(result->name, name, (length + 1) * sizeof(char_t)); - - return result; -} - -PUGI__FN xpath_variable *new_xpath_variable(xpath_value_type type, const char_t *name) -{ - switch (type) - { - case xpath_type_node_set: - return new_xpath_variable(name); - - case xpath_type_number: - return new_xpath_variable(name); - - case xpath_type_string: - return new_xpath_variable(name); - - case xpath_type_boolean: - return new_xpath_variable(name); - - default: - return 0; - } -} - -template -PUGI__FN void delete_xpath_variable(T *var) -{ - var->~T(); - xml_memory::deallocate(var); -} - -PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable *var) -{ - switch (type) - { - case xpath_type_node_set: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_number: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_string: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_boolean: - delete_xpath_variable(static_cast(var)); - break; - - default: - assert(!"Invalid variable type"); - } -} - -PUGI__FN xpath_variable *get_variable_scratch(char_t (&buffer)[32], xpath_variable_set *set, const char_t *begin, - const char_t *end) -{ - size_t length = static_cast(end - begin); - char_t *scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) - return 0; - } - - // copy string to zero-terminated buffer and perform lookup - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - xpath_variable *result = set->get(scratch); - - // free dummy buffer - if (scratch != buffer) - xml_memory::deallocate(scratch); - - return result; -} -PUGI__NS_END - -// Internal node set class -PUGI__NS_BEGIN -PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node *begin, xpath_node *end, xpath_node_set::type_t type, bool rev) -{ - xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; - - if (type == xpath_node_set::type_unsorted) - { - sort(begin, end, document_order_comparator()); - - type = xpath_node_set::type_sorted; - } - - if (type != order) - reverse(begin, end); - - return order; -} - -PUGI__FN xpath_node xpath_first(const xpath_node *begin, const xpath_node *end, xpath_node_set::type_t type) -{ - if (begin == end) - return xpath_node(); - - switch (type) - { - case xpath_node_set::type_sorted: - return *begin; - - case xpath_node_set::type_sorted_reverse: - return *(end - 1); - - case xpath_node_set::type_unsorted: - return *min_element(begin, end, document_order_comparator()); - - default: - assert(!"Invalid node set type"); - return xpath_node(); - } -} - -class xpath_node_set_raw -{ - xpath_node_set::type_t _type; - - xpath_node *_begin; - xpath_node *_end; - xpath_node *_eos; - - public: - xpath_node_set_raw() : _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) {} - - xpath_node *begin() const { return _begin; } - - xpath_node *end() const { return _end; } - - bool empty() const { return _begin == _end; } - - size_t size() const { return static_cast(_end - _begin); } - - xpath_node first() const { return xpath_first(_begin, _end, _type); } - - void push_back(const xpath_node &node, xpath_allocator *alloc) - { - if (_end == _eos) - { - size_t capacity = static_cast(_eos - _begin); - - // get new capacity (1.5x rule) - size_t new_capacity = capacity + capacity / 2 + 1; - - // reallocate the old array or allocate a new one - xpath_node *data = static_cast( - alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + capacity; - _eos = data + new_capacity; - } - - *_end++ = node; - } - - void append(const xpath_node *begin_, const xpath_node *end_, xpath_allocator *alloc) - { - size_t size_ = static_cast(_end - _begin); - size_t capacity = static_cast(_eos - _begin); - size_t count = static_cast(end_ - begin_); - - if (size_ + count > capacity) - { - // reallocate the old array or allocate a new one - xpath_node *data = static_cast( - alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + size_; - _eos = data + size_ + count; - } - - memcpy(_end, begin_, count * sizeof(xpath_node)); - _end += count; - } - - void sort_do() { _type = xpath_sort(_begin, _end, _type, false); } - - void truncate(xpath_node *pos) - { - assert(_begin <= pos && pos <= _end); - - _end = pos; - } - - void remove_duplicates() - { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); - - _end = unique(_begin, _end); - } - - xpath_node_set::type_t type() const { return _type; } - - void set_type(xpath_node_set::type_t value) { _type = value; } -}; -PUGI__NS_END - -PUGI__NS_BEGIN -struct xpath_context -{ - xpath_node n; - size_t position, size; - - xpath_context(const xpath_node &n_, size_t position_, size_t size_) : n(n_), position(position_), size(size_) {} -}; - -enum lexeme_t -{ - lex_none = 0, - lex_equal, - lex_not_equal, - lex_less, - lex_greater, - lex_less_or_equal, - lex_greater_or_equal, - lex_plus, - lex_minus, - lex_multiply, - lex_union, - lex_var_ref, - lex_open_brace, - lex_close_brace, - lex_quoted_string, - lex_number, - lex_slash, - lex_double_slash, - lex_open_square_brace, - lex_close_square_brace, - lex_string, - lex_comma, - lex_axis_attribute, - lex_dot, - lex_double_dot, - lex_double_colon, - lex_eof -}; - -struct xpath_lexer_string -{ - const char_t *begin; - const char_t *end; - - xpath_lexer_string() : begin(0), end(0) {} - - bool operator==(const char_t *other) const - { - size_t length = static_cast(end - begin); - - return strequalrange(other, begin, length); - } -}; - -class xpath_lexer -{ - const char_t *_cur; - const char_t *_cur_lexeme_pos; - xpath_lexer_string _cur_lexeme_contents; - - lexeme_t _cur_lexeme; - - public: - explicit xpath_lexer(const char_t *query) : _cur(query) { next(); } - - const char_t *state() const { return _cur; } - - void next() - { - const char_t *cur = _cur; - - while (PUGI__IS_CHARTYPE(*cur, ct_space)) - ++cur; - - // save lexeme position for error reporting - _cur_lexeme_pos = cur; - - switch (*cur) - { - case 0: - _cur_lexeme = lex_eof; - break; - - case '>': - if (*(cur + 1) == '=') - { - cur += 2; - _cur_lexeme = lex_greater_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_greater; - } - break; - - case '<': - if (*(cur + 1) == '=') - { - cur += 2; - _cur_lexeme = lex_less_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_less; - } - break; - - case '!': - if (*(cur + 1) == '=') - { - cur += 2; - _cur_lexeme = lex_not_equal; - } - else - { - _cur_lexeme = lex_none; - } - break; - - case '=': - cur += 1; - _cur_lexeme = lex_equal; - - break; - - case '+': - cur += 1; - _cur_lexeme = lex_plus; - - break; - - case '-': - cur += 1; - _cur_lexeme = lex_minus; - - break; - - case '*': - cur += 1; - _cur_lexeme = lex_multiply; - - break; - - case '|': - cur += 1; - _cur_lexeme = lex_union; - - break; - - case '$': - cur += 1; - - if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) - cur++; - - if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) - cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_var_ref; - } - else - { - _cur_lexeme = lex_none; - } - - break; - - case '(': - cur += 1; - _cur_lexeme = lex_open_brace; - - break; - - case ')': - cur += 1; - _cur_lexeme = lex_close_brace; - - break; - - case '[': - cur += 1; - _cur_lexeme = lex_open_square_brace; - - break; - - case ']': - cur += 1; - _cur_lexeme = lex_close_square_brace; - - break; - - case ',': - cur += 1; - _cur_lexeme = lex_comma; - - break; - - case '/': - if (*(cur + 1) == '/') - { - cur += 2; - _cur_lexeme = lex_double_slash; - } - else - { - cur += 1; - _cur_lexeme = lex_slash; - } - break; - - case '.': - if (*(cur + 1) == '.') - { - cur += 2; - _cur_lexeme = lex_double_dot; - } - else if (PUGI__IS_CHARTYPEX(*(cur + 1), ctx_digit)) - { - _cur_lexeme_contents.begin = cur; // . - - ++cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - cur++; - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else - { - cur += 1; - _cur_lexeme = lex_dot; - } - break; - - case '@': - cur += 1; - _cur_lexeme = lex_axis_attribute; - - break; - - case '"': - case '\'': - { - char_t terminator = *cur; - - ++cur; - - _cur_lexeme_contents.begin = cur; - while (*cur && *cur != terminator) - cur++; - _cur_lexeme_contents.end = cur; - - if (!*cur) - _cur_lexeme = lex_none; - else - { - cur += 1; - _cur_lexeme = lex_quoted_string; - } - - break; - } - - case ':': - if (*(cur + 1) == ':') - { - cur += 2; - _cur_lexeme = lex_double_colon; - } - else - { - _cur_lexeme = lex_none; - } - break; - - default: - if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - cur++; - - if (*cur == '.') - { - cur++; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) - cur++; - - if (cur[0] == ':') - { - if (cur[1] == '*') // namespace test ncname:* - { - cur += 2; // :* - } - else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) - cur++; - } - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_string; - } - else - { - _cur_lexeme = lex_none; - } - } - - _cur = cur; - } - - lexeme_t current() const { return _cur_lexeme; } - - const char_t *current_pos() const { return _cur_lexeme_pos; } - - const xpath_lexer_string &contents() const - { - assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || - _cur_lexeme == lex_quoted_string); - - return _cur_lexeme_contents; - } -}; - -enum ast_type_t -{ - ast_unknown, - ast_op_or, // left or right - ast_op_and, // left and right - ast_op_equal, // left = right - ast_op_not_equal, // left != right - ast_op_less, // left < right - ast_op_greater, // left > right - ast_op_less_or_equal, // left <= right - ast_op_greater_or_equal, // left >= right - ast_op_add, // left + right - ast_op_subtract, // left - right - ast_op_multiply, // left * right - ast_op_divide, // left / right - ast_op_mod, // left % right - ast_op_negate, // left - right - ast_op_union, // left | right - ast_predicate, // apply predicate to set; next points to next predicate - ast_filter, // select * from left where right - ast_filter_posinv, // select * from left where right; proximity position invariant - ast_string_constant, // string constant - ast_number_constant, // number constant - ast_variable, // variable - ast_func_last, // last() - ast_func_position, // position() - ast_func_count, // count(left) - ast_func_id, // id(left) - ast_func_local_name_0, // local-name() - ast_func_local_name_1, // local-name(left) - ast_func_namespace_uri_0, // namespace-uri() - ast_func_namespace_uri_1, // namespace-uri(left) - ast_func_name_0, // name() - ast_func_name_1, // name(left) - ast_func_string_0, // string() - ast_func_string_1, // string(left) - ast_func_concat, // concat(left, right, siblings) - ast_func_starts_with, // starts_with(left, right) - ast_func_contains, // contains(left, right) - ast_func_substring_before, // substring-before(left, right) - ast_func_substring_after, // substring-after(left, right) - ast_func_substring_2, // substring(left, right) - ast_func_substring_3, // substring(left, right, third) - ast_func_string_length_0, // string-length() - ast_func_string_length_1, // string-length(left) - ast_func_normalize_space_0, // normalize-space() - ast_func_normalize_space_1, // normalize-space(left) - ast_func_translate, // translate(left, right, third) - ast_func_boolean, // boolean(left) - ast_func_not, // not(left) - ast_func_true, // true() - ast_func_false, // false() - ast_func_lang, // lang(left) - ast_func_number_0, // number() - ast_func_number_1, // number(left) - ast_func_sum, // sum(left) - ast_func_floor, // floor(left) - ast_func_ceiling, // ceiling(left) - ast_func_round, // round(left) - ast_step, // process set left with step - ast_step_root // select root node -}; - -enum axis_t -{ - axis_ancestor, - axis_ancestor_or_self, - axis_attribute, - axis_child, - axis_descendant, - axis_descendant_or_self, - axis_following, - axis_following_sibling, - axis_namespace, - axis_parent, - axis_preceding, - axis_preceding_sibling, - axis_self -}; - -enum nodetest_t -{ - nodetest_none, - nodetest_name, - nodetest_type_node, - nodetest_type_comment, - nodetest_type_pi, - nodetest_type_text, - nodetest_pi, - nodetest_all, - nodetest_all_in_namespace -}; - -template -struct axis_to_type -{ - static const axis_t axis; -}; - -template -const axis_t axis_to_type::axis = N; - -class xpath_ast_node -{ - private: - // node type - char _type; - char _rettype; - - // for ast_step / ast_predicate - char _axis; - char _test; - - // tree node structure - xpath_ast_node *_left; - xpath_ast_node *_right; - xpath_ast_node *_next; - - union { - // value for ast_string_constant - const char_t *string; - // value for ast_number_constant - double number; - // variable for ast_variable - xpath_variable *variable; - // node test for ast_step (node name/namespace/node type/pi target) - const char_t *nodetest; - } _data; - - xpath_ast_node(const xpath_ast_node &); - xpath_ast_node &operator=(const xpath_ast_node &); - - template - static bool compare_eq(xpath_ast_node *lhs, xpath_ast_node *rhs, const xpath_context &c, const xpath_stack &stack, - const Comp &comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - { - if (lt == xpath_type_boolean || rt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number || rt == xpath_type_number) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_string || rt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string ls = lhs->eval_string(c, stack); - xpath_string rs = rhs->eval_string(c, stack); - - return comp(ls, rs); - } - } - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack); - - for (const xpath_node *li = ls.begin(); li != ls.end(); ++li) - for (const xpath_node *ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) - return true; - } - - return false; - } - else - { - if (lt == xpath_type_node_set) - { - swap(lhs, rhs); - swap(lt, rt); - } - - if (lt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack); - - for (const xpath_node *ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string l = lhs->eval_string(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack); - - for (const xpath_node *ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, string_value(*ri, stack.result))) - return true; - } - - return false; - } - } - - assert(!"Wrong types"); - return false; - } - - template - static bool compare_rel(xpath_ast_node *lhs, xpath_ast_node *rhs, const xpath_context &c, const xpath_stack &stack, - const Comp &comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack); - - for (const xpath_node *li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - double l = convert_string_to_number(string_value(*li, stack.result).c_str()); - - for (const xpath_node *ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture crii(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - } - - return false; - } - else if (lt != xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack); - - for (const xpath_node *ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_node_set && rt != xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack); - double r = rhs->eval_number(c, stack); - - for (const xpath_node *li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) - return true; - } - - return false; - } - else - { - assert(!"Wrong types"); - return false; - } - } - - void apply_predicate(xpath_node_set_raw &ns, size_t first, xpath_ast_node *expr, const xpath_stack &stack) - { - assert(ns.size() >= first); - - size_t i = 1; - size_t size = ns.size() - first; - - xpath_node *last = ns.begin() + first; - - // remove_if... or well, sort of - for (xpath_node *it = last; it != ns.end(); ++it, ++i) - { - xpath_context c(*it, i, size); - - if (expr->rettype() == xpath_type_number) - { - if (expr->eval_number(c, stack) == i) - *last++ = *it; - } - else if (expr->eval_boolean(c, stack)) - *last++ = *it; - } - - ns.truncate(last); - } - - void apply_predicates(xpath_node_set_raw &ns, size_t first, const xpath_stack &stack) - { - if (ns.size() == first) - return; - - for (xpath_ast_node *pred = _right; pred; pred = pred->_next) - { - apply_predicate(ns, first, pred->_left, stack); - } - } - - void step_push(xpath_node_set_raw &ns, const xml_attribute &a, const xml_node &parent, xpath_allocator *alloc) - { - if (!a) - return; - - const char_t *name = a.name(); - - // There are no attribute nodes corresponding to attributes that declare namespaces - // That is, "xmlns:..." or "xmlns" - if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) - return; - - switch (_test) - { - case nodetest_name: - if (strequal(name, _data.nodetest)) - ns.push_back(xpath_node(a, parent), alloc); - break; - - case nodetest_type_node: - case nodetest_all: - ns.push_back(xpath_node(a, parent), alloc); - break; - - case nodetest_all_in_namespace: - if (starts_with(name, _data.nodetest)) - ns.push_back(xpath_node(a, parent), alloc); - break; - - default:; - } - } - - void step_push(xpath_node_set_raw &ns, const xml_node &n, xpath_allocator *alloc) - { - if (!n) - return; - - switch (_test) - { - case nodetest_name: - if (n.type() == node_element && strequal(n.name(), _data.nodetest)) - ns.push_back(n, alloc); - break; - - case nodetest_type_node: - ns.push_back(n, alloc); - break; - - case nodetest_type_comment: - if (n.type() == node_comment) - ns.push_back(n, alloc); - break; - - case nodetest_type_text: - if (n.type() == node_pcdata || n.type() == node_cdata) - ns.push_back(n, alloc); - break; - - case nodetest_type_pi: - if (n.type() == node_pi) - ns.push_back(n, alloc); - break; - - case nodetest_pi: - if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) - ns.push_back(n, alloc); - break; - - case nodetest_all: - if (n.type() == node_element) - ns.push_back(n, alloc); - break; - - case nodetest_all_in_namespace: - if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) - ns.push_back(n, alloc); - break; - - default: - assert(!"Unknown axis"); - } - } - - template - void step_fill(xpath_node_set_raw &ns, const xml_node &n, xpath_allocator *alloc, T) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_attribute: - { - for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) - step_push(ns, a, n, alloc); - - break; - } - - case axis_child: - { - for (xml_node c = n.first_child(); c; c = c.next_sibling()) - step_push(ns, c, alloc); - - break; - } - - case axis_descendant: - case axis_descendant_or_self: - { - if (axis == axis_descendant_or_self) - step_push(ns, n, alloc); - - xml_node cur = n.first_child(); - - while (cur && cur != n) - { - step_push(ns, cur, alloc); - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur != n) - cur = cur.parent(); - - if (cur != n) - cur = cur.next_sibling(); - } - } - - break; - } - - case axis_following_sibling: - { - for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) - step_push(ns, c, alloc); - - break; - } - - case axis_preceding_sibling: - { - for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) - step_push(ns, c, alloc); - - break; - } - - case axis_following: - { - xml_node cur = n; - - // exit from this node so that we don't include descendants - while (cur && !cur.next_sibling()) - cur = cur.parent(); - cur = cur.next_sibling(); - - for (;;) - { - step_push(ns, cur, alloc); - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (cur && !cur.next_sibling()) - cur = cur.parent(); - cur = cur.next_sibling(); - - if (!cur) - break; - } - } - - break; - } - - case axis_preceding: - { - xml_node cur = n; - - while (cur && !cur.previous_sibling()) - cur = cur.parent(); - cur = cur.previous_sibling(); - - for (;;) - { - if (cur.last_child()) - cur = cur.last_child(); - else - { - // leaf node, can't be ancestor - step_push(ns, cur, alloc); - - if (cur.previous_sibling()) - cur = cur.previous_sibling(); - else - { - do - { - cur = cur.parent(); - if (!cur) - break; - - if (!node_is_ancestor(cur, n)) - step_push(ns, cur, alloc); - } while (!cur.previous_sibling()); - - cur = cur.previous_sibling(); - - if (!cur) - break; - } - } - } - - break; - } - - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self) - step_push(ns, n, alloc); - - xml_node cur = n.parent(); - - while (cur) - { - step_push(ns, cur, alloc); - - cur = cur.parent(); - } - - break; - } - - case axis_self: - { - step_push(ns, n, alloc); - - break; - } - - case axis_parent: - { - if (n.parent()) - step_push(ns, n.parent(), alloc); - - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template - void step_fill(xpath_node_set_raw &ns, const xml_attribute &a, const xml_node &p, xpath_allocator *alloc, T v) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self && - _test == nodetest_type_node) // reject attributes based on principal node type test - step_push(ns, a, p, alloc); - - xml_node cur = p; - - while (cur) - { - step_push(ns, cur, alloc); - - cur = cur.parent(); - } - - break; - } - - case axis_descendant_or_self: - case axis_self: - { - if (_test == nodetest_type_node) // reject attributes based on principal node type test - step_push(ns, a, p, alloc); - - break; - } - - case axis_following: - { - xml_node cur = p; - - for (;;) - { - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (cur && !cur.next_sibling()) - cur = cur.parent(); - cur = cur.next_sibling(); - - if (!cur) - break; - } - - step_push(ns, cur, alloc); - } - - break; - } - - case axis_parent: - { - step_push(ns, p, alloc); - - break; - } - - case axis_preceding: - { - // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's - // ancestors), so we can reuse node preceding - step_fill(ns, p, alloc, v); - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template - xpath_node_set_raw step_do(const xpath_context &c, const xpath_stack &stack, T v) - { - const axis_t axis = T::axis; - bool attributes = - (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || - axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); - - xpath_node_set_raw ns; - ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || - axis == axis_preceding_sibling) - ? xpath_node_set::type_sorted_reverse - : xpath_node_set::type_sorted); - - if (_left) - { - xpath_node_set_raw s = _left->eval_node_set(c, stack); - - // self axis preserves the original order - if (axis == axis_self) - ns.set_type(s.type()); - - for (const xpath_node *it = s.begin(); it != s.end(); ++it) - { - size_t size = ns.size(); - - // in general, all axes generate elements in a particular order, but there is no order guarantee if axis - // is applied to two nodes - if (axis != axis_self && size != 0) - ns.set_type(xpath_node_set::type_unsorted); - - if (it->node()) - step_fill(ns, it->node(), stack.result, v); - else if (attributes) - step_fill(ns, it->attribute(), it->parent(), stack.result, v); - - apply_predicates(ns, size, stack); - } - } - else - { - if (c.n.node()) - step_fill(ns, c.n.node(), stack.result, v); - else if (attributes) - step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v); - - apply_predicates(ns, 0, stack); - } - - // child, attribute and self axes always generate unique set of nodes - // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the - // same node twice - if (axis != axis_child && axis != axis_attribute && axis != axis_self && - ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); - - return ns; - } - - public: - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t *value) - : _type(static_cast(type)) - , _rettype(static_cast(rettype_)) - , _axis(0) - , _test(0) - , _left(0) - , _right(0) - , _next(0) - { - assert(type == ast_string_constant); - _data.string = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value) - : _type(static_cast(type)) - , _rettype(static_cast(rettype_)) - , _axis(0) - , _test(0) - , _left(0) - , _right(0) - , _next(0) - { - assert(type == ast_number_constant); - _data.number = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable *value) - : _type(static_cast(type)) - , _rettype(static_cast(rettype_)) - , _axis(0) - , _test(0) - , _left(0) - , _right(0) - , _next(0) - { - assert(type == ast_variable); - _data.variable = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node *left = 0, xpath_ast_node *right = 0) - : _type(static_cast(type)) - , _rettype(static_cast(rettype_)) - , _axis(0) - , _test(0) - , _left(left) - , _right(right) - , _next(0) - { - } - - xpath_ast_node(ast_type_t type, xpath_ast_node *left, axis_t axis, nodetest_t test, const char_t *contents) - : _type(static_cast(type)) - , _rettype(xpath_type_node_set) - , _axis(static_cast(axis)) - , _test(static_cast(test)) - , _left(left) - , _right(0) - , _next(0) - { - _data.nodetest = contents; - } - - void set_next(xpath_ast_node *value) { _next = value; } - - void set_right(xpath_ast_node *value) { _right = value; } - - bool eval_boolean(const xpath_context &c, const xpath_stack &stack) - { - switch (_type) - { - case ast_op_or: - return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); - - case ast_op_and: - return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); - - case ast_op_equal: - return compare_eq(_left, _right, c, stack, equal_to()); - - case ast_op_not_equal: - return compare_eq(_left, _right, c, stack, not_equal_to()); - - case ast_op_less: - return compare_rel(_left, _right, c, stack, less()); - - case ast_op_greater: - return compare_rel(_right, _left, c, stack, less()); - - case ast_op_less_or_equal: - return compare_rel(_left, _right, c, stack, less_equal()); - - case ast_op_greater_or_equal: - return compare_rel(_right, _left, c, stack, less_equal()); - - case ast_func_starts_with: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return starts_with(lr.c_str(), rr.c_str()); - } - - case ast_func_contains: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return find_substring(lr.c_str(), rr.c_str()) != 0; - } - - case ast_func_boolean: - return _left->eval_boolean(c, stack); - - case ast_func_not: - return !_left->eval_boolean(c, stack); - - case ast_func_true: - return true; - - case ast_func_false: - return false; - - case ast_func_lang: - { - if (c.n.attribute()) - return false; - - xpath_allocator_capture cr(stack.result); - - xpath_string lang = _left->eval_string(c, stack); - - for (xml_node n = c.n.node(); n; n = n.parent()) - { - xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); - - if (a) - { - const char_t *value = a.value(); - - // strnicmp / strncasecmp is not portable - for (const char_t *lit = lang.c_str(); *lit; ++lit) - { - if (tolower_ascii(*lit) != tolower_ascii(*value)) - return false; - ++value; - } - - return *value == 0 || *value == '-'; - } - } - - return false; - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_boolean) - return _data.variable->get_boolean(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack).empty(); - } - - default: - assert(!"Wrong expression for return type boolean"); - return false; - } - } - } - } - - double eval_number(const xpath_context &c, const xpath_stack &stack) - { - switch (_type) - { - case ast_op_add: - return _left->eval_number(c, stack) + _right->eval_number(c, stack); - - case ast_op_subtract: - return _left->eval_number(c, stack) - _right->eval_number(c, stack); - - case ast_op_multiply: - return _left->eval_number(c, stack) * _right->eval_number(c, stack); - - case ast_op_divide: - return _left->eval_number(c, stack) / _right->eval_number(c, stack); - - case ast_op_mod: - return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); - - case ast_op_negate: - return -_left->eval_number(c, stack); - - case ast_number_constant: - return _data.number; - - case ast_func_last: - return static_cast(c.size); - - case ast_func_position: - return static_cast(c.position); - - case ast_func_count: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_node_set(c, stack).size()); - } - - case ast_func_string_length_0: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(string_value(c.n, stack.result).length()); - } - - case ast_func_string_length_1: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_string(c, stack).length()); - } - - case ast_func_number_0: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(string_value(c.n, stack.result).c_str()); - } - - case ast_func_number_1: - return _left->eval_number(c, stack); - - case ast_func_sum: - { - xpath_allocator_capture cr(stack.result); - - double r = 0; - - xpath_node_set_raw ns = _left->eval_node_set(c, stack); - - for (const xpath_node *it = ns.begin(); it != ns.end(); ++it) - { - xpath_allocator_capture cri(stack.result); - - r += convert_string_to_number(string_value(*it, stack.result).c_str()); - } - - return r; - } - - case ast_func_floor: - { - double r = _left->eval_number(c, stack); - - return r == r ? floor(r) : r; - } - - case ast_func_ceiling: - { - double r = _left->eval_number(c, stack); - - return r == r ? ceil(r) : r; - } - - case ast_func_round: - return round_nearest_nzero(_left->eval_number(c, stack)); - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_number) - return _data.variable->get_number(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(!"Wrong expression for return type number"); - return 0; - } - } - } - } - - xpath_string eval_string_concat(const xpath_context &c, const xpath_stack &stack) - { - assert(_type == ast_func_concat); - - xpath_allocator_capture ct(stack.temp); - - // count the string number - size_t count = 1; - for (xpath_ast_node *nc = _right; nc; nc = nc->_next) - count++; - - // gather all strings - xpath_string static_buffer[4]; - xpath_string *buffer = static_buffer; - - // allocate on-heap for large concats - if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) - { - buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); - assert(buffer); - } - - // evaluate all strings to temporary stack - xpath_stack swapped_stack = { stack.temp, stack.result }; - - buffer[0] = _left->eval_string(c, swapped_stack); - - size_t pos = 1; - for (xpath_ast_node *n = _right; n; n = n->_next, ++pos) - buffer[pos] = n->eval_string(c, swapped_stack); - assert(pos == count); - - // get total length - size_t length = 0; - for (size_t i = 0; i < count; ++i) - length += buffer[i].length(); - - // create final string - char_t *result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); - assert(result); - - char_t *ri = result; - - for (size_t j = 0; j < count; ++j) - for (const char_t *bi = buffer[j].c_str(); *bi; ++bi) - *ri++ = *bi; - - *ri = 0; - - return xpath_string(result, true); - } - - xpath_string eval_string(const xpath_context &c, const xpath_stack &stack) - { - switch (_type) - { - case ast_string_constant: - return xpath_string_const(_data.string); - - case ast_func_local_name_0: - { - xpath_node na = c.n; - - return xpath_string_const(local_name(na)); - } - - case ast_func_local_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack); - xpath_node na = ns.first(); - - return xpath_string_const(local_name(na)); - } - - case ast_func_name_0: - { - xpath_node na = c.n; - - return xpath_string_const(qualified_name(na)); - } - - case ast_func_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack); - xpath_node na = ns.first(); - - return xpath_string_const(qualified_name(na)); - } - - case ast_func_namespace_uri_0: - { - xpath_node na = c.n; - - return xpath_string_const(namespace_uri(na)); - } - - case ast_func_namespace_uri_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack); - xpath_node na = ns.first(); - - return xpath_string_const(namespace_uri(na)); - } - - case ast_func_string_0: - return string_value(c.n, stack.result); - - case ast_func_string_1: - return _left->eval_string(c, stack); - - case ast_func_concat: - return eval_string_concat(c, stack); - - case ast_func_substring_before: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t *pos = find_substring(s.c_str(), p.c_str()); - - return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string(); - } - - case ast_func_substring_after: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t *pos = find_substring(s.c_str(), p.c_str()); - if (!pos) - return xpath_string(); - - const char_t *result = pos + p.length(); - - return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result); - } - - case ast_func_substring_2: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - - if (is_nan(first)) - return xpath_string(); // NaN - else if (first >= s_length + 1) - return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - assert(1 <= pos && pos <= s_length + 1); - - const char_t *rbegin = s.c_str() + (pos - 1); - - return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin); - } - - case ast_func_substring_3: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - double last = first + round_nearest(_right->_next->eval_number(c, stack)); - - if (is_nan(first) || is_nan(last)) - return xpath_string(); - else if (first >= s_length + 1) - return xpath_string(); - else if (first >= last) - return xpath_string(); - else if (last < 1) - return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); - - assert(1 <= pos && pos <= end && end <= s_length + 1); - const char_t *rbegin = s.c_str() + (pos - 1); - const char_t *rend = s.c_str() + (end - 1); - - return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) - : xpath_string(rbegin, rend, stack.result); - } - - case ast_func_normalize_space_0: - { - xpath_string s = string_value(c.n, stack.result); - - normalize_space(s.data(stack.result)); - - return s; - } - - case ast_func_normalize_space_1: - { - xpath_string s = _left->eval_string(c, stack); - - normalize_space(s.data(stack.result)); - - return s; - } - - case ast_func_translate: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_string s = _left->eval_string(c, stack); - xpath_string from = _right->eval_string(c, swapped_stack); - xpath_string to = _right->_next->eval_string(c, swapped_stack); - - translate(s.data(stack.result), from.c_str(), to.c_str()); - - return s; - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_string) - return xpath_string_const(_data.variable->get_string()); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(!"Wrong expression for return type string"); - return xpath_string(); - } - } - } - } - - xpath_node_set_raw eval_node_set(const xpath_context &c, const xpath_stack &stack) - { - switch (_type) - { - case ast_op_union: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = { stack.temp, stack.result }; - - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack); - xpath_node_set_raw rs = _right->eval_node_set(c, stack); - - // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); - - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); - - return rs; - } - - case ast_filter: - case ast_filter_posinv: - { - xpath_node_set_raw set = _left->eval_node_set(c, stack); - - // either expression is a number or it contains position() call; sort by document order - if (_type == ast_filter) - set.sort_do(); - - apply_predicate(set, 0, _right, stack); - - return set; - } - - case ast_func_id: - return xpath_node_set_raw(); - - case ast_step: - { - switch (_axis) - { - case axis_ancestor: - return step_do(c, stack, axis_to_type()); - - case axis_ancestor_or_self: - return step_do(c, stack, axis_to_type()); - - case axis_attribute: - return step_do(c, stack, axis_to_type()); - - case axis_child: - return step_do(c, stack, axis_to_type()); - - case axis_descendant: - return step_do(c, stack, axis_to_type()); - - case axis_descendant_or_self: - return step_do(c, stack, axis_to_type()); - - case axis_following: - return step_do(c, stack, axis_to_type()); - - case axis_following_sibling: - return step_do(c, stack, axis_to_type()); - - case axis_namespace: - // namespaced axis is not supported - return xpath_node_set_raw(); - - case axis_parent: - return step_do(c, stack, axis_to_type()); - - case axis_preceding: - return step_do(c, stack, axis_to_type()); - - case axis_preceding_sibling: - return step_do(c, stack, axis_to_type()); - - case axis_self: - return step_do(c, stack, axis_to_type()); - - default: - assert(!"Unknown axis"); - return xpath_node_set_raw(); - } - } - - case ast_step_root: - { - assert(!_right); // root step can't have any predicates - - xpath_node_set_raw ns; - - ns.set_type(xpath_node_set::type_sorted); - - if (c.n.node()) - ns.push_back(c.n.node().root(), stack.result); - else if (c.n.attribute()) - ns.push_back(c.n.parent().root(), stack.result); - - return ns; - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_node_set) - { - const xpath_node_set &s = _data.variable->get_node_set(); - - xpath_node_set_raw ns; - - ns.set_type(s.type()); - ns.append(s.begin(), s.end(), stack.result); - - return ns; - } - - // fallthrough to type conversion - } - - default: - assert(!"Wrong expression for return type node set"); - return xpath_node_set_raw(); - } - } - - bool is_posinv() - { - switch (_type) - { - case ast_func_position: - return false; - - case ast_string_constant: - case ast_number_constant: - case ast_variable: - return true; - - case ast_step: - case ast_step_root: - return true; - - case ast_predicate: - case ast_filter: - case ast_filter_posinv: - return true; - - default: - if (_left && !_left->is_posinv()) - return false; - - for (xpath_ast_node *n = _right; n; n = n->_next) - if (!n->is_posinv()) - return false; - - return true; - } - } - - xpath_value_type rettype() const { return static_cast(_rettype); } -}; - -struct xpath_parser -{ - xpath_allocator *_alloc; - xpath_lexer _lexer; - - const char_t *_query; - xpath_variable_set *_variables; - - xpath_parse_result *_result; - - char_t _scratch[32]; - -#ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf _error_handler; -#endif - - void throw_error(const char *message) - { - _result->error = message; - _result->offset = _lexer.current_pos() - _query; - -#ifdef PUGIXML_NO_EXCEPTIONS - longjmp(_error_handler, 1); -#else - throw xpath_exception(*_result); -#endif - } - - void throw_error_oom() - { -#ifdef PUGIXML_NO_EXCEPTIONS - throw_error("Out of memory"); -#else - throw std::bad_alloc(); -#endif - } - - void *alloc_node() - { - void *result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); - - if (!result) - throw_error_oom(); - - return result; - } - - const char_t *alloc_string(const xpath_lexer_string &value) - { - if (value.begin) - { - size_t length = static_cast(value.end - value.begin); - - char_t *c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); - if (!c) - throw_error_oom(); - assert(c); // workaround for clang static analysis - - memcpy(c, value.begin, length * sizeof(char_t)); - c[length] = 0; - - return c; - } - else - return 0; - } - - xpath_ast_node *parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node *args[2]) - { - assert(argc <= 1); - - if (argc == 1 && args[0]->rettype() != xpath_type_node_set) - throw_error("Function has to be applied to node set"); - - return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); - } - - xpath_ast_node *parse_function(const xpath_lexer_string &name, size_t argc, xpath_ast_node *args[2]) - { - switch (name.begin[0]) - { - case 'b': - if (name == PUGIXML_TEXT("boolean") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); - - break; - - case 'c': - if (name == PUGIXML_TEXT("count") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) - throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); - } - else if (name == PUGIXML_TEXT("contains") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("concat") && argc >= 2) - return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("ceiling") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); - - break; - - case 'f': - if (name == PUGIXML_TEXT("false") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); - else if (name == PUGIXML_TEXT("floor") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); - - break; - - case 'i': - if (name == PUGIXML_TEXT("id") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); - - break; - - case 'l': - if (name == PUGIXML_TEXT("last") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); - else if (name == PUGIXML_TEXT("lang") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("local-name") && argc <= 1) - return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); - - break; - - case 'n': - if (name == PUGIXML_TEXT("name") && argc <= 1) - return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); - else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) - return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); - else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) - return new (alloc_node()) - xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, - xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("not") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("number") && argc <= 1) - return new (alloc_node()) - xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); - - break; - - case 'p': - if (name == PUGIXML_TEXT("position") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); - - break; - - case 'r': - if (name == PUGIXML_TEXT("round") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); - - break; - - case 's': - if (name == PUGIXML_TEXT("string") && argc <= 1) - return new (alloc_node()) - xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); - else if (name == PUGIXML_TEXT("string-length") && argc <= 1) - return new (alloc_node()) xpath_ast_node( - argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); - else if (name == PUGIXML_TEXT("starts-with") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-before") && argc == 2) - return new (alloc_node()) - xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-after") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) - return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, - xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("sum") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) - throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); - } - - break; - - case 't': - if (name == PUGIXML_TEXT("translate") && argc == 3) - return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("true") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); - - break; - - default: - break; - } - - throw_error("Unrecognized function or wrong parameter count"); - - return 0; - } - - axis_t parse_axis_name(const xpath_lexer_string &name, bool &specified) - { - specified = true; - - switch (name.begin[0]) - { - case 'a': - if (name == PUGIXML_TEXT("ancestor")) - return axis_ancestor; - else if (name == PUGIXML_TEXT("ancestor-or-self")) - return axis_ancestor_or_self; - else if (name == PUGIXML_TEXT("attribute")) - return axis_attribute; - - break; - - case 'c': - if (name == PUGIXML_TEXT("child")) - return axis_child; - - break; - - case 'd': - if (name == PUGIXML_TEXT("descendant")) - return axis_descendant; - else if (name == PUGIXML_TEXT("descendant-or-self")) - return axis_descendant_or_self; - - break; - - case 'f': - if (name == PUGIXML_TEXT("following")) - return axis_following; - else if (name == PUGIXML_TEXT("following-sibling")) - return axis_following_sibling; - - break; - - case 'n': - if (name == PUGIXML_TEXT("namespace")) - return axis_namespace; - - break; - - case 'p': - if (name == PUGIXML_TEXT("parent")) - return axis_parent; - else if (name == PUGIXML_TEXT("preceding")) - return axis_preceding; - else if (name == PUGIXML_TEXT("preceding-sibling")) - return axis_preceding_sibling; - - break; - - case 's': - if (name == PUGIXML_TEXT("self")) - return axis_self; - - break; - - default: - break; - } - - specified = false; - return axis_child; - } - - nodetest_t parse_node_test_type(const xpath_lexer_string &name) - { - switch (name.begin[0]) - { - case 'c': - if (name == PUGIXML_TEXT("comment")) - return nodetest_type_comment; - - break; - - case 'n': - if (name == PUGIXML_TEXT("node")) - return nodetest_type_node; - - break; - - case 'p': - if (name == PUGIXML_TEXT("processing-instruction")) - return nodetest_type_pi; - - break; - - case 't': - if (name == PUGIXML_TEXT("text")) - return nodetest_type_text; - - break; - - default: - break; - } - - return nodetest_none; - } - - // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall - xpath_ast_node *parse_primary_expression() - { - switch (_lexer.current()) - { - case lex_var_ref: - { - xpath_lexer_string name = _lexer.contents(); - - if (!_variables) - throw_error("Unknown variable: variable set is not provided"); - - xpath_variable *var = get_variable_scratch(_scratch, _variables, name.begin, name.end); - - if (!var) - throw_error("Unknown variable: variable set does not contain the given name"); - - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); - } - - case lex_open_brace: - { - _lexer.next(); - - xpath_ast_node *n = parse_expression(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched braces"); - - _lexer.next(); - - return n; - } - - case lex_quoted_string: - { - const char_t *value = alloc_string(_lexer.contents()); - - xpath_ast_node *n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); - _lexer.next(); - - return n; - } - - case lex_number: - { - double value = 0; - - if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) - throw_error_oom(); - - xpath_ast_node *n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); - _lexer.next(); - - return n; - } - - case lex_string: - { - xpath_ast_node *args[2] = { 0 }; - size_t argc = 0; - - xpath_lexer_string function = _lexer.contents(); - _lexer.next(); - - xpath_ast_node *last_arg = 0; - - if (_lexer.current() != lex_open_brace) - throw_error("Unrecognized function call"); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - args[argc++] = parse_expression(); - - while (_lexer.current() != lex_close_brace) - { - if (_lexer.current() != lex_comma) - throw_error("No comma between function arguments"); - _lexer.next(); - - xpath_ast_node *n = parse_expression(); - - if (argc < 2) - args[argc] = n; - else - last_arg->set_next(n); - - argc++; - last_arg = n; - } - - _lexer.next(); - - return parse_function(function, argc, args); - } - - default: - throw_error("Unrecognizable primary expression"); - - return 0; - } - } - - // FilterExpr ::= PrimaryExpr | FilterExpr Predicate - // Predicate ::= '[' PredicateExpr ']' - // PredicateExpr ::= Expr - xpath_ast_node *parse_filter_expression() - { - xpath_ast_node *n = parse_primary_expression(); - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node *expr = parse_expression(); - - if (n->rettype() != xpath_type_node_set) - throw_error("Predicate has to be applied to node set"); - - bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv(); - - n = new (alloc_node()) - xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - - _lexer.next(); - } - - return n; - } - - // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep - // AxisSpecifier ::= AxisName '::' | '@'? - // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' - // NameTest ::= '*' | NCName ':' '*' | QName - // AbbreviatedStep ::= '.' | '..' - xpath_ast_node *parse_step(xpath_ast_node *set) - { - if (set && set->rettype() != xpath_type_node_set) - throw_error("Step has to be applied to node set"); - - bool axis_specified = false; - axis_t axis = axis_child; // implied child axis - - if (_lexer.current() == lex_axis_attribute) - { - axis = axis_attribute; - axis_specified = true; - - _lexer.next(); - } - else if (_lexer.current() == lex_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); - } - else if (_lexer.current() == lex_double_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); - } - - nodetest_t nt_type = nodetest_none; - xpath_lexer_string nt_name; - - if (_lexer.current() == lex_string) - { - // node name test - nt_name = _lexer.contents(); - _lexer.next(); - - // was it an axis name? - if (_lexer.current() == lex_double_colon) - { - // parse axis name - if (axis_specified) - throw_error("Two axis specifiers in one step"); - - axis = parse_axis_name(nt_name, axis_specified); - - if (!axis_specified) - throw_error("Unknown axis"); - - // read actual node test - _lexer.next(); - - if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - nt_name = xpath_lexer_string(); - _lexer.next(); - } - else if (_lexer.current() == lex_string) - { - nt_name = _lexer.contents(); - _lexer.next(); - } - else - throw_error("Unrecognized node test"); - } - - if (nt_type == nodetest_none) - { - // node type test or processing-instruction - if (_lexer.current() == lex_open_brace) - { - _lexer.next(); - - if (_lexer.current() == lex_close_brace) - { - _lexer.next(); - - nt_type = parse_node_test_type(nt_name); - - if (nt_type == nodetest_none) - throw_error("Unrecognized node type"); - - nt_name = xpath_lexer_string(); - } - else if (nt_name == PUGIXML_TEXT("processing-instruction")) - { - if (_lexer.current() != lex_quoted_string) - throw_error("Only literals are allowed as arguments to processing-instruction()"); - - nt_type = nodetest_pi; - nt_name = _lexer.contents(); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched brace near processing-instruction()"); - _lexer.next(); - } - else - throw_error("Unmatched brace near node type test"); - } - // QName or NCName:* - else - { - if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* - { - nt_name.end--; // erase * - - nt_type = nodetest_all_in_namespace; - } - else - nt_type = nodetest_name; - } - } - } - else if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - _lexer.next(); - } - else - throw_error("Unrecognized node test"); - - xpath_ast_node *n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); - - xpath_ast_node *last = 0; - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node *expr = parse_expression(); - - xpath_ast_node *pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - _lexer.next(); - - if (last) - last->set_next(pred); - else - n->set_right(pred); - - last = pred; - } - - return n; - } - - // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step - xpath_ast_node *parse_relative_location_path(xpath_ast_node *set) - { - xpath_ast_node *n = parse_step(set); - - while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - n = parse_step(n); - } - - return n; - } - - // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath - // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath - xpath_ast_node *parse_location_path() - { - if (_lexer.current() == lex_slash) - { - _lexer.next(); - - xpath_ast_node *n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - - // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any - // other lexeme means standalone root path - lexeme_t l = _lexer.current(); - - if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) - return parse_relative_location_path(n); - else - return n; - } - else if (_lexer.current() == lex_double_slash) - { - _lexer.next(); - - xpath_ast_node *n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - return parse_relative_location_path(n); - } - - // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being - // inlined' in gcc 4.0.1 - return parse_relative_location_path(0); - } - - // PathExpr ::= LocationPath - // | FilterExpr - // | FilterExpr '/' RelativeLocationPath - // | FilterExpr '//' RelativeLocationPath - // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr - // UnaryExpr ::= UnionExpr | '-' UnaryExpr - xpath_ast_node *parse_path_or_unary_expression() - { - // Clarification. - // PathExpr begins with either LocationPath or FilterExpr. - // FilterExpr begins with PrimaryExpr - // PrimaryExpr begins with '$' in case of it being a variable reference, - // '(' in case of it being an expression, string literal, number constant or - // function call. - - if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || - _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) - { - if (_lexer.current() == lex_string) - { - // This is either a function call, or not - if not, we shall proceed with location path - const char_t *state = _lexer.state(); - - while (PUGI__IS_CHARTYPE(*state, ct_space)) - ++state; - - if (*state != '(') - return parse_location_path(); - - // This looks like a function call; however this still can be a node-test. Check it. - if (parse_node_test_type(_lexer.contents()) != nodetest_none) - return parse_location_path(); - } - - xpath_ast_node *n = parse_filter_expression(); - - if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - { - if (n->rettype() != xpath_type_node_set) - throw_error("Step has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - } - - // select from location path - return parse_relative_location_path(n); - } - - return n; - } - else if (_lexer.current() == lex_minus) - { - _lexer.next(); - - // precedence 7+ - only parses union expressions - xpath_ast_node *expr = parse_expression_rec(parse_path_or_unary_expression(), 7); - - return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); - } - else - return parse_location_path(); - } - - struct binary_op_t - { - ast_type_t asttype; - xpath_value_type rettype; - int precedence; - - binary_op_t() : asttype(ast_unknown), rettype(xpath_type_none), precedence(0) {} - - binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_) - : asttype(asttype_), rettype(rettype_), precedence(precedence_) - { - } - - static binary_op_t parse(xpath_lexer &lexer) - { - switch (lexer.current()) - { - case lex_string: - if (lexer.contents() == PUGIXML_TEXT("or")) - return binary_op_t(ast_op_or, xpath_type_boolean, 1); - else if (lexer.contents() == PUGIXML_TEXT("and")) - return binary_op_t(ast_op_and, xpath_type_boolean, 2); - else if (lexer.contents() == PUGIXML_TEXT("div")) - return binary_op_t(ast_op_divide, xpath_type_number, 6); - else if (lexer.contents() == PUGIXML_TEXT("mod")) - return binary_op_t(ast_op_mod, xpath_type_number, 6); - else - return binary_op_t(); - - case lex_equal: - return binary_op_t(ast_op_equal, xpath_type_boolean, 3); - - case lex_not_equal: - return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); - - case lex_less: - return binary_op_t(ast_op_less, xpath_type_boolean, 4); - - case lex_greater: - return binary_op_t(ast_op_greater, xpath_type_boolean, 4); - - case lex_less_or_equal: - return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); - - case lex_greater_or_equal: - return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); - - case lex_plus: - return binary_op_t(ast_op_add, xpath_type_number, 5); - - case lex_minus: - return binary_op_t(ast_op_subtract, xpath_type_number, 5); - - case lex_multiply: - return binary_op_t(ast_op_multiply, xpath_type_number, 6); - - case lex_union: - return binary_op_t(ast_op_union, xpath_type_node_set, 7); - - default: - return binary_op_t(); - } - } - }; - - xpath_ast_node *parse_expression_rec(xpath_ast_node *lhs, int limit) - { - binary_op_t op = binary_op_t::parse(_lexer); - - while (op.asttype != ast_unknown && op.precedence >= limit) - { - _lexer.next(); - - xpath_ast_node *rhs = parse_path_or_unary_expression(); - - binary_op_t nextop = binary_op_t::parse(_lexer); - - while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) - { - rhs = parse_expression_rec(rhs, nextop.precedence); - - nextop = binary_op_t::parse(_lexer); - } - - if (op.asttype == ast_op_union && - (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) - throw_error("Union operator has to be applied to node sets"); - - lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); - - op = binary_op_t::parse(_lexer); - } - - return lhs; - } - - // Expr ::= OrExpr - // OrExpr ::= AndExpr | OrExpr 'or' AndExpr - // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr - // EqualityExpr ::= RelationalExpr - // | EqualityExpr '=' RelationalExpr - // | EqualityExpr '!=' RelationalExpr - // RelationalExpr ::= AdditiveExpr - // | RelationalExpr '<' AdditiveExpr - // | RelationalExpr '>' AdditiveExpr - // | RelationalExpr '<=' AdditiveExpr - // | RelationalExpr '>=' AdditiveExpr - // AdditiveExpr ::= MultiplicativeExpr - // | AdditiveExpr '+' MultiplicativeExpr - // | AdditiveExpr '-' MultiplicativeExpr - // MultiplicativeExpr ::= UnaryExpr - // | MultiplicativeExpr '*' UnaryExpr - // | MultiplicativeExpr 'div' UnaryExpr - // | MultiplicativeExpr 'mod' UnaryExpr - xpath_ast_node *parse_expression() { return parse_expression_rec(parse_path_or_unary_expression(), 0); } - - xpath_parser(const char_t *query, xpath_variable_set *variables, xpath_allocator *alloc, xpath_parse_result *result) - : _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) - { - } - - xpath_ast_node *parse() - { - xpath_ast_node *result = parse_expression(); - - if (_lexer.current() != lex_eof) - { - // there are still unparsed tokens left, error - throw_error("Incorrect query"); - } - - return result; - } - - static xpath_ast_node *parse(const char_t *query, xpath_variable_set *variables, xpath_allocator *alloc, - xpath_parse_result *result) - { - xpath_parser parser(query, variables, alloc, result); - -#ifdef PUGIXML_NO_EXCEPTIONS - int error = setjmp(parser._error_handler); - - return (error == 0) ? parser.parse() : 0; -#else - return parser.parse(); -#endif - } -}; - -struct xpath_query_impl -{ - static xpath_query_impl *create() - { - void *memory = xml_memory::allocate(sizeof(xpath_query_impl)); - - return new (memory) xpath_query_impl(); - } - - static void destroy(void *ptr) - { - if (!ptr) - return; - - // free all allocated pages - static_cast(ptr)->alloc.release(); - - // free allocator memory (with the first page) - xml_memory::deallocate(ptr); - } - - xpath_query_impl() : root(0), alloc(&block) { block.next = 0; } - - xpath_ast_node *root; - xpath_allocator alloc; - xpath_memory_block block; -}; - -PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl *impl, const xpath_node &n, xpath_stack_data &sd) -{ - if (!impl) - return xpath_string(); - -#ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) - return xpath_string(); -#endif - - xpath_context c(n, 1, 1); - - return impl->root->eval_string(c, sd.stack); -} -PUGI__NS_END - -namespace pugi -{ -#ifndef PUGIXML_NO_EXCEPTIONS -PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result &result_) : _result(result_) -{ - assert(_result.error); -} - -PUGI__FN const char *xpath_exception::what() const throw() -{ - return _result.error; -} - -PUGI__FN const xpath_parse_result &xpath_exception::result() const -{ - return _result; -} -#endif - -PUGI__FN xpath_node::xpath_node() {} - -PUGI__FN xpath_node::xpath_node(const xml_node &node_) : _node(node_) {} - -PUGI__FN xpath_node::xpath_node(const xml_attribute &attribute_, const xml_node &parent_) - : _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) -{ -} - -PUGI__FN xml_node xpath_node::node() const -{ - return _attribute ? xml_node() : _node; -} - -PUGI__FN xml_attribute xpath_node::attribute() const -{ - return _attribute; -} - -PUGI__FN xml_node xpath_node::parent() const -{ - return _attribute ? _node : _node.parent(); -} - -PUGI__FN static void unspecified_bool_xpath_node(xpath_node ***) {} - -PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const -{ - return (_node || _attribute) ? unspecified_bool_xpath_node : 0; -} - -PUGI__FN bool xpath_node::operator!() const -{ - return !(_node || _attribute); -} - -PUGI__FN bool xpath_node::operator==(const xpath_node &n) const -{ - return _node == n._node && _attribute == n._attribute; -} - -PUGI__FN bool xpath_node::operator!=(const xpath_node &n) const -{ - return _node != n._node || _attribute != n._attribute; -} - -#ifdef __BORLANDC__ -PUGI__FN bool operator&&(const xpath_node &lhs, bool rhs) -{ - return (bool)lhs && rhs; -} - -PUGI__FN bool operator||(const xpath_node &lhs, bool rhs) -{ - return (bool)lhs || rhs; -} -#endif - -PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) -{ - assert(begin_ <= end_); - - size_t size_ = static_cast(end_ - begin_); - - if (size_ <= 1) - { - // deallocate old buffer - if (_begin != &_storage) - impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) - _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - } - else - { - // make heap copy - xpath_node *storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - - if (!storage) - { -#ifdef PUGIXML_NO_EXCEPTIONS - return; -#else - throw std::bad_alloc(); -#endif - } - - memcpy(storage, begin_, size_ * sizeof(xpath_node)); - - // deallocate old buffer - if (_begin != &_storage) - impl::xml_memory::deallocate(_begin); - - // finalize - _begin = storage; - _end = storage + size_; - } -} - -PUGI__FN xpath_node_set::xpath_node_set() : _type(type_unsorted), _begin(&_storage), _end(&_storage) {} - -PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_) - : _type(type_), _begin(&_storage), _end(&_storage) -{ - _assign(begin_, end_); -} - -PUGI__FN xpath_node_set::~xpath_node_set() -{ - if (_begin != &_storage) - impl::xml_memory::deallocate(_begin); -} - -PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set &ns) : _type(ns._type), _begin(&_storage), _end(&_storage) -{ - _assign(ns._begin, ns._end); -} - -PUGI__FN xpath_node_set &xpath_node_set::operator=(const xpath_node_set &ns) -{ - if (this == &ns) - return *this; - - _type = ns._type; - _assign(ns._begin, ns._end); - - return *this; -} - -PUGI__FN xpath_node_set::type_t xpath_node_set::type() const -{ - return _type; -} - -PUGI__FN size_t xpath_node_set::size() const -{ - return _end - _begin; -} - -PUGI__FN bool xpath_node_set::empty() const -{ - return _begin == _end; -} - -PUGI__FN const xpath_node &xpath_node_set::operator[](size_t index) const -{ - assert(index < size()); - return _begin[index]; -} - -PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const -{ - return _begin; -} - -PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const -{ - return _end; -} - -PUGI__FN void xpath_node_set::sort(bool reverse) -{ - _type = impl::xpath_sort(_begin, _end, _type, reverse); -} - -PUGI__FN xpath_node xpath_node_set::first() const -{ - return impl::xpath_first(_begin, _end, _type); -} - -PUGI__FN xpath_parse_result::xpath_parse_result() : error("Internal error"), offset(0) {} - -PUGI__FN xpath_parse_result::operator bool() const -{ - return error == 0; -} - -PUGI__FN const char *xpath_parse_result::description() const -{ - return error ? error : "No error"; -} - -PUGI__FN xpath_variable::xpath_variable() : _type(xpath_type_none), _next(0) {} - -PUGI__FN const char_t *xpath_variable::name() const -{ - switch (_type) - { - case xpath_type_node_set: - return static_cast(this)->name; - - case xpath_type_number: - return static_cast(this)->name; - - case xpath_type_string: - return static_cast(this)->name; - - case xpath_type_boolean: - return static_cast(this)->name; - - default: - assert(!"Invalid variable type"); - return 0; - } -} - -PUGI__FN xpath_value_type xpath_variable::type() const -{ - return _type; -} - -PUGI__FN bool xpath_variable::get_boolean() const -{ - return (_type == xpath_type_boolean) ? static_cast(this)->value : false; -} - -PUGI__FN double xpath_variable::get_number() const -{ - return (_type == xpath_type_number) ? static_cast(this)->value - : impl::gen_nan(); -} - -PUGI__FN const char_t *xpath_variable::get_string() const -{ - const char_t *value = - (_type == xpath_type_string) ? static_cast(this)->value : 0; - return value ? value : PUGIXML_TEXT(""); -} - -PUGI__FN const xpath_node_set &xpath_variable::get_node_set() const -{ - return (_type == xpath_type_node_set) ? static_cast(this)->value - : impl::dummy_node_set; -} - -PUGI__FN bool xpath_variable::set(bool value) -{ - if (_type != xpath_type_boolean) - return false; - - static_cast(this)->value = value; - return true; -} - -PUGI__FN bool xpath_variable::set(double value) -{ - if (_type != xpath_type_number) - return false; - - static_cast(this)->value = value; - return true; -} - -PUGI__FN bool xpath_variable::set(const char_t *value) -{ - if (_type != xpath_type_string) - return false; - - impl::xpath_variable_string *var = static_cast(this); - - // duplicate string - size_t size = (impl::strlength(value) + 1) * sizeof(char_t); - - char_t *copy = static_cast(impl::xml_memory::allocate(size)); - if (!copy) - return false; - - memcpy(copy, value, size); - - // replace old string - if (var->value) - impl::xml_memory::deallocate(var->value); - var->value = copy; - - return true; -} - -PUGI__FN bool xpath_variable::set(const xpath_node_set &value) -{ - if (_type != xpath_type_node_set) - return false; - - static_cast(this)->value = value; - return true; -} - -PUGI__FN xpath_variable_set::xpath_variable_set() -{ - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) - _data[i] = 0; -} - -PUGI__FN xpath_variable_set::~xpath_variable_set() -{ - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) - { - xpath_variable *var = _data[i]; - - while (var) - { - xpath_variable *next = var->_next; - - impl::delete_xpath_variable(var->_type, var); - - var = next; - } - } -} - -PUGI__FN xpath_variable *xpath_variable_set::find(const char_t *name) const -{ - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable *var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var; - - return 0; -} - -PUGI__FN xpath_variable *xpath_variable_set::add(const char_t *name, xpath_value_type type) -{ - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable *var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var->type() == type ? var : 0; - - // add new variable - xpath_variable *result = impl::new_xpath_variable(type, name); - - if (result) - { - result->_type = type; - result->_next = _data[hash]; - - _data[hash] = result; - } - - return result; -} - -PUGI__FN bool xpath_variable_set::set(const char_t *name, bool value) -{ - xpath_variable *var = add(name, xpath_type_boolean); - return var ? var->set(value) : false; -} - -PUGI__FN bool xpath_variable_set::set(const char_t *name, double value) -{ - xpath_variable *var = add(name, xpath_type_number); - return var ? var->set(value) : false; -} - -PUGI__FN bool xpath_variable_set::set(const char_t *name, const char_t *value) -{ - xpath_variable *var = add(name, xpath_type_string); - return var ? var->set(value) : false; -} - -PUGI__FN bool xpath_variable_set::set(const char_t *name, const xpath_node_set &value) -{ - xpath_variable *var = add(name, xpath_type_node_set); - return var ? var->set(value) : false; -} - -PUGI__FN xpath_variable *xpath_variable_set::get(const char_t *name) -{ - return find(name); -} - -PUGI__FN const xpath_variable *xpath_variable_set::get(const char_t *name) const -{ - return find(name); -} - -PUGI__FN xpath_query::xpath_query(const char_t *query, xpath_variable_set *variables) : _impl(0) -{ - impl::xpath_query_impl *qimpl = impl::xpath_query_impl::create(); - - if (!qimpl) - { -#ifdef PUGIXML_NO_EXCEPTIONS - _result.error = "Out of memory"; -#else - throw std::bad_alloc(); -#endif - } - else - { - impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); - - qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); - - if (qimpl->root) - { - _impl = static_cast(impl_holder.release()); - _result.error = 0; - } - } -} - -PUGI__FN xpath_query::~xpath_query() -{ - impl::xpath_query_impl::destroy(_impl); -} - -PUGI__FN xpath_value_type xpath_query::return_type() const -{ - if (!_impl) - return xpath_type_none; - - return static_cast(_impl)->root->rettype(); -} - -PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node &n) const -{ - if (!_impl) - return false; - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - -#ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) - return false; -#endif - - return static_cast(_impl)->root->eval_boolean(c, sd.stack); -} - -PUGI__FN double xpath_query::evaluate_number(const xpath_node &n) const -{ - if (!_impl) - return impl::gen_nan(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - -#ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) - return impl::gen_nan(); -#endif - - return static_cast(_impl)->root->eval_number(c, sd.stack); -} - -#ifndef PUGIXML_NO_STL -PUGI__FN string_t xpath_query::evaluate_string(const xpath_node &n) const -{ - impl::xpath_stack_data sd; - - return impl::evaluate_string_impl(static_cast(_impl), n, sd).c_str(); -} -#endif - -PUGI__FN size_t xpath_query::evaluate_string(char_t *buffer, size_t capacity, const xpath_node &n) const -{ - impl::xpath_stack_data sd; - - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); - - size_t full_size = r.length() + 1; - - if (capacity > 0) - { - size_t size = (full_size < capacity) ? full_size : capacity; - assert(size > 0); - - memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); - buffer[size - 1] = 0; - } - - return full_size; -} - -PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node &n) const -{ - if (!_impl) - return xpath_node_set(); - - impl::xpath_ast_node *root = static_cast(_impl)->root; - - if (root->rettype() != xpath_type_node_set) - { -#ifdef PUGIXML_NO_EXCEPTIONS - return xpath_node_set(); -#else - xpath_parse_result res; - res.error = "Expression does not evaluate to node set"; - - throw xpath_exception(res); -#endif - } - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - -#ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) - return xpath_node_set(); -#endif - - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack); - - return xpath_node_set(r.begin(), r.end(), r.type()); -} - -PUGI__FN const xpath_parse_result &xpath_query::result() const -{ - return _result; -} - -PUGI__FN static void unspecified_bool_xpath_query(xpath_query ***) {} - -PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const -{ - return _impl ? unspecified_bool_xpath_query : 0; -} - -PUGI__FN bool xpath_query::operator!() const -{ - return !_impl; -} - -PUGI__FN xpath_node xml_node::select_single_node(const char_t *query, xpath_variable_set *variables) const -{ - xpath_query q(query, variables); - return select_single_node(q); -} - -PUGI__FN xpath_node xml_node::select_single_node(const xpath_query &query) const -{ - xpath_node_set s = query.evaluate_node_set(*this); - return s.empty() ? xpath_node() : s.first(); -} - -PUGI__FN xpath_node_set xml_node::select_nodes(const char_t *query, xpath_variable_set *variables) const -{ - xpath_query q(query, variables); - return select_nodes(q); -} - -PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query &query) const -{ - return query.evaluate_node_set(*this); -} -} // namespace pugi - -#endif - -#ifdef __BORLANDC__ -#pragma option pop -#endif - -// Intel C++ does not properly keep warning state for function templates, -// so popping warning state at the end of translation unit leads to warnings in -// the middle. -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(pop) -#endif - -// Undefine all local macros (makes sure we're not leaking macros in header-only -// mode) -#undef PUGI__NO_INLINE -#undef PUGI__STATIC_ASSERT -#undef PUGI__DMC_VOLATILE -#undef PUGI__MSVC_CRT_VERSION -#undef PUGI__NS_BEGIN -#undef PUGI__NS_END -#undef PUGI__FN -#undef PUGI__FN_NO_INLINE -#undef PUGI__IS_CHARTYPE_IMPL -#undef PUGI__IS_CHARTYPE -#undef PUGI__IS_CHARTYPEX -#undef PUGI__SKIPWS -#undef PUGI__OPTSET -#undef PUGI__PUSHNODE -#undef PUGI__POPNODE -#undef PUGI__SCANFOR -#undef PUGI__SCANWHILE -#undef PUGI__ENDSEG -#undef PUGI__THROW_ERROR -#undef PUGI__CHECK_ERROR - -#endif - -/** - * Copyright (c) 2006-2014 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ From 5e795d7a7c263acee2695a07d92f86bc5b940709 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:19:07 +0100 Subject: [PATCH 37/51] replace custom smart enum types with safer third party implementation --- CMakeLists.txt | 10 ++ .../fault/MemoryManipulationSystem.h | 41 ++--- include/etiss/fault/Action.h | 64 +++----- include/etiss/fault/Fault.h | 4 +- include/etiss/fault/Misc.h | 57 ------- include/etiss/fault/Trigger.h | 20 +-- .../fault/MemoryManipulationSystem.cpp | 58 +++---- src/VirtualStruct.cpp | 28 ++-- src/fault/Action.cpp | 153 +++++++++--------- src/fault/Stressor.cpp | 12 +- src/fault/Trigger.cpp | 142 ++++++++-------- 11 files changed, 239 insertions(+), 350 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04d1d933cc..9301d52a6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,6 +257,15 @@ if(NOT pugixml_srcs_POPULATED) endif() add_subdirectory(${pugixml_srcs_SOURCE_DIR} ${pugixml_srcs_BINARY_DIR} EXCLUDE_FROM_ALL) +FetchContent_Declare(betterenums_srcs + GIT_REPOSITORY https://github.com/aantron/better-enums.git + GIT_TAG 0.11.3 +) +if(NOT betterenums_srcs_POPULATED) + FetchContent_Populate(betterenums_srcs) + FetchContent_GetProperties(betterenums_srcs) +endif() + ### Doxyfile set(ETISS_DOX_LOCATIONS ${ETISS_DOX_LOCATIONS} ${PROJECT_BINARY_DIR}/include) set(ETISS_DOX_LOCATIONS ${ETISS_DOX_LOCATIONS} ${PROJECT_BINARY_DIR}/include_c) @@ -382,6 +391,7 @@ TARGET_INCLUDE_DIRECTORIES(ETISS PUBLIC ${INCGEN_INC_DIR} ${elfio_srcs_SOURCE_DIR} ${simpleini_srcs_SOURCE_DIR} + ${betterenums_srcs_SOURCE_DIR} ) TARGET_LINK_LIBRARIES(ETISS PUBLIC pugixml) diff --git a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h index cbf1738dbd..26059d9d24 100644 --- a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h +++ b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h @@ -65,27 +65,20 @@ #include #include "etiss/fault/Misc.h" +#include "enum.h" namespace etiss { + +BETTER_ENUM(MM_MemOpType, char, UNDEF = 0, COPY, AND, OR, XOR, NAND, NOR) +BETTER_ENUM(MM_MemManipCmd, char, UNDEF = 0, PUSH, POP, OR, RMW, RRMW) + //////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory word faulter base class class MemoryWordManipulatorBase { public: - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Memory operation type code - enum class MemOpType - { - COPY, - AND, - OR, - XOR, - NAND, - NOR, - UNDEF - }; - typedef etiss::fault::SmartType mem_op_t; + typedef MM_MemOpType mem_op_t; //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory operation class class MemOp : mem_op_t @@ -102,15 +95,7 @@ class MemoryWordManipulatorBase //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Memory operation type code - enum class MemManipCmd - { - PUSH, - POP, - RMW, - RRMW, - UNDEF - }; - typedef etiss::fault::SmartType mem_manip_cmd_t; + typedef MM_MemManipCmd mem_manip_cmd_t; virtual etiss::int32 push(size_t address) = 0; virtual etiss::int32 pop(size_t address) = 0; @@ -189,17 +174,17 @@ word_t MemoryWordManipulatorBase::MemOp::operator()(word_t src1, word_t src2) co { switch (*this) { - case MemOpType::COPY: + case MM_MemOpType::COPY: return src2; - case MemOpType::AND: + case MM_MemOpType::AND: return (src1 & src2); - case MemOpType::OR: + case MM_MemOpType::OR: return (src1 | src2); - case MemOpType::XOR: + case MM_MemOpType::XOR: return (src1 ^ src2); - case MemOpType::NAND: + case MM_MemOpType::NAND: return ~(src1 & src2); - case MemOpType::NOR: + case MM_MemOpType::NOR: return ~(src1 | src2); default: break; diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index d67b9d1e52..95c537f67c 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -59,11 +59,11 @@ #include "etiss/Misc.h" #include "etiss/fault/Defs.h" #include "etiss/fault/XML.h" -#include "etiss/fault/Misc.h" +#include "enum.h" #else #include "fault/Defs.h" #include "fault/XML.h" -#include "fault/Misc.h" +#include "enum.h" #endif namespace etiss @@ -74,40 +74,20 @@ namespace fault class FaultRef; class InjectorAddress; +// BITFLIP: applies a bit flip to a bit in a specified field +// MASK: applies a mask type injection (field = mask;) where can be any MaskOp +// COMMAND: commands are targetet at Injectors, not fields. in case a command is targetet at a certain field that +// information must be passed within the command string INJECTION: an action that injects a fault definition (trigger + +// actions) EJECTION: an action that ejects a referenced fault (which must exist) +// EVENT: an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE exception +BETTER_ENUM(Action_Type, char, NOP = 0, BITFLIP, MASK, COMMAND, INJECTION, EJECTION, EVENT) +BETTER_ENUM(Action_MaskOp, char, NOP = 0, AND, OR, XOR, NAND, NOR) + class Action : public etiss::ToString { public: - enum class Type - { - NOP = 0 /// NO Operation. used by default constructor - , - BITFLIP /// applies a bit flip to a bit in a specified field - , - MASK /// applies a mask type injection (field = mask;) where can be any MaskOp - , - COMMAND /// commands are targetet at Injectors, not fields. in case a command is targetet at a certain field - /// that information must be passed within the command string - , - INJECTION /// an action that injects a fault definition (trigger + actions) - , - EJECTION /// an action that ejects a referenced fault (must exist) -#ifndef NO_ETISS - , - EVENT /// an event that breaks the JIT-block and forces the simulation loop to handle the etiss::RETURNCODE - /// exception -#endif - }; - typedef SmartType type_t; - enum class MaskOp - { - NOP, - AND, - OR, - XOR, - NAND, - NOR - }; - typedef SmartType mask_op_t; + typedef Action_Type type_t; + typedef Action_MaskOp mask_op_t; /** * @brief returns true if type_ is an action on a Field */ @@ -143,7 +123,7 @@ class Action : public etiss::ToString * * @brief applies a mask type injection (field op= mask;) where can be bitwise AND, OR, XOR, NAND, NOR */ - Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value); + Action(const InjectorAddress &inj, const std::string &field, mask_op_t mask_op, uint64_t mask_value); /** * @note Type: Injection * @@ -184,21 +164,21 @@ class Action : public etiss::ToString // Members std::string toString() const; ///< operator<< can be used. - private: // Attributes - type_t type_; ///< type of the Attribute + private: // Attributes + type_t type_; ///< type of the Attribute std::unique_ptr inj_; ///< Address of Injector - std::string command_; ///< command e.g. for booting OR1KVCPU - std::string field_; ///< concerning Field (for fault injection) - unsigned bit_ = { 0 }; ///< concerning Bit (for fault injection) - mask_op_t mask_op_{ MaskOp::NOP }; ///< mask operation (for mask injection) - uint64_t mask_value_{ 0 }; ///< mask value (for mask injection) + std::string command_; ///< command e.g. for booting OR1KVCPU + std::string field_; ///< concerning Field (for fault injection) + unsigned bit_ = { 0 }; ///< concerning Bit (for fault injection) + mask_op_t mask_op_{ mask_op_t::NOP }; ///< mask operation (for mask injection) + uint64_t mask_value_{ 0 }; ///< mask value (for mask injection) std::unique_ptr fault_ref_; ///< for fault injection #ifndef NO_ETISS int32_t event_{ 0 }; ///< exception, or rather etiss::RETURNCODE to /// to be injected into the simulation loop #endif // private Members - void ensure(Type); + void ensure(type_t); }; #ifndef NO_ETISS diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index 4cebaeb890..26320ff9dd 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -103,8 +103,8 @@ class Fault : public etiss::ToString class FaultRef : public etiss::ToString { private: - mutable Fault fault_; ///< referenced Fault, needs to be resolved during sim. runtime - std::string name_; ///< string identifier, used to resolve actual reference via fault_ + mutable Fault fault_; ///< referenced Fault, needs to be resolved during sim. runtime + std::string name_; ///< string identifier, used to resolve actual reference via fault_ public: std::string toString() const; ///< operator<< can be used. diff --git a/include/etiss/fault/Misc.h b/include/etiss/fault/Misc.h index 8d460acb60..b5b3178190 100644 --- a/include/etiss/fault/Misc.h +++ b/include/etiss/fault/Misc.h @@ -65,63 +65,6 @@ namespace etiss namespace fault { -//////////////////////////////////////////////////////////////////////////////////////////////// -/// \brief Action type class -template -class SmartType : public etiss::ToString -{ - public: - typedef std::map map_t; - - private: - enum_t type_; - static map_t TABLE; - - public: - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief decode a string \p type_str to \ref enum_t, return true if successful - static bool fromString(const std::string &type_str, enum_t &out) - { - for (auto const &e : TABLE) - { - if (type_str == e.second) - { - out = e.first; - return true; - } - } - return false; - } - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief convert a \ref enum_t \p in to a std::string with the static \ref TABLE - static std::string toString(enum_t in) { return TABLE.at(in); } - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief return self as string - std::string toString() const { return toString(type_); } - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Constructor takes \p type specifying action type - SmartType() : type_() {} - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Constructor takes \p type specifying action type - SmartType(enum_t type) : type_(type) {} - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Constructor takes \p type_str specifying action type string encoded - /// \note If string does not match, default \ref enum_t() is used. Make sure to adapt - SmartType(const std::string &type_str) : type_() - { - auto ret = fromString(type_str, type_); - if (!ret) - etiss::log(etiss::ERROR, std::string("SmartType: Unrecognized type string: \"") + type_str + - std::string("\" using default enum_t()")); - } - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Typecast operator to \ref code_t - operator enum_t() const { return type_; } - //////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Typecast operator to \ref code_t - operator std::string() const { return toString(); } -} /* class SmartType */; - } // end of namespace fault } // end of namespace etiss diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index 6a5e94a6db..dadc45adb6 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -68,6 +68,8 @@ #include "fault/Misc.h" #endif +#include "enum.h" + namespace etiss { namespace fault @@ -82,22 +84,12 @@ typedef std::shared_ptr Injector_ptr; typedef Injector *Injector_ptr; #endif +BETTER_ENUM(Trigger_Type, char, NOP = 0, META_COUNTER, VARIABLEVALUE, TIME, TIMERELATIVE, ASAP) + class Trigger : public etiss::ToString { public: - enum class Type - { - META_COUNTER, - VARIABLEVALUE, - TIME - /// needs to be resolved. this can only be used in connection with an - /// injection action - , - TIMERELATIVE, - ASAP, - NOP - }; - typedef SmartType type_t; + typedef Trigger_Type type_t; // constructors /** Type: NOP (no operation) @@ -176,7 +168,7 @@ class Trigger : public etiss::ToString uint64_t param2_{ 0 }; // Private Members - void ensure(Type type) const; + void ensure(type_t type) const; }; #if ETISS_FAULT_XML diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp index 5adc85c729..f0e396e60f 100644 --- a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -116,13 +116,15 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); return x; }; - auto write = [this](size_t address, etiss::uint32 word, etiss::int32 &return_code) { + auto write = [this](size_t address, etiss::uint32 word, etiss::int32 &return_code) + { return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); }; @@ -130,13 +132,15 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr(nullptr, static_cast(address), reinterpret_cast(&x), sizeof(x)); return x; }; - auto write = [this](size_t address, etiss::uint64 word, etiss::int32 &return_code) { + auto write = [this](size_t address, etiss::uint64 word, etiss::int32 &return_code) + { return_code = dbus_access(nullptr, static_cast(address), reinterpret_cast(&word), sizeof(word)); }; @@ -148,8 +152,9 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrapplyCustomAction = [this](const etiss::fault::Fault &fault, const etiss::fault::Action &action, - std::string &errormsg) { + getStruct()->applyCustomAction = + [this](const etiss::fault::Fault &fault, const etiss::fault::Action &action, std::string &errormsg) + { auto cmd = action.getCommand(); size_t pos = 0; @@ -166,16 +171,21 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrpush(dst_address); break; - case MemoryWordManipulatorBase::MemManipCmd::POP: + case MemoryWordManipulatorBase::mem_manip_cmd_t::POP: return_code = mem_manipulator_->pop(dst_address); break; - case MemoryWordManipulatorBase::MemManipCmd::RMW: + case MemoryWordManipulatorBase::mem_manip_cmd_t::RMW: { etiss::uint64 val; val = std::stoll(split_cmd[3], nullptr, 16); @@ -183,7 +193,7 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptrrmw(dst_address, MemoryWordManipulatorBase::MemOp(split_cmd[2]), val); break; } - case MemoryWordManipulatorBase::MemManipCmd::RRMW: + case MemoryWordManipulatorBase::mem_manip_cmd_t::RRMW: { etiss::uint64 src2_addr; src2_addr = std::stoll(split_cmd[3], nullptr, 16); @@ -222,29 +232,3 @@ MemoryManipulationSystem::MemoryManipulationSystem(const std::string &name) : SimpleMemSystem(), name_(name), vsystem_(), mem_manipulator_() { } - -/* MemoryWordManipulatorBase implementation */ - -template <> -etiss::MemoryWordManipulatorBase::mem_op_t::map_t etiss::MemoryWordManipulatorBase::mem_op_t::TABLE = { - { MemoryWordManipulatorBase::MemOpType::COPY, "COPY" }, { MemoryWordManipulatorBase::MemOpType::AND, "OR" }, - { MemoryWordManipulatorBase::MemOpType::OR, "OR" }, { MemoryWordManipulatorBase::MemOpType::XOR, "XOR" }, - { MemoryWordManipulatorBase::MemOpType::NAND, "NAND" }, { MemoryWordManipulatorBase::MemOpType::NOR, "NOR" }, - { MemoryWordManipulatorBase::MemOpType::UNDEF, "UNDEF" } -}; - -MemoryWordManipulatorBase::MemOp::MemOp(const std::string &memop_str) : mem_op_t(memop_str) -{ - if (*this == MemOpType::UNDEF) - etiss::log(etiss::FATALERROR, - std::string("MemoryManipulationSystem/MemFaulter: Unrecognized op code: ") + memop_str); -} - -template <> -etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::map_t etiss::MemoryWordManipulatorBase::mem_manip_cmd_t::TABLE = { - { MemoryWordManipulatorBase::MemManipCmd::PUSH, "push" }, - { MemoryWordManipulatorBase::MemManipCmd::POP, "pop" }, - { MemoryWordManipulatorBase::MemManipCmd::RMW, "rmw" }, - { MemoryWordManipulatorBase::MemManipCmd::RRMW, "rrmw" }, - { MemoryWordManipulatorBase::MemManipCmd::UNDEF, "UNDEF" } -}; diff --git a/src/VirtualStruct.cpp b/src/VirtualStruct.cpp index caf44e2f76..4b19e198e3 100644 --- a/src/VirtualStruct.cpp +++ b/src/VirtualStruct.cpp @@ -214,39 +214,39 @@ bool VirtualStruct::Field::_applyBitflip(unsigned position, uint64_t fault_id) bool VirtualStruct::Field::_applyAction(const etiss::fault::Fault &f, const etiss::fault::Action &a, std::string &errormsg) { - if (a.getType() == etiss::fault::Action::Type::MASK) + if (a.getType() == +etiss::fault::Action::type_t::MASK) { uint64_t mask_value = a.getMaskValue(); uint64_t val = read(), errval; switch (a.getMaskOp()) { - case etiss::fault::Action::MaskOp::AND: + case etiss::fault::Action::mask_op_t::AND: errval = (val & mask_value); break; - case etiss::fault::Action::MaskOp::OR: + case etiss::fault::Action::mask_op_t::OR: errval = (val | mask_value); break; - case etiss::fault::Action::MaskOp::XOR: + case etiss::fault::Action::mask_op_t::XOR: errval = (val ^ mask_value); break; - case etiss::fault::Action::MaskOp::NAND: + case etiss::fault::Action::mask_op_t::NAND: errval = ~(val & mask_value); break; - case etiss::fault::Action::MaskOp::NOR: + case etiss::fault::Action::mask_op_t::NOR: errval = ~(val | mask_value); break; - case etiss::fault::Action::MaskOp::NOP: + case etiss::fault::Action::mask_op_t::NOP: errval = val; break; } write(errval); std::stringstream ss; - ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << std::string(a.getMaskOp()) + ss << "Injected mask fault in " << name_ << " 0x" << std::hex << val << " " << a.getMaskOp() << " 0x" << mask_value << "->0x" << errval << std::dec; etiss::log(etiss::INFO, ss.str()); return true; } - else if (a.getType() == etiss::fault::Action::Type::BITFLIP) + else if (a.getType() == +etiss::fault::Action::type_t::BITFLIP) { return applyBitflip(a.getTargetBit(), f.id_); } @@ -504,7 +504,7 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f { switch (action.getType()) { - case etiss::fault::Action::Type::COMMAND: // handle command + case +etiss::fault::Action::type_t::COMMAND: // handle command { if (!applyCustomAction) { @@ -514,9 +514,9 @@ bool VirtualStruct::applyAction(const etiss::fault::Fault &fault, const etiss::f } return applyCustomAction(fault, action, errormsg); } - case etiss::fault::Action::Type::MASK: + case +etiss::fault::Action::type_t::MASK: [[fallthrough]]; - case etiss::fault::Action::Type::BITFLIP: // handle bitflip + case +etiss::fault::Action::type_t::BITFLIP: // handle bitflip { Field *f; auto find = fieldNames_.find(action.getTargetField()); @@ -574,9 +574,9 @@ bool VirtualStruct::update_field_access_rights(const etiss::fault::Action &actio { switch (action.getType()) { - case etiss::fault::Action::Type::MASK: + case +etiss::fault::Action::type_t::MASK: [[fallthrough]]; - case etiss::fault::Action::Type::BITFLIP: + case +etiss::fault::Action::type_t::BITFLIP: f->flags_ |= Field::F; break; default: diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index cef5b7879b..3999a18422 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -60,7 +60,7 @@ namespace etiss namespace fault { -void Action::ensure(Type t) +void Action::ensure(type_t t) { if (type_ != t) throw "wrong action type"; @@ -68,56 +68,57 @@ void Action::ensure(Type t) bool Action::is_action_on_field(void) const { - return ((type_ == Type::BITFLIP || type_ == Type::MASK) ? true : false); + return ((type_ == +type_t::BITFLIP || type_ == +type_t::MASK) ? true : false); } -Action::Action() : type_(Type::NOP) +Action::Action() : type_(type_t::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action() called. ")); } #ifndef NO_ETISS -Action::Action(int32_t event) : type_(Type::EVENT), event_(event) +Action::Action(int32_t event) : type_(type_t::EVENT), event_(event) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(int32_t exception) called. ")); } #endif Action::Action(const InjectorAddress &inj, const std::string &command) - : type_(Type::COMMAND), inj_(std::make_unique(inj)), command_(command) + : type_(type_t::COMMAND), inj_(std::make_unique(inj)), command_(command) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", command=") + command + std::string(") called. ")); } Action::Action(const InjectorAddress &inj, const std::string &field, unsigned bit) - : type_(Type::BITFLIP), inj_(std::make_unique(inj)), field_(field), bit_(bit) + : type_(type_t::BITFLIP), inj_(std::make_unique(inj)), field_(field), bit_(bit) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + std::string(", field=") + field + std::string(", bit=") + std::to_string(bit) + std::string(") called. ")); } -Action::Action(const InjectorAddress &inj, const std::string &field, MaskOp mask_op, uint64_t mask_value) - : type_(Type::MASK) +Action::Action(const InjectorAddress &inj, const std::string &field, mask_op_t mask_op, uint64_t mask_value) + : type_(type_t::MASK) , inj_(std::make_unique(inj)) , field_(field) , mask_op_(mask_op) , mask_value_(mask_value) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(InjectorAddress &=") + inj.getInjectorPath() + - std::string(", field=") + field + std::string(", mask_op=") + std::string(mask_op_) + + std::string(", field=") + field + std::string(", mask_op=") + mask_op_._to_string() + std::string(", mask_value=") + std::to_string(mask_value) + std::string(") called. ")); } -Action::Action(const FaultRef &fault_ref, type_t type) : type_(type), fault_ref_(std::make_unique(fault_ref)) +Action::Action(const FaultRef &fault_ref, type_t type) + : type_(type), fault_ref_(std::make_unique(fault_ref)) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Action::Action(FaultRef &=") + fault_ref.toString() + std::string(") called. ")); } -Action::Action(const Action &cpy) +Action::Action(const Action &cpy) : type_(cpy.getType()) { *this = cpy; } @@ -127,32 +128,32 @@ Action &Action::operator=(const Action &cpy) type_ = cpy.type_; switch (type_) { - case Type::BITFLIP: + case type_t::BITFLIP: inj_ = std::make_unique(cpy.getInjectorAddress()); field_ = cpy.getTargetField(); bit_ = cpy.getTargetBit(); break; - case Type::MASK: + case type_t::MASK: inj_ = std::make_unique(cpy.getInjectorAddress()); field_ = cpy.getTargetField(); mask_op_ = cpy.getMaskOp(); mask_value_ = cpy.getMaskValue(); break; - case Type::COMMAND: + case type_t::COMMAND: inj_ = std::make_unique(cpy.getInjectorAddress()); command_ = cpy.getCommand(); break; - case Type::EJECTION: + case type_t::EJECTION: [[fallthrough]]; - case Type::INJECTION: + case type_t::INJECTION: fault_ref_ = std::make_unique(cpy.getFaultRef()); break; #ifndef NO_ETISS - case Type::EVENT: + case type_t::EVENT: event_ = cpy.getEvent(); break; #endif - case Type::NOP: + case type_t::NOP: break; } return *this; @@ -160,6 +161,7 @@ Action &Action::operator=(const Action &cpy) #if CXX0X_UP_SUPPORTED Action::Action(Action &&cpy) + : type_(cpy.getType()) { operator=(cpy); } @@ -177,7 +179,7 @@ const Action::type_t &Action::getType() const const InjectorAddress &Action::getInjectorAddress() const { - if (!(type_ == Type::BITFLIP || type_ == Type::MASK || type_ == Type::COMMAND)) + if (!(type_ == +type_t::BITFLIP || type_ == +type_t::MASK || type_ == +type_t::COMMAND)) etiss::log( etiss::FATALERROR, std::string( @@ -188,7 +190,7 @@ const InjectorAddress &Action::getInjectorAddress() const /// COMMAND only const std::string &Action::getCommand() const { - if (type_ != Type::COMMAND) + if (type_ != +type_t::COMMAND) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getCommand(): Requested Action::Type is not Command")); return command_; @@ -197,7 +199,7 @@ const std::string &Action::getCommand() const /// is_action_on_field only const std::string &Action::getTargetField() const { - if (!(type_ == Type::BITFLIP || type_ == Type::MASK)) + if (!(type_ == +type_t::BITFLIP || type_ == +type_t::MASK)) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetField(): Requested Action::Type is not TargetField")); return field_; @@ -206,7 +208,7 @@ const std::string &Action::getTargetField() const /// BITFLIP only unsigned Action::getTargetBit() const { - if (type_ != Type::BITFLIP) + if (type_ != +type_t::BITFLIP) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getTargetBit(): Requested Action::Type is not TargetBit")); return bit_; @@ -214,7 +216,7 @@ unsigned Action::getTargetBit() const const FaultRef &Action::getFaultRef() const { - if (!((type_ == Type::INJECTION) || (type_ == Type::EJECTION))) + if (!((type_ == +type_t::INJECTION) || (type_ == +type_t::EJECTION))) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getFault(): Requested Action::Type is not injectable Fault")); return *fault_ref_; @@ -222,7 +224,7 @@ const FaultRef &Action::getFaultRef() const const Action::mask_op_t &Action::getMaskOp() const { - if (type_ != Type::MASK) + if (type_ != +type_t::MASK) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskOp(): Requested Action::Type is not Mask")); return mask_op_; @@ -230,7 +232,7 @@ const Action::mask_op_t &Action::getMaskOp() const uint64_t Action::getMaskValue() const { - if (type_ != Type::MASK) + if (type_ != +type_t::MASK) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getMaskValue(): Requested Action::Type is not Mask")); return mask_value_; @@ -239,7 +241,7 @@ uint64_t Action::getMaskValue() const #ifndef NO_ETISS int32_t Action::getEvent() const { - if (type_ != Type::EVENT) + if (type_ != +type_t::EVENT) etiss::log(etiss::FATALERROR, std::string("etiss::fault::Action::getEvent(): Requested Action::Type is not Event")); return event_; @@ -251,9 +253,9 @@ std::string Action::toString() const pugi::xml_document doc; doc.load_string(""); - etiss::fault::xml::Diagnostics diag; + xml::Diagnostics diag; - etiss::fault::xml::write(doc.append_child("action"), *this, diag); + xml::write(doc.append_child("action"), *this, diag); std::stringstream ss; @@ -266,29 +268,38 @@ std::string Action::toString() const namespace xml { template <> -bool parse(pugi::xml_node node, etiss::fault::Action &f, Diagnostics &diag) +bool parse(pugi::xml_node node, Action &f, Diagnostics &diag) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::parse(node, Action") + std::string(", Diagnostics) called. ")); - std::string type; - if (!parse_attr(node, "type", type, diag)) + std::string type_s; + if (!parse_attr(node, "type", type_s, diag)) { diag.unexpectedNode(node, "Failed to parse type of action"); return false; } + if(! Action::type_t::_from_string_nothrow(type_s.c_str())) + { + diag.unexpectedNode(node, std::string("There is no Action type ") + type_s); + return false; + } + auto type = Action::type_t::_from_string(type_s.c_str()); - if (type == "NOP") + switch (type) + { + case Action::type_t::NOP: { f = Action(); return true; + break; } - else if (type == "BITFLIP") + case Action::type_t::BITFLIP: { - etiss::fault::InjectorAddress inj; + InjectorAddress inj; if (!etiss::cfg().get("faultInjection::allowBitFlip", true)) return true; - if (!parse(findSingleNode(node, "injector", diag), inj, diag)) + if (!parse(findSingleNode(node, "injector", diag), inj, diag)) { diag.unexpectedNode(node, "Failed to parse target "); return false; @@ -308,11 +319,12 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D } f = Action(inj, field, bit); return true; + break; } - else if (type == "COMMAND") + case Action::type_t::COMMAND: { - etiss::fault::InjectorAddress inj; - if (!parse(findSingleNode(node, "injector", diag), inj, diag)) + InjectorAddress inj; + if (!parse(findSingleNode(node, "injector", diag), inj, diag)) { diag.unexpectedNode(node, "Failed to parse target "); return false; @@ -325,10 +337,13 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D } f = Action(inj, command); return true; + break; } - else if ((type == "INJECTION") || (type == "EJECTION")) + case Action::type_t::INJECTION: + [[fallthrough]]; + case Action::type_t::EJECTION: { - etiss::fault::FaultRef fault_ref; + FaultRef fault_ref; if (!parse(findSingleNode(node, "fault_ref", diag), fault_ref, diag)) { diag.unexpectedNode(node, "Failed to parse to inject"); @@ -336,11 +351,12 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D } f = Action(fault_ref, type); return true; + break; } - else if (type == "MASK") + case Action::type_t::MASK: { - etiss::fault::InjectorAddress inj; - if (!parse(findSingleNode(node, "injector", diag), inj, diag)) + InjectorAddress inj; + if (!parse(findSingleNode(node, "injector", diag), inj, diag)) { diag.unexpectedNode(node, "Failed to parse target "); return false; @@ -353,13 +369,13 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D } setCoreName(field); std::string op_str; - etiss::fault::Action::MaskOp op; if (!parse(findSingleNode(node, "op", diag), op_str, diag)) { diag.unexpectedNode(node, "Failed to parse mask operation "); return false; } - if (!etiss::fault::Action::mask_op_t::fromString(op_str, op)) + + if (!(etiss::fault::Action::mask_op_t::_from_string_nothrow(op_str.c_str()))) { diag.unexpectedNode(node, "Failed to parse mask operation "); return false; @@ -370,11 +386,12 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D diag.unexpectedNode(node, "Failed to parse mask operation "); return false; } - f = Action(inj, field, op, value); + f = Action(inj, field, Action::mask_op_t::_from_string(op_str.c_str()), value); return true; + break; } #ifndef NO_ETISS - else if (type == "EVENT") + case Action::type_t::EVENT: { std::string event_str; int32_t event; @@ -393,51 +410,51 @@ bool parse(pugi::xml_node node, etiss::fault::Action &f, D return true; } #endif - else - { - diag.unexpectedNode(node, std::string("Unknown type of action: ") + type); + default: + diag.unexpectedNode(node, std::string("Unknown type of action: ") + type._to_string()); return false; + break; } return false; } template <> -bool write(pugi::xml_node node, const etiss::fault::Action &f, Diagnostics &diag) +bool write(pugi::xml_node node, const Action &f, Diagnostics &diag) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::write(node, Action&=") + std::string(", Diagnostics) called. ")); bool ok = true; switch (f.getType()) { - case etiss::fault::Action::Type::NOP: + case Action::type_t::NOP: return write_attr(node, "type", "NOP", diag); break; - case etiss::fault::Action::Type::BITFLIP: + case Action::type_t::BITFLIP: ok = ok & write_attr(node, "type", "BITFLIP", diag); ok = ok & write(node.append_child("injector"), f.getInjectorAddress(), diag); ok = ok & write(node.append_child("field"), f.getTargetField(), diag); ok = ok & write(node.append_child("bit"), f.getTargetBit(), diag); break; - case etiss::fault::Action::Type::COMMAND: + case Action::type_t::COMMAND: ok = ok & write_attr(node, "type", "COMMAND", diag); ok = ok & write(node.append_child("command"), f.getCommand(), diag); break; - case etiss::fault::Action::Type::INJECTION: + case Action::type_t::INJECTION: ok = ok & write_attr(node, "type", "INJECTION", diag); ok = ok & write(node.append_child("fault_ref"), f.getFaultRef(), diag); break; - case etiss::fault::Action::Type::EJECTION: + case Action::type_t::EJECTION: ok = ok & write_attr(node, "type", "EJECTION", diag); ok = ok & write(node.append_child("fault_ref"), f.getFaultRef(), diag); break; - case etiss::fault::Action::Type::MASK: + case Action::type_t::MASK: ok = ok & write_attr(node, "type", "MASK", diag); ok = ok & write(node.append_child("injector"), f.getInjectorAddress(), diag); ok = ok & write(node.append_child("field"), f.getTargetField(), diag); - ok = ok & write(node.append_child("op"), SmartType::toString(f.getMaskOp()), diag); + ok = ok & write(node.append_child("op"), f.getMaskOp()._to_string(), diag); ok = ok & write(node.append_child("value"), f.getMaskValue(), diag); break; #ifndef NO_ETISS - case etiss::fault::Action::Type::EVENT: + case Action::type_t::EVENT: ok = ok & write_attr(node, "type", "EVENT", diag); ok = ok & write(node.append_child("cause"), etiss::fault::returncode_tostring(f.getEvent()), diag); break; @@ -574,24 +591,6 @@ std::string returncode_tostring(int32_t in) #endif -template <> -Action::type_t::map_t Action::type_t::TABLE = { { Action::Type::NOP, "NOP" }, - { Action::Type::BITFLIP, "BITFLIP" }, - { Action::Type::MASK, "MASK" }, - { Action::Type::COMMAND, "COMMAND" }, - { Action::Type::INJECTION, "INJECTION" }, - { Action::Type::EJECTION, "EJECTION" } -#ifndef NO_ETISS - , - { Action::Type::EVENT, "EVENT" } -#endif -}; - -template <> -Action::mask_op_t::map_t Action::mask_op_t::TABLE = { { Action::MaskOp::AND, "AND" }, { Action::MaskOp::OR, "OR" }, - { Action::MaskOp::XOR, "XOR" }, { Action::MaskOp::NAND, "NAND" }, - { Action::MaskOp::NOR, "NOR" }, { Action::MaskOp::NOP, "NOP" } }; - } // namespace fault } // namespace etiss diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 6658034f4b..5e7f19c4da 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -259,7 +259,7 @@ bool Stressor::addFault(const Fault &f, bool injected_fault) for (const auto &trigger : f.triggers) { - if (trigger.getType() != etiss::fault::Trigger::Type::NOP) // only add Trigger, if it is not a NOP + if (trigger.getType() != +etiss::fault::Trigger_Type::NOP) // only add Trigger, if it is not a NOP { iptr = trigger.getInjector(); @@ -343,7 +343,7 @@ bool Stressor::removeFault(const Fault &f, bool injected_fault) for (const auto &trigger : f.triggers) { - if (trigger.getType() != etiss::fault::Trigger::Type::NOP) // don't care + if (trigger.getType() != +etiss::fault::Trigger_Type::NOP) // don't care { iptr = trigger.getInjector(); @@ -400,7 +400,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector { switch (iter->getType()) { - case etiss::fault::Action::Type::INJECTION: + case +etiss::fault::Action_Type::INJECTION: if (!iter->getFaultRef().is_set()) { // try to resolve the reference again @@ -415,7 +415,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector } addFault(iter->getFaultRef().get_fault(), true); break; - case etiss::fault::Action::Type::EJECTION: + case +etiss::fault::Action_Type::EJECTION: if (!iter->getFaultRef().is_set()) { // try to resolve the reference again @@ -430,12 +430,12 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector } removeFault(iter->getFaultRef().get_fault(), true); break; - case etiss::fault::Action::Type::NOP: + case +etiss::fault::Action_Type::NOP: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); return true; #ifndef NO_ETISS - case etiss::fault::Action::Type::EVENT: + case +etiss::fault::Action_Type::EVENT: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EVENT")); set_event(iter->getEvent()); return true; diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index e29721ccc8..9d968929a1 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -61,35 +61,35 @@ namespace etiss namespace fault { -void Trigger::ensure(Type type) const +void Trigger::ensure(type_t type) const { if (type_ != type) { etiss::log(etiss::FATALERROR, std::string("etiss::fault::Trigger::ensure: Type mismatch type=") + - type_t::toString(type) + " type_=" + std::string(type_)); + type._to_string() + " type_=" + type_._to_string()); throw "called function of different trigger type"; } } -Trigger::Trigger() : type_(Type::NOP) +Trigger::Trigger() : type_(type_t::NOP) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (NOP)")); } Trigger::Trigger(const InjectorAddress &target_injector) - : type_(Type::ASAP), inj_(std::make_unique(target_injector)) + : type_(type_t::ASAP), inj_(std::make_unique(target_injector)) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger(const InjectorAddress &target_injector) : type_ (ASAP)")); } Trigger::Trigger(const Trigger &sub, uint64_t count) - : type_(Type::META_COUNTER), sub_(new Trigger(sub)), param1_(count), param2_(0) + : type_(type_t::META_COUNTER), sub_(new Trigger(sub)), param1_(count), param2_(0) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (META_COUNTER)")); } Trigger::Trigger(const InjectorAddress &target_injector, const std::string &field, uint64_t value) - : type_(Type::VARIABLEVALUE) + : type_(type_t::VARIABLEVALUE) , field_(field) , inj_(std::make_unique(target_injector)) , fieldptr_(0) @@ -98,7 +98,7 @@ Trigger::Trigger(const InjectorAddress &target_injector, const std::string &fiel etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (VARIABLEVALUE)")); } Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool relative) - : type_(relative ? Type::TIMERELATIVE : Type::TIME) + : type_(relative ? type_t::TIMERELATIVE : type_t::TIME) , inj_(std::make_unique(target_injector)) , param1_(time_ps) , param2_(0) @@ -107,13 +107,14 @@ Trigger::Trigger(const InjectorAddress &target_injector, uint64_t time_ps, bool : etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::Trigger() : type_ (TIME)")); } -Trigger::Trigger(const Trigger &cpy) : type_(Type::NOP) +Trigger::Trigger(const Trigger &cpy) : type_(type_t::NOP) { *this = cpy; } + Trigger &Trigger::operator=(const Trigger &cpy) { - if (type_ == Type::VARIABLEVALUE) + if (type_ == +type_t::VARIABLEVALUE) { if (inj_->isResolved() && fieldptr_) { @@ -123,35 +124,35 @@ Trigger &Trigger::operator=(const Trigger &cpy) type_ = cpy.type_; switch (type_) { - case Type::META_COUNTER: + case type_t::META_COUNTER: sub_ = std::make_unique(cpy.getSubTrigger()); param1_ = cpy.param1_; param2_ = cpy.param2_; break; - case Type::VARIABLEVALUE: + case type_t::VARIABLEVALUE: field_ = cpy.field_; inj_ = std::make_unique(cpy.getInjectorAddress()); fieldptr_ = 0; param1_ = cpy.param1_; break; - case Type::TIMERELATIVE: + case type_t::TIMERELATIVE: [[fallthrough]]; - case Type::TIME: + case type_t::TIME: inj_ = std::make_unique(cpy.getInjectorAddress()); param1_ = cpy.param1_; param2_ = cpy.param2_; break; - case Type::ASAP: + case type_t::ASAP: inj_ = std::make_unique(cpy.getInjectorAddress()); break; - case Type::NOP: + case type_t::NOP: break; } return *this; } #if CXX0X_UP_SUPPORTED -Trigger::Trigger(Trigger &&cpy) : type_(Type::NOP) +Trigger::Trigger(Trigger &&cpy) : type_(cpy.getType()) { operator=(cpy); } @@ -164,7 +165,7 @@ Trigger &Trigger::operator=(Trigger &&cpy) Trigger::~Trigger() { - if (type_ == Type::VARIABLEVALUE) + if (type_ == +type_t::VARIABLEVALUE) { if (inj_->isResolved() && fieldptr_) { @@ -179,7 +180,7 @@ bool Trigger::check(uint64_t time_ps, etiss::fault::Injector *target_injector) std::string(", Injector*)")); switch (type_) { - case Type::META_COUNTER: + case type_t::META_COUNTER: { if (sub_->check(time_ps, target_injector)) { @@ -191,7 +192,7 @@ bool Trigger::check(uint64_t time_ps, etiss::fault::Injector *target_injector) } break; } - case Type::VARIABLEVALUE: + case type_t::VARIABLEVALUE: { if (fieldptr_ == 0) { @@ -231,13 +232,13 @@ bool Trigger::check(uint64_t time_ps, etiss::fault::Injector *target_injector) } return val == param1_; } - case Type::TIMERELATIVE: + case type_t::TIMERELATIVE: { etiss::log(etiss::WARNING, "Trigger::fired: Unresolved TIMERELATIVE - resolving now", *this, *inj_); resolveTime(time_ps); [[fallthrough]]; } - case Type::TIME: + case type_t::TIME: { /* TODO: Why doing it like this ? this would always fire after time_ps has reached */ // Possibly because might be called not exaclty but later than excact trigger time, then late triggering should @@ -259,11 +260,11 @@ bool Trigger::check(uint64_t time_ps, etiss::fault::Injector *target_injector) // return true; break; } - case Type::ASAP: + case type_t::ASAP: { return true; // as soon as possible means on next check } - case Type::NOP: + case type_t::NOP: { return true; } @@ -276,50 +277,50 @@ void Trigger::resolveTime(uint64_t time) { etiss::log(etiss::VERBOSE, std::string("etiss::fault::Trigger::resolveTime(time=") + std::to_string(time) + std::string(")")); - if (type_ == Type::TIMERELATIVE) + if (type_ == +type_t::TIMERELATIVE) { - type_ = Type::TIME; + type_ = type_t::TIME; param1_ = param1_ + time; } - else if (type_ == Type::META_COUNTER) + else if (type_ == +type_t::META_COUNTER) { return sub_->resolveTime(time); } } bool Trigger::isResolved() const { - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->isResolved(); } - return type_ != Type::TIMERELATIVE; + return type_ != +type_t::TIMERELATIVE; } uint64_t Trigger::getTriggerCount() const { - ensure(Type::META_COUNTER); + ensure(type_t::META_COUNTER); return param1_; } const Trigger &Trigger::getSubTrigger() const { - ensure(Type::META_COUNTER); + ensure(type_t::META_COUNTER); return *sub_; } uint64_t Trigger::getTriggerTime() const { - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->getTriggerTime(); } try { - ensure(Type::TIME); + ensure(type_t::TIME); } catch (char const *) { - ensure(Type::TIMERELATIVE); + ensure(type_t::TIMERELATIVE); } return param1_; } @@ -331,7 +332,7 @@ const InjectorAddress &Trigger::getInjectorAddress() const const Injector_ptr &Trigger::getInjector() const { - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->getInjector(); } @@ -343,35 +344,35 @@ const Injector_ptr &Trigger::getInjector() const bool Trigger::isNOP() const { - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->isNOP(); } else { - return type_ == Type::NOP; + return type_ == +type_t::NOP; } } const std::string &Trigger::getTriggerField() const { // std::cout << "Trigger::getTriggerField() called" << std::endl; - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->getTriggerField(); } - ensure(Type::VARIABLEVALUE); + ensure(type_t::VARIABLEVALUE); return field_; } const uint64_t &Trigger::getTriggerFieldValue() const { // std::cout << "Trigger::getTriggerFieldValue() called" << std::endl; - if (type_ == Type::META_COUNTER) + if (type_ == +type_t::META_COUNTER) { return sub_->getTriggerFieldValue(); } - ensure(Type::VARIABLEVALUE); + ensure(type_t::VARIABLEVALUE); return param1_; } @@ -383,18 +384,18 @@ const Trigger::type_t &Trigger::getType() const std::string Trigger::toString() const { std::stringstream ss; - ss << "Trigger { type=" << std::string(type_); + ss << "Trigger { type=" << type_; switch (type_) { - case Type::META_COUNTER: + case type_t::META_COUNTER: ss << " triggerCount=" << +param1_ << " currentCount=" << +param2_; break; - case Type::VARIABLEVALUE: + case type_t::VARIABLEVALUE: ss << " field=" << field_ << " triggerValue=" << +param1_; break; - case Type::TIMERELATIVE: + case type_t::TIMERELATIVE: [[fallthrough]]; - case Type::TIME: + case type_t::TIME: ss << " triggerTime=" << +param1_; break; default: @@ -413,16 +414,22 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::xml::parse") + std::string("(node, Trigger*&, Diagnostics)")); f = 0; - std::string type_str; - if (!parse_attr(node, "type", type_str, diag)) + std::string type_s; + if (!parse_attr(node, "type", type_s, diag)) { return false; } - etiss::fault::Trigger::type_t type(type_str); + + if (!Trigger::type_t::_from_string_nothrow(type_s.c_str())) + { + diag.unexpectedNode(node, std::string("There is no Trigger type ") + type_s); + return false; + } + auto type = Trigger::type_t::_from_string(type_s.c_str()); switch (type) { - case etiss::fault::Trigger::Type::META_COUNTER: + case Trigger::type_t::META_COUNTER: { uint64_t count; if (!parse(findSingleNode(node, "count", diag), count, diag)) @@ -440,7 +447,7 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return true; } - case etiss::fault::Trigger::Type::VARIABLEVALUE: + case Trigger::type_t::VARIABLEVALUE: { uint64_t value; if (!parse_hex(findSingleNode(node, "value", diag), value, diag)) @@ -462,7 +469,7 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return true; } - case etiss::fault::Trigger::Type::TIME: + case Trigger::type_t::TIME: { uint64_t count; if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) @@ -479,7 +486,7 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return true; } - case etiss::fault::Trigger::Type::TIMERELATIVE: // if (type == "TIMERELATIVE") + case Trigger::type_t::TIMERELATIVE: { uint64_t count; if (!parse(findSingleNode(node, "time_ps", diag), count, diag)) @@ -497,7 +504,7 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger * return true; } - case etiss::fault::Trigger::Type::ASAP: + case Trigger::type_t::ASAP: { etiss::fault::InjectorAddress injector; if (!parse(findSingleNode(node, "injector", diag), injector, diag)) @@ -538,23 +545,22 @@ bool parse(pugi::xml_node node, etiss::fault::Trigger &f, return true; } template <> -bool write(pugi::xml_node node, const etiss::fault::Trigger &f, Diagnostics &diag) +bool write(pugi::xml_node node, const Trigger &f, Diagnostics &diag) { etiss::log(etiss::VERBOSE, std::string("Called etiss::fault::xml::write") + std::string("(node, Trigger&, Diagnostics)")); + write_attr(node, "type", f.getType()._to_string(), diag); switch (f.getType()) { - case etiss::fault::Trigger::Type::META_COUNTER: + case Trigger::type_t::META_COUNTER: { - write_attr(node, "type", std::string(f.getType()), diag); write(node.append_child("count"), f.getTriggerCount(), diag); - write(node.append_child("trigger"), f.getSubTrigger(), diag); + write(node.append_child("trigger"), f.getSubTrigger(), diag); } return true; - case etiss::fault::Trigger::Type::VARIABLEVALUE: + case Trigger::type_t::VARIABLEVALUE: { - write_attr(node, "type", std::string(f.getType()), diag); write(node.append_child("field"), f.getTriggerField(), diag); write(node.append_child("count"), f.getTriggerFieldValue(), diag); Injector_ptr ptr = f.getInjector(); @@ -567,11 +573,10 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg write(node.append_child("injector"), ptr->getInjectorPath(), diag); } return true; - case etiss::fault::Trigger::Type::TIME: + case Trigger::type_t::TIME: [[fallthrough]]; - case etiss::fault::Trigger::Type::TIMERELATIVE: + case Trigger::type_t::TIMERELATIVE: { - write_attr(node, "type", std::string(f.getType()), diag); write(node.append_child("time_ps"), f.getTriggerTime(), diag); Injector_ptr ptr = f.getInjector(); if (!ptr) @@ -583,11 +588,10 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg write(node.append_child("injector"), ptr->getInjectorPath(), diag); return true; } - case etiss::fault::Trigger::Type::ASAP: + case Trigger::type_t::ASAP: [[fallthrough]]; - case etiss::fault::Trigger::Type::NOP: + case Trigger::type_t::NOP: { - write_attr(node, "type", std::string(f.getType()), diag); return true; } } @@ -601,14 +605,6 @@ bool write(pugi::xml_node node, const etiss::fault::Trigg } // namespace xml #endif -template <> -Trigger::type_t::map_t Trigger::type_t::TABLE = { { Trigger::Type::META_COUNTER, "META_COUNTER" }, - { Trigger::Type::VARIABLEVALUE, "VARIABLEVALUE" }, - { Trigger::Type::TIME, "TIME" }, - { Trigger::Type::TIMERELATIVE, "TIMERELATIVE" }, - { Trigger::Type::ASAP, "ASAP" }, - { Trigger::Type::NOP, "NOP" } }; - } // namespace fault } // namespace etiss From dbc0914ec4ca1b74cf2ff7124d32d5cba8e3568b Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:50:14 +0100 Subject: [PATCH 38/51] explicit constructor for MemOp ub manipulator base for clang --- .../etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h index 26059d9d24..9b2f488683 100644 --- a/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h +++ b/include/etiss/IntegratedLibrary/fault/MemoryManipulationSystem.h @@ -90,7 +90,7 @@ class MemoryWordManipulatorBase word_t operator()(word_t src1, word_t src2) const; //////////////////////////////////////////////////////////////////////////////////////////// /// \brief Constructor takes string encoded memory operation \ref MemOpType - MemOp(const std::string &memop_str); + MemOp(const std::string &memop_str) : mem_op_t(mem_op_t::_from_string(memop_str.c_str())) {} } /* class MemOp */; //////////////////////////////////////////////////////////////////////////////////////////// From 6c17d12bd96a7084ffadd780de0e6bb7e59f0a2f Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 16 Feb 2023 16:52:12 +0100 Subject: [PATCH 39/51] refactor pugixml integration to avoid dependency in installed etiss --- CMakeLists.txt | 48 ++++++++++++++------- include/etiss/ETISS.h | 1 - include/etiss/fault/Action.h | 7 ---- include/etiss/fault/Fault.h | 16 ------- include/etiss/fault/Injector.h | 2 - include/etiss/fault/InjectorAddress.h | 15 +------ include/etiss/fault/Stressor.h | 7 ---- include/etiss/fault/Trigger.h | 11 ----- include/etiss/fault/XML.h | 60 +++++++++++++++++++++++++++ src/ETISS.cpp | 2 + src/fault/Action.cpp | 2 + src/fault/Fault.cpp | 2 + src/fault/InjectorAddress.cpp | 2 + src/fault/Stressor.cpp | 15 +------ src/fault/Trigger.cpp | 2 + src/fault/XML.cpp | 13 ++++++ 16 files changed, 120 insertions(+), 85 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9301d52a6c..0064cce9d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,27 +244,49 @@ if(NOT simpleini_srcs_POPULATED) FetchContent_Populate(simpleini_srcs) FetchContent_GetProperties(simpleini_srcs) endif() - -set(ETISS_SOURCE ${ETISS_SOURCE} "${simpleini_srcs_SOURCE_DIR}/ConvertUTF.c") +add_library(simpleini + "${simpleini_srcs_SOURCE_DIR}/ConvertUTF.c" +) +set_target_properties(simpleini PROPERTIES + PUBLIC_HEADER "${simpleini_srcs_SOURCE_DIR}/SimpleIni.h;${simpleini_srcs_SOURCE_DIR}/ConvertUTF.h" +) +target_include_directories(simpleini PUBLIC + $ + $ +) +install(TARGETS simpleini + PUBLIC_HEADER DESTINATION include +) FetchContent_Declare(pugixml_srcs - GIT_REPOSITORY https://github.com/zeux/pugixml.git - GIT_TAG v1.11.4 + GIT_REPOSITORY https://github.com/zeux/pugixml.git + GIT_TAG v1.11.4 ) if(NOT pugixml_srcs_POPULATED) - FetchContent_Populate(pugixml_srcs) - FetchContent_GetProperties(pugixml_srcs) + FetchContent_Populate(pugixml_srcs) + FetchContent_GetProperties(pugixml_srcs) endif() add_subdirectory(${pugixml_srcs_SOURCE_DIR} ${pugixml_srcs_BINARY_DIR} EXCLUDE_FROM_ALL) FetchContent_Declare(betterenums_srcs - GIT_REPOSITORY https://github.com/aantron/better-enums.git - GIT_TAG 0.11.3 + GIT_REPOSITORY https://github.com/aantron/better-enums.git + GIT_TAG 0.11.3 ) if(NOT betterenums_srcs_POPULATED) - FetchContent_Populate(betterenums_srcs) - FetchContent_GetProperties(betterenums_srcs) + FetchContent_Populate(betterenums_srcs) + FetchContent_GetProperties(betterenums_srcs) endif() +add_library(betterenums INTERFACE) +set_target_properties(betterenums PROPERTIES + PUBLIC_HEADER "${betterenums_srcs_SOURCE_DIR}/enum.h" +) +target_include_directories(betterenums INTERFACE + $ + $ +) +install(TARGETS betterenums + PUBLIC_HEADER DESTINATION include +) ### Doxyfile set(ETISS_DOX_LOCATIONS ${ETISS_DOX_LOCATIONS} ${PROJECT_BINARY_DIR}/include) @@ -368,7 +390,7 @@ set_target_properties(ETISS PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/ ) IF(UNIX) - TARGET_LINK_LIBRARIES(ETISS PUBLIC ${Boost_LIBRARIES}) + TARGET_LINK_LIBRARIES(ETISS PUBLIC Boost::filesystem Boost::system Boost::program_options) ELSE(UNIX) TARGET_LINK_LIBRARIES(ETISS PUBLIC Boost::boost Threads::Threads) ENDIF() @@ -390,10 +412,8 @@ TARGET_INCLUDE_DIRECTORIES(ETISS PUBLIC "${PROJECT_BINARY_DIR}/include_c" ${INCGEN_INC_DIR} ${elfio_srcs_SOURCE_DIR} - ${simpleini_srcs_SOURCE_DIR} - ${betterenums_srcs_SOURCE_DIR} ) -TARGET_LINK_LIBRARIES(ETISS PUBLIC pugixml) +TARGET_LINK_LIBRARIES(ETISS PUBLIC simpleini pugixml betterenums) GENERATE_EXPORT_HEADER(ETISS BASE_NAME ETISS_PLUGIN diff --git a/include/etiss/ETISS.h b/include/etiss/ETISS.h index 5518bedbc6..3a81d0f465 100644 --- a/include/etiss/ETISS.h +++ b/include/etiss/ETISS.h @@ -65,7 +65,6 @@ #include "etiss/Misc.h" #include "etiss/LibraryInterface.h" -#include "SimpleIni.h" namespace etiss { diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 95c537f67c..2b70f573b2 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -58,11 +58,9 @@ #ifndef NO_ETISS #include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/XML.h" #include "enum.h" #else #include "fault/Defs.h" -#include "fault/XML.h" #include "enum.h" #endif @@ -197,11 +195,6 @@ std::string returncode_tostring(int32_t in); namespace xml { -template <> -bool parse(pugi::xml_node node, etiss::fault::Action &f, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::Action &f, Diagnostics &diag); - } // namespace xml #endif diff --git a/include/etiss/fault/Fault.h b/include/etiss/fault/Fault.h index 26320ff9dd..8b5c260edf 100644 --- a/include/etiss/fault/Fault.h +++ b/include/etiss/fault/Fault.h @@ -63,10 +63,8 @@ #ifndef NO_ETISS #include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/XML.h" #else #include "fault/Defs.h" -#include "fault/XML.h" #endif /// if true then mutex will be used to create unique ids for faults in a threadsafe way @@ -118,23 +116,9 @@ class FaultRef : public etiss::ToString #if ETISS_FAULT_XML -bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); -bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); - -bool writeXML(const std::vector &vec, std::ostream &out, std::ostream &diagnostics_out = std::cout); - namespace xml { -template <> -bool parse(pugi::xml_node node, etiss::fault::Fault &f, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::Fault &f, Diagnostics &diag); -template <> -bool parse(pugi::xml_node node, etiss::fault::FaultRef &fref, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::FaultRef &fref, Diagnostics &diag); - } // namespace xml #endif diff --git a/include/etiss/fault/Injector.h b/include/etiss/fault/Injector.h index 7190feda90..f041a2a087 100644 --- a/include/etiss/fault/Injector.h +++ b/include/etiss/fault/Injector.h @@ -55,11 +55,9 @@ #ifndef NO_ETISS #include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/XML.h" #include "etiss/fault/Misc.h" #else #include "fault/Defs.h" -#include "fault/XML.h" #include "fault/Misc.h" #endif diff --git a/include/etiss/fault/InjectorAddress.h b/include/etiss/fault/InjectorAddress.h index 05136e948b..897ce55d05 100644 --- a/include/etiss/fault/InjectorAddress.h +++ b/include/etiss/fault/InjectorAddress.h @@ -60,10 +60,8 @@ #ifndef NO_ETISS #include "etiss/Misc.h" #include "etiss/fault/Defs.h" -#include "etiss/fault/XML.h" #else #include "fault/Defs.h" -#include "fault/XML.h" #endif namespace etiss @@ -100,18 +98,7 @@ class InjectorAddress : public etiss::ToString namespace xml { -template <> -bool parse(pugi::xml_node node, etiss::fault::InjectorAddress &dst, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::InjectorAddress &src, - Diagnostics &diag); - -template <> -bool parse(pugi::xml_node node, etiss::fault::InjectorAddress *&dst, - Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::InjectorAddress *const &src, - Diagnostics &diag); + } // namespace xml diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index 3abc8afe6b..41ba3d1d98 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -55,9 +55,6 @@ #ifndef NO_ETISS #include "etiss/jit/ReturnCode.h" -#include "etiss/fault/XML.h" -#else -#include "fault/XML.h" #endif #include @@ -151,10 +148,6 @@ class Stressor static std::map &faults(); }; -/** @brief parse a XML document held in \p input stream and return as \p doc - */ -bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out = std::cout); - } // namespace fault } // namespace etiss diff --git a/include/etiss/fault/Trigger.h b/include/etiss/fault/Trigger.h index dadc45adb6..477c6e708d 100644 --- a/include/etiss/fault/Trigger.h +++ b/include/etiss/fault/Trigger.h @@ -61,10 +61,8 @@ #ifndef NO_ETISS #include "etiss/Misc.h" -#include "etiss/fault/XML.h" #include "etiss/fault/Misc.h" #else -#include "fault/XML.h" #include "fault/Misc.h" #endif @@ -175,16 +173,7 @@ class Trigger : public etiss::ToString namespace xml { -template <> -bool parse(pugi::xml_node node, etiss::fault::Trigger *&f, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::Trigger *const &f, - Diagnostics &diag); -template <> -bool parse(pugi::xml_node node, etiss::fault::Trigger &f, Diagnostics &diag); -template <> -bool write(pugi::xml_node node, const etiss::fault::Trigger &f, Diagnostics &diag); } // namespace xml #endif diff --git a/include/etiss/fault/XML.h b/include/etiss/fault/XML.h index 54b02c03e8..491262c79a 100644 --- a/include/etiss/fault/XML.h +++ b/include/etiss/fault/XML.h @@ -54,6 +54,7 @@ #include #include +#include #ifndef NO_ETISS #include "etiss/fault/Defs.h" @@ -71,6 +72,13 @@ namespace etiss namespace fault { +// forwards +class Trigger; +class Fault; +class FaultRef; +class Action; +class InjectorAddress; + // some helper for changing Core Names extern int coreIDActuallXML; void setCoreName(std::string &str); @@ -211,8 +219,60 @@ pugi::xml_node findSingleNode(pugi::xml_node node, const std::string &name, Diag //////////////////////////////////////////// +// TRIGGER +template <> +bool parse(pugi::xml_node node, etiss::fault::Trigger *&f, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::Trigger *const &f, + Diagnostics &diag); + +template <> +bool parse(pugi::xml_node node, etiss::fault::Trigger &f, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::Trigger &f, Diagnostics &diag); + +// FAULT +template <> +bool parse(pugi::xml_node node, etiss::fault::Fault &f, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::Fault &f, Diagnostics &diag); +template <> +bool parse(pugi::xml_node node, etiss::fault::FaultRef &fref, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::FaultRef &fref, Diagnostics &diag); + +// ACTION +template <> +bool parse(pugi::xml_node node, etiss::fault::Action &f, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::Action &f, Diagnostics &diag); + +// INJECTORADDRESS +template <> +bool parse(pugi::xml_node node, etiss::fault::InjectorAddress &dst, Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::InjectorAddress &src, + Diagnostics &diag); + +template <> +bool parse(pugi::xml_node node, etiss::fault::InjectorAddress *&dst, + Diagnostics &diag); +template <> +bool write(pugi::xml_node node, const etiss::fault::InjectorAddress *const &src, + Diagnostics &diag); + } // namespace xml #endif + +/** @brief parse a XML document held in \p input stream and return as \p doc + */ +bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out = std::cout); + +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); +bool parseXML(std::vector &vec, const pugi::xml_document &doc, xml::Diagnostics &diag); + +bool writeXML(const std::vector &vec, std::ostream &out, std::ostream &diagnostics_out = std::cout); + } // namespace fault } // namespace etiss diff --git a/src/ETISS.cpp b/src/ETISS.cpp index 71a8e07254..58620fd8f9 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -47,6 +47,8 @@ #include #include +#include "SimpleIni.h" + #if ETISS_USE_DLSYM #include #endif diff --git a/src/fault/Action.cpp b/src/fault/Action.cpp index 3999a18422..34d5393a5d 100644 --- a/src/fault/Action.cpp +++ b/src/fault/Action.cpp @@ -41,12 +41,14 @@ */ #ifndef NO_ETISS +#include "etiss/fault/XML.h" #include "etiss/fault/Action.h" #include "etiss/fault/Trigger.h" #include "etiss/fault/Fault.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/jit/ReturnCode.h" #else +#include "fault/XML.h" #include "fault/Action.h" #include "fault/Trigger.h" #include "fault/Fault.h" diff --git a/src/fault/Fault.cpp b/src/fault/Fault.cpp index 8efc103e40..0067d8970b 100644 --- a/src/fault/Fault.cpp +++ b/src/fault/Fault.cpp @@ -41,6 +41,7 @@ */ #ifndef NO_ETISS +#include "etiss/fault/XML.h" #include "etiss/fault/Fault.h" #include "etiss/fault/Trigger.h" #include "etiss/fault/Action.h" @@ -49,6 +50,7 @@ #include "etiss/fault/Stressor.h" #include "pugixml.hpp" #else +#include "fault/XML.h" #include "fault/Fault.h" #include "fault/Trigger.h" #include "fault/Action.h" diff --git a/src/fault/InjectorAddress.cpp b/src/fault/InjectorAddress.cpp index 1c65652448..c4d4107645 100644 --- a/src/fault/InjectorAddress.cpp +++ b/src/fault/InjectorAddress.cpp @@ -41,11 +41,13 @@ */ #ifndef NO_ETISS +#include "etiss/fault/XML.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/Injector.h" #include "etiss/fault/Trigger.h" #include "etiss/fault/Action.h" #else +#include "fault/XML.h" #include "fault/InjectorAddress.h" #include "fault/Injector.h" #include "fault/Trigger.h" diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 5e7f19c4da..5fc4912c4a 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -41,6 +41,7 @@ */ #ifndef NO_ETISS +#include "etiss/fault/XML.h" #include "etiss/fault/Stressor.h" #include "etiss/fault/Injector.h" #include "etiss/fault/InjectorAddress.h" @@ -49,6 +50,7 @@ #include "etiss/fault/Fault.h" #include "etiss/Misc.h" #else +#include "fault/XML.h" #include "fault/Stressor.h" #include "fault/Injector.h" #include "fault/InjectorAddress.h" @@ -508,19 +510,6 @@ void Stressor::clear() faults().clear(); } -bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out) -{ - pugi::xml_parse_result pr = doc.load(input); // load from stream - - if (!pr) - { // load failure - diagnostics_out << "failed to load xml from stream: " << pr.description() << std::endl; - return false; - } - - return true; -} - } // namespace fault } // namespace etiss diff --git a/src/fault/Trigger.cpp b/src/fault/Trigger.cpp index 9d968929a1..a095a8239e 100644 --- a/src/fault/Trigger.cpp +++ b/src/fault/Trigger.cpp @@ -41,12 +41,14 @@ */ #ifndef NO_ETISS +#include "etiss/fault/XML.h" #include "etiss/fault/Trigger.h" #include "etiss/fault/Injector.h" #include "etiss/fault/InjectorAddress.h" #include "etiss/fault/Fault.h" #include "etiss/Misc.h" #else +#include "fault/XML.h" #include "fault/Trigger.h" #include "fault/Injector.h" #include "fault/InjectorAddress.h" diff --git a/src/fault/XML.cpp b/src/fault/XML.cpp index 1580511d37..6aaf657ff1 100644 --- a/src/fault/XML.cpp +++ b/src/fault/XML.cpp @@ -290,6 +290,19 @@ pugi::xml_node findSingleNode(pugi::xml_node node, const std::string &name, Diag } // namespace xml #endif +bool parseXML(pugi::xml_document &doc, std::istream &input, std::ostream &diagnostics_out) +{ + pugi::xml_parse_result pr = doc.load(input); // load from stream + + if (!pr) + { // load failure + diagnostics_out << "failed to load xml from stream: " << pr.description() << std::endl; + return false; + } + + return true; +} + } // namespace fault } // namespace etiss From 40851ec12681a80b46f2ee0a01d313b6dadd9fa8 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 16 Feb 2023 16:52:28 +0100 Subject: [PATCH 40/51] force command to upper case for enum resolve --- src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp index f0e396e60f..cdb18970ee 100644 --- a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -54,6 +54,7 @@ #include "etiss/Misc.h" #include "elfio/elfio.hpp" #include +#include using namespace etiss; @@ -171,10 +172,11 @@ void MemoryManipulationSystem::init_manipulation(std::shared_ptr Date: Thu, 23 Feb 2023 11:16:13 +0100 Subject: [PATCH 41/51] fix typo in error message --- src/ETISS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ETISS.cpp b/src/ETISS.cpp index 58620fd8f9..072ab5072d 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -901,7 +901,7 @@ void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) if(!mount_successful) { - etiss::log(etiss::FATALERROR, std::string("Failed to mount ") + cpu_core->getName() + std::string("'s VirtualStruct, but failed: etiss::CPUCore not created!")); + etiss::log(etiss::FATALERROR, std::string("Tried to mount ") + cpu_core->getName() + std::string("'s VirtualStruct, but failed: etiss::CPUCore not created!")); } else { From 0566f5e6ddb99b16dbb5c92cc6c9ab31ca5d5f91 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:08:11 +0100 Subject: [PATCH 42/51] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68c28a1d63..af0cf0b365 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,7 @@ target_include_directories(simpleini PUBLIC ) install(TARGETS simpleini PUBLIC_HEADER DESTINATION include/simpleini - +) FetchContent_Declare(pugixml_srcs GIT_REPOSITORY https://github.com/zeux/pugixml.git GIT_TAG v1.11.4 From 5d0d8ae4d60d7f784f2c270b85072661967ffb11 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Fri, 24 Mar 2023 10:34:31 +0100 Subject: [PATCH 43/51] update elfio --- CMakeLists.txt | 2 +- src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05fc3b1a4e..e18e0ddbea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,7 +414,7 @@ TARGET_INCLUDE_DIRECTORIES(ETISS PUBLIC "${PROJECT_BINARY_DIR}/include_c" ${INCGEN_INC_DIR} ) -TARGET_LINK_LIBRARIES(ETISS PUBLIC simpleini pugixml betterenums) +TARGET_LINK_LIBRARIES(ETISS PUBLIC simpleini elfio pugixml betterenums) GENERATE_EXPORT_HEADER(ETISS BASE_NAME ETISS_PLUGIN diff --git a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp index cdb18970ee..2d335a9df8 100644 --- a/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp +++ b/src/IntegratedLibrary/fault/MemoryManipulationSystem.cpp @@ -57,6 +57,7 @@ #include using namespace etiss; +using namespace ELFIO; std::shared_ptr MemoryManipulationSystem::getStruct(void) { From 4fae67f96d5c96606302e27048a56341718cde67 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:05:05 +0200 Subject: [PATCH 44/51] typos --- include/etiss/fault/Action.h | 2 +- include/etiss/fault/Stressor.h | 2 +- src/fault/Stressor.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/etiss/fault/Action.h b/include/etiss/fault/Action.h index 2b70f573b2..e966ad5409 100644 --- a/include/etiss/fault/Action.h +++ b/include/etiss/fault/Action.h @@ -153,8 +153,8 @@ class Action : public etiss::ToString /// INJECTION and EJECTION only const FaultRef &getFaultRef() const; + /// MASK only const mask_op_t &getMaskOp() const; - uint64_t getMaskValue() const; #ifndef NO_ETISS int32_t getEvent() const; diff --git a/include/etiss/fault/Stressor.h b/include/etiss/fault/Stressor.h index 41ba3d1d98..b0e5a2c8bd 100644 --- a/include/etiss/fault/Stressor.h +++ b/include/etiss/fault/Stressor.h @@ -123,7 +123,7 @@ class Stressor /** @brief removes a fault's active triggers from their injectors, thus, * deactivating the fault. * @param f the fault for adding to the map. - * @return false if refernced fault is not the static fault list faults(). + * @return false if referenced fault is not the static fault list faults(). */ static bool removeFault(const Fault &f, bool injected_fault = false); diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 5fc4912c4a..11fb056e8d 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -330,11 +330,11 @@ bool Stressor::removeFault(const Fault &f, bool injected_fault) if (find == faults().end()) { #ifdef NO_ETISS - std::cout << "etiss::fault::Stressor::addFault: Fault not registered in Fault definitions:" << f.toString() + std::cout << "etiss::fault::Stressor::removeFault: Fault not registered in Fault definitions:" << f.toString() << std::endl; #else etiss::log(etiss::ERROR, - std::string("etiss::fault::Stressor::addFault:") + + std::string("etiss::fault::Stressor::removeFault:") + std::string(" Fault not registered in Fault definitions. "), f); #endif From 861256fdd43b77e18833c628087fb2ae756d486c Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:05:28 +0200 Subject: [PATCH 45/51] add missing CSRs and interrupt vector to legacy RISCV-32bit Arch --- ArchImpl/RISCV/RISCVArchSpecificImp.h | 4 ++++ ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ArchImpl/RISCV/RISCVArchSpecificImp.h b/ArchImpl/RISCV/RISCVArchSpecificImp.h index 00b0c4a23f..7dd516dfd0 100755 --- a/ArchImpl/RISCV/RISCVArchSpecificImp.h +++ b/ArchImpl/RISCV/RISCVArchSpecificImp.h @@ -638,12 +638,16 @@ std::shared_ptr RISCVArch::getVirtualStruct(ETISS_CPU *cpu addCSRCustom(3, &((RISCV *)cpu)->FCSR); ret->addField(new FFLAGSField_RISCV(*ret)); ret->addField(new FRMField_RISCV(*ret)); + addCSR(CSR_MSTATUS); addCSR(CSR_MISA); addCSR(CSR_MIE); addCSR(CSR_MTVEC); + addCSR(CSR_MCOUNTEREN); + addCSR(CSR_MSCRATCH); addCSR(CSR_MEPC); addCSR(CSR_MCAUSE); addCSR(CSR_MTVAL); + addCSR(CSR_MIP); return ret; } diff --git a/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp b/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp index 507e197bca..96533851fb 100644 --- a/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp +++ b/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp @@ -501,10 +501,14 @@ etiss::InterruptVector * RV32IMACFDArch::createInterruptVector(ETISS_CPU * cpu) // This is a default vector, implemented to avoid segfaults. Replace // with actual implementation if necessary. + RV32IMACFD* rvcpu = (RV32IMACFD *)cpu; std::vector vec; std::vector mask; + vec.push_back(rvcpu->CSR[CSR_MIP]); + mask.push_back(rvcpu->CSR[CSR_MIE]); + return new etiss::MappedInterruptVector(vec, mask); } From bd0835c362d1c6d4fae396a62f76b1e1e4d42b55 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:05:52 +0200 Subject: [PATCH 46/51] make VirtualStruct inheritable --- include/etiss/VirtualStruct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etiss/VirtualStruct.h b/include/etiss/VirtualStruct.h index 3907489fd8..0f9dbbad0a 100644 --- a/include/etiss/VirtualStruct.h +++ b/include/etiss/VirtualStruct.h @@ -304,7 +304,7 @@ class VirtualStruct : public std::enable_shared_from_this, public virtual void _write(uint64_t val) { ((structT *)parent_.structure_)->*field = (retT)val; } }; - private: + protected: VirtualStruct( void *structure, std::function dtor = [](Field *f) { delete f; }); From 209745b4337efa77e9abe1c0a941dc321973cfff Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:06:35 +0200 Subject: [PATCH 47/51] allow naming cpu cores other than "core+" --- include/etiss/CPUCore.h | 3 ++- src/CPUCore.cpp | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/etiss/CPUCore.h b/include/etiss/CPUCore.h index 78709d38b4..fe0f887cf7 100644 --- a/include/etiss/CPUCore.h +++ b/include/etiss/CPUCore.h @@ -113,7 +113,7 @@ class CPUCore : public VirtualStructSupport, public etiss::ToString friend class CPUArchRegListenerInterface; friend class InterruptVectorWrapper; - private: + protected: /** * @brief Private constructor of CPUCore. * @@ -123,6 +123,7 @@ class CPUCore : public VirtualStructSupport, public etiss::ToString * * @param arch Pointer to the CPU architecture used by the CPU core simulator. */ + CPUCore(std::shared_ptr arch, std::string const& name); CPUCore(std::shared_ptr arch); class InterruptVectorWrapper : public InterruptVector diff --git a/src/CPUCore.cpp b/src/CPUCore.cpp index 1aad4f0280..5c482b6f27 100644 --- a/src/CPUCore.cpp +++ b/src/CPUCore.cpp @@ -135,9 +135,9 @@ int CPUCore::getNextID() { return currID; } -CPUCore::CPUCore(std::shared_ptr arch) +CPUCore::CPUCore(std::shared_ptr arch, std::string const& name) : arch_(arch) - , name_("core" + std::to_string(currID)) + , name_(name) , id_(currID++) , cpu_(arch->newCPU()) , vcpu_(arch->getVirtualStruct(cpu_)) @@ -185,6 +185,10 @@ CPUCore::CPUCore(std::shared_ptr arch) } } } +CPUCore::CPUCore(std::shared_ptr arch) + : CPUCore(arch, std::string("core" + std::to_string(currID))) +{ +} void CPUCore::addPlugin(std::shared_ptr plugin) { @@ -261,7 +265,11 @@ std::shared_ptr CPUCore::create(std::string archname, std::string insta } // creat core - std::shared_ptr ret(new CPUCore(arch)); + std::shared_ptr ret{nullptr}; + if(instancename != "") + ret.reset(new CPUCore(arch, instancename));//std::make_shared(arch, instancename); + else + ret.reset(new CPUCore(arch)); //std::make_shared(arch); { std::lock_guard lock(instances_mu_); From 31f8610dfa5dc8d544d6f054c585e01138e79106 Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:09:02 +0200 Subject: [PATCH 48/51] check if VirtualStruct is already mounted to root --- src/ETISS.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ETISS.cpp b/src/ETISS.cpp index d1baf8ec28..f44cb74e6e 100644 --- a/src/ETISS.cpp +++ b/src/ETISS.cpp @@ -902,6 +902,11 @@ bool etiss_shutdownOk = false; void etiss::initialize_virtualstruct(std::shared_ptr cpu_core) { auto mount_successful = etiss::VirtualStruct::root()->mountStruct(cpu_core->getName(), cpu_core->getStruct()); + // check if it the core is already mounted. + if( etiss::VirtualStruct::root()->findStruct(cpu_core->getName()) ) + { + mount_successful = true; // already mounted as a substruct to the root() + } if(!mount_successful) { From 92e851ca85b44419e0a5735fc0e7ccd671ad389c Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Wed, 3 May 2023 16:10:30 +0200 Subject: [PATCH 49/51] bugix fault actions NOP and EVENT to correctly return status --- src/fault/Stressor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fault/Stressor.cpp b/src/fault/Stressor.cpp index 11fb056e8d..9bc8c69b2e 100644 --- a/src/fault/Stressor.cpp +++ b/src/fault/Stressor.cpp @@ -381,6 +381,7 @@ bool Stressor::removeFault(const Fault &f, bool injected_fault) std::string(" Trigger is a NOP and is does not need to be removed.")); } } + faults().erase(find); return true; } @@ -418,6 +419,7 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector addFault(iter->getFaultRef().get_fault(), true); break; case +etiss::fault::Action_Type::EJECTION: + etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EJECTION")); if (!iter->getFaultRef().is_set()) { // try to resolve the reference again @@ -435,12 +437,12 @@ bool Stressor::firedTrigger(const Trigger &triggered, int32_t fault_id, Injector case +etiss::fault::Action_Type::NOP: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Discarded - Action is NOP (do not care).")); - return true; + break; #ifndef NO_ETISS case +etiss::fault::Action_Type::EVENT: etiss::log(etiss::VERBOSE, std::string("Stressor::firedTrigger: Action is EVENT")); set_event(iter->getEvent()); - return true; + break; #endif default: // on field actions { From 8fd276aa4a581489a21200dc1f00b00b6df763bd Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:06:08 +0200 Subject: [PATCH 50/51] add unique ID to instruction specific callback plugin, to allow multiple instances insert callback within same codeblock --- .../IntegratedLibrary/InstructionSpecificAddressCallback.h | 2 ++ src/IntegratedLibrary/InstructionSpecificAddressCallback.cpp | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/etiss/IntegratedLibrary/InstructionSpecificAddressCallback.h b/include/etiss/IntegratedLibrary/InstructionSpecificAddressCallback.h index 1391dd8a40..0b533196b4 100644 --- a/include/etiss/IntegratedLibrary/InstructionSpecificAddressCallback.h +++ b/include/etiss/IntegratedLibrary/InstructionSpecificAddressCallback.h @@ -66,6 +66,8 @@ namespace plugin */ class InstructionSpecificAddressCallback : public etiss::TranslationPlugin { + size_t get_uid_once(void) const {static size_t __uid_cntr{0}; return ++__uid_cntr;} + size_t const uid_; public: struct Data { diff --git a/src/IntegratedLibrary/InstructionSpecificAddressCallback.cpp b/src/IntegratedLibrary/InstructionSpecificAddressCallback.cpp index e130b971f8..c32cad206c 100644 --- a/src/IntegratedLibrary/InstructionSpecificAddressCallback.cpp +++ b/src/IntegratedLibrary/InstructionSpecificAddressCallback.cpp @@ -76,6 +76,7 @@ extern "C" using namespace etiss::plugin; InstructionSpecificAddressCallback::InstructionSpecificAddressCallback() + : uid_{get_uid_once()} { pluginData_.state_ = 0; pluginData_.this_ = this; @@ -107,14 +108,14 @@ void InstructionSpecificAddressCallback::finalizeInstrSet(etiss::instr::ModedIns ss.str(""); ss.clear(); - ss << " uint32_t callbackCalled = InstructionSpecificAddressCallback_callback(" + ss << " uint32_t callbackCalled" << uid_ <<" = InstructionSpecificAddressCallback_callback(" << getPointerCode() << ");"; // cs.append(CodePart::APPENDEDREQUIRED).code() = ss.str(); cs.append(CodePart::PREINITIALDEBUGRETURNING).code() = ss.str(); ss.str(""); ss.clear(); - ss << " if(callbackCalled)\n" + ss << " if(callbackCalled" << uid_ <<")\n" << " return ETISS_RETURNCODE_NOERROR;"; // cs.append(CodePart::APPENDEDRETURNINGREQUIRED).code() = ss.str(); cs.append(CodePart::PREINITIALDEBUGRETURNING).code() = ss.str(); From f8bf2d6a66313936f5b30cb04b22f9bdfd08ef4d Mon Sep 17 00:00:00 2001 From: JoGei <62236820+JoGei@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:50:13 +0100 Subject: [PATCH 51/51] revert changes --- ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp b/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp index 96533851fb..507e197bca 100644 --- a/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp +++ b/ArchImpl/RV32IMACFD/RV32IMACFDArchSpecificImp.cpp @@ -501,14 +501,10 @@ etiss::InterruptVector * RV32IMACFDArch::createInterruptVector(ETISS_CPU * cpu) // This is a default vector, implemented to avoid segfaults. Replace // with actual implementation if necessary. - RV32IMACFD* rvcpu = (RV32IMACFD *)cpu; std::vector vec; std::vector mask; - vec.push_back(rvcpu->CSR[CSR_MIP]); - mask.push_back(rvcpu->CSR[CSR_MIE]); - return new etiss::MappedInterruptVector(vec, mask); }