vendredi 27 juillet 2018

Linear Hashig ForwardIterator Implementation begin() & end()

I'm implementing an Iterator for a data structure that works based on Linear Hashing Algorithm. As this data structure uses buckets(arrays) and each bucket can further own have its own bucket(also an array) and all of them are structured via table which is dynamically growing.

To implement begin() method i simply set the iterator to the very first element in the table. The code is:

const_iterator begin() const {

    if(this->empty()) { return end(); } //if container is empty return end iterator
    //else return the iterator to first taken element 
    //try only passing to the first element regardless if its full or not cause it shouldnt make a difference
    bucket* next{table[0]};
    element* ptr{next->Bucket};

    return const_iterator{ptr, table};
}

To implement end() method my idea was to create an additional array at the bottom of the table which will not be used during inserts or anything in the structure, its sole purpose is to act as end. First element of that array has a value (state) 'end' and once this is reached the end is set to the position behind the last element of that array. Code is:

const_iterator end() const { //should point to position behind the last element?
    //find the last taken element in the table
    bucket* next{table[tableSize]};
    element* ptr{next->Bucket}; //cause that one is marked with end and will trigger returning the end

    return const_iterator{ptr, table};
}

The iterator class, responsible for handling these requests is:

template <typename Key, size_t N>
class ADS_set<Key, N>::Iterator {
private:
    element* elem{nullptr}; //pointer to element for which the iterator is called
    bucket** tab{nullptr}; //pointer to table

    //find the bucket in which the element for which the constructor was called is in
    bucket* current_bucket{nullptr};
    size_type current_idx{0};
    bool endFound{false};

    void find_current_bucket() {
        bucket* next{tab[current_idx]};

        while(next != nullptr) {
            for(unsigned i=0; i<N; ++i) { //loop through the bucket and its overflows
                if(&(next->Bucket[i]) == elem) {
                    current_bucket = tab[current_idx];
                    return;
                }
            }   

        if(next->overflowBucket != nullptr) {
            next = next->overflowBucket;
        } else {
            ++current_idx;
            if(tab[current_idx]->Bucket[0].state == State::end) {
                endFound = true;
                elem = &(tab[current_idx]->Bucket[N]);
                return;
            }
            next = tab[current_idx];
        }
    }

}

void skip() {
    //set the current bucket
    find_current_bucket();

    if(endFound == true) { return; }

    bucket* next{current_bucket};

    while(next != nullptr) {
        for(unsigned i=0; i<N; ++i) {
            if(&(next->Bucket[i]) == elem) {
                if(elem->state == State::free) {
                    ++elem;
                } else {
                    return; //you found the element
                    //and its not a free place so nothing to skip--> return
                }
            } //goes through each element of bucket
        } //needs to go through overflows as well

        if(next->overflowBucket != nullptr) {
            next = next->overflowBucket;
        } else { //if there is no more overflows and element still not found it has to move to the next index in the table
            ++current_idx;
            if(tab[current_idx]->Bucket[0].state == State::end) {
                elem = &(tab[current_idx]->Bucket[N]);
                //++elem;
                return; //if end return
            }
            next = tab[current_idx];
          }
       }

   }

public:
    using value_type = Key;
    using difference_type = std::ptrdiff_t;
    using reference = const value_type&;
    using pointer = const value_type*;
    using iterator_category = std::forward_iterator_tag;

    explicit Iterator(element* elem = nullptr, bucket** tab = nullptr): elem{elem}, tab{tab} {
        if(elem && tab) {
            skip();
        }
    }

    reference operator*() const { 
        return elem->key;
    }

    pointer operator->() const {
        return &(elem->key);
    }

    Iterator& operator++() {
        ++elem;
        skip();
        return *this;
    }

    Iterator operator++(int) { //increments the pointer but returns the old value
        Iterator copy = *this;
        ++(*this);
        return copy;
    }

    friend bool operator==(const Iterator &lhs, const Iterator &rhs) { return lhs.elem == rhs.elem; }
    friend bool operator!=(const Iterator &lhs, const Iterator &rhs) { return lhs.elem != rhs.elem; }

};

When I test my program with Valgrind and using a specified test program I get an error when following code is executed (from the test prg):

void test_iter(ads::set<val_t> const& a, std::set<val_t> const& r) {
    std::cerr << "\n=== test_iter ===\n";

    size_t dist = std::distance(a.begin(), a.end());
    **if(dist != r.size()) {
        std::cerr << RED("[iter] err: range size (distance between begin and end) is wrong.\n"
              << "expected: " << r.size() << ", but got: " << dist << "and begin is: " << *(a.begin()) << '\n');
       dump_compare(a, r);
       std::abort();
    }**

std::vector<val_t> dump; dump.reserve(a.size());
for(auto const& v: a) {
    if(!r.count(v)) {
        std::cerr << RED("[iter] err: encountered unexpected value while iterating: " << v << " should not be part of container!\n");
        dump_compare(a, r);
        std::abort();
    }
    dump.push_back(v);
}

std::vector<val_t> dump_copy = dump;

std::sort(dump.begin(), dump.end(), std::less<val_t>{});
auto last = std::unique(dump.begin(), dump.end(), std::equal_to<val_t>{});
dump.erase(last, dump.end());

if(dump.size() != r.size()) {
    std::cerr << RED("[iter] err: had duplicate encounters while iterating. found following elements (in this order):\n\n");

    for(auto const& v: dump_copy) { std::cerr << v << ' '; }
    std::cerr << '\n';

    dump_compare(a, r);
    std::abort();
}

ads::set<val_t>::iterator i;
for(i = a.begin(); i != a.end(); ++i) {
    if(!r.count(*i)) {
        std::cerr << RED("[iter] err: encountered unexpected value while iterating (default iterator constructor): "
                  << *i << " should not be part of container!\n");

        dump_compare(a, r);
        std::abort();
    }
}

}

More specifically the error occurs when the bolded part of the code is executed and the errror message is as follows (image is below):

Error message, distance between begin and end is wrong-> expected dist is 9 and the one I got is 2

I truly don't understand why this is happening as I see no logical mistake in my code or the approach to it, so I would appreciate any suggestions on correction of the error or on the implementation of these two methods. Thank You!

Aucun commentaire:

Enregistrer un commentaire