diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-20 16:20:45 +0100 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-20 16:20:45 +0100 |
commit | b1f91277a29ddac92479106543ecfd62ff99d152 (patch) | |
tree | 55aab26342b34c5d9e661f42077c480d444ef345 | |
parent | 6d42a8f99cbe98201a0d52ab276f6929b66cfe4f (diff) |
added hashiterator, this is going to be one of the implementation classes for the upcoming value iterator
-rw-r--r-- | src/hashiterator.cpp | 168 | ||||
-rw-r--r-- | src/hashiterator.h | 121 | ||||
-rw-r--r-- | src/includes.h | 2 | ||||
-rw-r--r-- | src/iteratorimpl.h | 52 |
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> ¤t() 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> ¤t() const = 0; +}; + +/** + * End namespace + */ +} + |