summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-20 16:20:45 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-20 16:20:45 +0100
commitb1f91277a29ddac92479106543ecfd62ff99d152 (patch)
tree55aab26342b34c5d9e661f42077c480d444ef345
parent6d42a8f99cbe98201a0d52ab276f6929b66cfe4f (diff)
added hashiterator, this is going to be one of the implementation classes for the upcoming value iterator
-rw-r--r--src/hashiterator.cpp168
-rw-r--r--src/hashiterator.h121
-rw-r--r--src/includes.h2
-rw-r--r--src/iteratorimpl.h52
4 files changed, 343 insertions, 0 deletions
diff --git a/src/hashiterator.cpp b/src/hashiterator.cpp
new file mode 100644
index 0000000..43a9d27
--- /dev/null
+++ b/src/hashiterator.cpp
@@ -0,0 +1,168 @@
+/**
+ * HashIterator.cpp
+ *
+ * Implementation of the value iterator
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Constructor
+ * @param hashtable The hashtable to iterate over
+ * @param first Should it start at the first position?
+ */
+HashIterator::HashIterator(HashTable *hashtable, bool first) : _table(hashtable)
+{
+ // reset the hash pointer to the internal position
+ if (hashtable && first)
+ {
+ // move to first position
+ zend_hash_internal_pointer_reset_ex(_table, &_position);
+
+ // read current data
+ if (read()) return;
+
+ // data was private, move on
+ increment();
+ }
+ else
+ {
+ // start with invalid data
+ invalidate();
+ }
+}
+
+/**
+ * Increment position
+ * @return bool
+ */
+bool HashIterator::increment()
+{
+ // leap out if already on an invalid pos (behind the last pos)
+ if (!_position) return false;
+
+ // move the iterator forward
+ if (zend_hash_move_forward_ex(_table, &_position) == SUCCESS)
+ {
+ // read current key and value
+ if (read()) return true;
+
+ // data was private or invalid, move further
+ return increment();
+ }
+ else
+ {
+ // invalidate current position
+ return invalidate();
+ }
+}
+
+/**
+ * Decrement position
+ * @return bool
+ */
+bool HashIterator::decrement()
+{
+ // leap out if we're not even iterating over a hash table
+ if (!_table) return false;
+
+ // if position is invalid, it is one position behind the last position
+ if (!_position)
+ {
+ // move to last position
+ zend_hash_internal_pointer_end_ex(_table, &_position);
+ }
+ else if (zend_hash_move_backwards_ex(_table, &_position) == FAILURE)
+ {
+ // invalidate current position
+ return invalidate();
+ }
+
+ // read current key and value
+ if (read()) return true;
+
+ // data was private, move on
+ return decrement();
+}
+
+/**
+ * Read current key and value
+ * @return bool true if the object is in a valid position, false otherwise
+ */
+bool HashIterator::read()
+{
+ // zval to read the current key in
+ Value key;
+
+#if PHP_VERSION_ID >= 50500
+
+ // read in the current key
+ zend_hash_get_current_key_zval_ex(_table, key._val, &_position);
+
+ // if the key is set to NULL, it means that the object is not at a valid position
+ if (key.isNull()) return invalidate();
+
+#else
+
+ // php 5.3 and php 5.4 need a different implementation because the function
+ // zend_hash_get_current_key_zval_ex is missing in php 5.3, declare variables
+ // we need for storing the key in
+ char *string_key;
+ unsigned int str_len;
+ unsigned long num_key;
+
+ // get the current key
+ int type = zend_hash_get_current_key_ex(_table, &string_key, &str_len, &num_key, 0, &_position);
+
+ // if key is not found, the iterator is at an invalid position
+ if (type == HASH_KEY_NON_EXISTANT) return invalidate();
+
+ // numeric keys are the easiest ones
+ if (type == HASH_KEY_IS_LONG) key = (int64_t)num_key;
+ else key = string_key;
+
+#endif
+
+ // iterator is at a valid position, go fetch the data
+ // this is the variable we need for fetching the data
+ zval **value;
+
+ // retrieve data
+ zend_hash_get_current_data_ex(_table, (void **) &value, &_position);
+
+ // we can now update the current data
+ _current = std::make_pair<Value,Value>(std::move(key), *value);
+
+ // if the key is private (it starts with a null character) we should return
+ // false to report that the object is not in a completely valid state
+ return !_current.first.isString() || _current.first.rawValue()[0];
+}
+
+/**
+ * Invalidate the iterator
+ * @return bool always false
+ */
+bool HashIterator::invalidate()
+{
+ // forget current position
+ _position = nullptr;
+
+ // make the data a pair of null ptrs
+ _current = std::make_pair<Value,Value>(nullptr,nullptr);
+
+ // done
+ return false;
+}
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/hashiterator.h b/src/hashiterator.h
new file mode 100644
index 0000000..c37c22b
--- /dev/null
+++ b/src/hashiterator.h
@@ -0,0 +1,121 @@
+/**
+ * HashIterator.h
+ *
+ * This is an internal helper class that is used when iterating over a
+ * Php::Value object that holds a hash table (an array or an object that
+ * does not implement the Traversable interface - stl style.
+ *
+ * Thus, when you do c++ things like "for (auto &iter : value)", internally
+ * a ValueIterator object is being used.
+ *
+ * @author Emiel Bruijntjes
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Forward declaration
+ */
+struct _hashtable;
+struct bucket;
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class HashIterator : public IteratorImpl
+{
+public:
+ /**
+ * Constructor
+ * @param hashtable The hashtable to iterate over
+ * @param first Should it start on the first position?
+ */
+ HashIterator(struct _hashtable *hashtable, bool first);
+
+ /**
+ * Copy constructor
+ * @param that
+ */
+ HashIterator(const ValueIterator &that);
+
+ /**
+ * Destructor
+ */
+ virtual ~HashIterator() {}
+
+ /**
+ * Increment position (pre-increment)
+ * @return bool
+ */
+ virtual bool increment() override;
+
+ /**
+ * Decrement position (pre-decrement)
+ * @return bool
+ */
+ virtual bool decrement() override;
+
+ /**
+ * Compare with other iterator
+ * @param that
+ * @return bool
+ */
+ virtual bool equals(const IteratorImpl *that) const override
+ {
+ // this always is a hash iterator
+ HashIterator *other = (HashIterator *)that;
+
+ // compare the positions
+ return _position == other->_position;
+ }
+
+ /**
+ * Derefecence, this returns a std::pair with the current key and value
+ * @return std::pair
+ */
+ virtual const std::pair<Value,Value> &current() const override
+ {
+ return _current;
+ }
+
+private:
+ /**
+ * The hash table over which is being iterated
+ * @var HashTable
+ */
+ struct _hashtable *_table = nullptr;
+
+ /**
+ * The position in the hash table
+ * @var HashPosition
+ */
+ struct bucket *_position = nullptr;
+
+ /**
+ * The current key and value
+ * @var std::pair
+ */
+ std::pair<Value,Value> _current;
+
+ /**
+ * Read current key and value
+ * @return bool
+ */
+ bool read();
+
+ /**
+ * Invalidate the iterator
+ * @return bool
+ */
+ bool invalidate();
+};
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/includes.h b/src/includes.h
index 42b52ce..e59d67b 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -92,6 +92,8 @@
#include "origexception.h"
#include "notimplemented.h"
#include "property.h"
+#include "iteratorimpl.h"
+#include "hashiterator.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/src/iteratorimpl.h b/src/iteratorimpl.h
new file mode 100644
index 0000000..97c59e4
--- /dev/null
+++ b/src/iteratorimpl.h
@@ -0,0 +1,52 @@
+/**
+ * IteratorImpl.h
+ *
+ * Interface that describes what an implementation of an iterator should
+ * look like. This is an internal class that extension developers do not
+ * need.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class IteratorImpl
+{
+ /**
+ * Increment position (pre-increment)
+ * @return bool
+ */
+ virtual bool increment() = 0;
+
+ /**
+ * Decrement position (pre-decrement)
+ * @return bool
+ */
+ virtual bool decrement() = 0;
+
+ /**
+ * Compare with other iterator
+ * @param that
+ * @return bool
+ */
+ virtual bool equals(const IteratorImpl *that) const = 0;
+
+ /**
+ * Derefecence, this returns a std::pair with the current key and value
+ * @return std::pair
+ */
+ virtual const std::pair<Value,Value> &current() const = 0;
+};
+
+/**
+ * End namespace
+ */
+}
+