diff options
-rw-r--r-- | src/pugixml.cpp | 327 | ||||
-rw-r--r-- | src/pugixml.hpp | 62 |
2 files changed, 373 insertions, 16 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 7ec18ae..dbf5f2f 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -4603,28 +4603,28 @@ namespace { using namespace pugi; - class xpath_string + char_t* duplicate_string(const char_t* string, size_t length) { - const char_t* _buffer; - bool _uses_heap; + char_t* result = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t))); + if (!result) return 0; // $$ out of memory - static char_t* duplicate_string(const char_t* string, size_t length) - { - char_t* result = static_cast<char_t*>(get_memory_allocation_function()((length + 1) * sizeof(char_t))); - if (!result) return 0; // $$ out of memory - - memcpy(result, string, length * sizeof(char_t)); - result[length] = 0; + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; - return result; - } + return result; + } - static char_t* duplicate_string(const char_t* string) - { - return duplicate_string(string, strlength(string)); - } + char_t* duplicate_string(const char_t* string) + { + return duplicate_string(string, strlength(string)); + } + class xpath_string + { + const char_t* _buffer; + bool _uses_heap; public: + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false) { } @@ -5331,6 +5331,119 @@ namespace // zero-terminate *write = 0; } + + struct xpath_variable_boolean: xpath_variable + { + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_node_set value; + char_t name[1]; + }; + + const xpath_node_set dummy_node_set; + + 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<unsigned int>(*str++); + result += result << 10; + result ^= result >> 6; + } + + result += result << 3; + result ^= result >> 11; + result += result << 15; + + return result; + } + + template <typename T> T* new_xpath_variable(const char_t* name) + { + size_t length = strlength(name); + + // we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters + void* memory = get_memory_allocation_function()(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; + } + + xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) + { + switch (type) + { + case xpath_type_node_set: + return new_xpath_variable<xpath_variable_node_set>(name); + + case xpath_type_number: + return new_xpath_variable<xpath_variable_number>(name); + + case xpath_type_string: + return new_xpath_variable<xpath_variable_string>(name); + + case xpath_type_boolean: + return new_xpath_variable<xpath_variable_boolean>(name); + + default: + assert(false); + return 0; + } + } + + template <typename T> void delete_xpath_variable(xpath_variable* var) + { + static_cast<T*>(var)->~T(); + get_memory_deallocation_function()(var); + } + + void delete_xpath_variable(xpath_value_type type, xpath_variable* var) + { + switch (type) + { + case xpath_type_node_set: + delete_xpath_variable<xpath_variable_node_set>(var); + break; + + case xpath_type_number: + delete_xpath_variable<xpath_variable_number>(var); + break; + + case xpath_type_string: + delete_xpath_variable<xpath_variable_string>(var); + break; + + case xpath_type_boolean: + delete_xpath_variable<xpath_variable_boolean>(var); + break; + + default: + assert(false); + } + } } namespace pugi @@ -8105,6 +8218,188 @@ namespace pugi return error ? error : "No error"; } + const char_t* xpath_variable::name() const + { + switch (_type) + { + case xpath_type_node_set: + return static_cast<const xpath_variable_node_set*>(this)->name; + + case xpath_type_number: + return static_cast<const xpath_variable_number*>(this)->name; + + case xpath_type_string: + return static_cast<const xpath_variable_string*>(this)->name; + + case xpath_type_boolean: + return static_cast<const xpath_variable_boolean*>(this)->name; + + default: + assert(false); + return 0; + } + } + + xpath_value_type xpath_variable::type() const + { + return _type; + } + + bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast<const xpath_variable_boolean*>(this)->value : false; + } + + double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast<const xpath_variable_number*>(this)->value : gen_nan(); + } + + const char_t* xpath_variable::get_string() const + { + const char_t* value = (_type == xpath_type_string) ? static_cast<const xpath_variable_string*>(this)->value : 0; + return value ? value : PUGIXML_TEXT(""); + } + + const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast<const xpath_variable_node_set*>(this)->value : dummy_node_set; + } + + bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast<xpath_variable_boolean*>(this)->value = value; + return true; + } + + bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast<xpath_variable_number*>(this)->value = value; + return true; + } + + bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + xpath_variable_string* var = static_cast<xpath_variable_string*>(this); + + // duplicate string + char_t* copy = duplicate_string(value); + if (!copy) return false; + + // replace old string + if (var->value) get_memory_deallocation_function()(var->value); + var->value = copy; + + return true; + } + + bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast<xpath_variable_node_set*>(this)->value = value; + return true; + } + + xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; + } + + 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; + + delete_xpath_variable(var->_type, var); + + var = next; + } + } + } + + xpath_variable* xpath_variable_set::find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var; + + return 0; + } + + 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 = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = new_xpath_variable(type, name); + + if (result) + { + result->_type = type; + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + xpath_variable* xpath_variable_set::get(const char_t* name) + { + return find(name); + } + + const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return find(name); + } + xpath_query::xpath_query(const char_t* query): _alloc(0), _root(0) { _result.error = 0; diff --git a/src/pugixml.hpp b/src/pugixml.hpp index b31e251..7e09551 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1805,6 +1805,68 @@ namespace pugi }; /** + * A class that holds XPath variable + */ + class xpath_variable + { + friend class xpath_variable_set; + + protected: + // Non-copyable semantics + xpath_variable(const xpath_variable&); + xpath_variable& operator=(const xpath_variable&); + + xpath_value_type _type; + xpath_variable* _next; + + xpath_variable() {} + ~xpath_variable() {} + + public: + const char_t* name() const; + xpath_value_type type() const; + + bool get_boolean() const; + double get_number() const; + const char_t* get_string() const; + const xpath_node_set& get_node_set() const; + + bool set(bool value); + bool set(double value); + bool set(const char_t* value); + bool set(const xpath_node_set& value); + }; + + /** + * A class that holds XPath variables + */ + class xpath_variable_set + { + private: + // Non-copyable semantics + xpath_variable_set(const xpath_variable_set&); + xpath_variable_set& operator=(const xpath_variable_set&); + + xpath_variable* _data[64]; + + xpath_variable* find(const char_t* name) const; + + public: + xpath_variable_set(); + ~xpath_variable_set(); + + xpath_variable* add(const char_t* name, xpath_value_type type); + + 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); + + xpath_variable* get(const char_t* name); + const xpath_variable* get(const char_t* name) const; + }; + + /** * A class that holds compiled XPath query and allows to evaluate query result */ class PUGIXML_CLASS xpath_query |