Skip to content
This repository has been archived by the owner on Jan 7, 2019. It is now read-only.

Commit

Permalink
[container] BoundedDeque: Add get() and [] operator.
Browse files Browse the repository at this point in the history
The bracket operator and the get() method can be used to access elements
by id, e.g. to do a binary search on ordered elements in the deque.
This also adds overwriting append() and prepend() methods to use the
BoundedDeque as a ring buffer.
  • Loading branch information
mhthies authored and salkinium committed Apr 3, 2017
1 parent ab0224d commit 8f9b154
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 1 deletion.
68 changes: 67 additions & 1 deletion src/xpcc/container/deque.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,85 @@ namespace xpcc
inline const T&
getFront() const;

/**
* \brief Get item at specified index
*
* Returns a reference to the item at index `n`, counting 0-indexed
* from Front to Back, which is the same order in that the items were
* appended and the iterator addresses them.
*
* \warning Please make sure `n` is a valid index: 0 <= *n* < *size*.
* Other indexes will cause undefined behaviour.
*/
inline T&
get(Index n);

inline const T&
get(Index n) const;

/**
* \brief Get item at specified index
*
* Returns a reference to the item at index `n`, counting 0-indexed
* from Front to Back.
*
* \warning Please make sure `n` is a valid index: 0 <= *n* < *size*.
* Other indexes will cause undefined behaviour.
*/
inline T&
operator[](Index n);

inline const T&
operator[](Index n) const;

/**
* \brief Get item at specified index
*
* Returns a reference to the item at index `n`. The items are indexed
* in reverse (Back to Front), which is their order when they have been
* prepended.
*
* \warning Please make sure `n` is a valid index: 0 <= *n* < *size*.
* Other indexes will cause undefined behaviour.
*/
inline T&
rget(Index n);

inline const T&
rget(Index n) const;

inline T&
getBack();

inline const T&
getBack() const;


bool
append(const T& value);

/**
* \brief Append an item to the back of the deque overwriting existing items
*
* This method, in contrast to `append()`, overwrites existing items in the deque
* if it is full. When an item is appended to the already full deque, the front
* item is removed to use its space for the new item, which is inserted after back.
*/
void
appendOverwrite(const T& value);

bool
prepend(const T& value);

/**
* \brief Prepend an item to the front of the deque overwriting existing items
*
* This method, in contrast to `prepend()`, overwrites existing items in the deque
* if it is full. When an item is prepended to the already full deque, the back
* item is removed to use its space for the new item, which is inserted at front.
*/
void
prependOverwrite(const T& value);

void
removeBack();

Expand Down
120 changes: 120 additions & 0 deletions src/xpcc/container/deque_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,68 @@ xpcc::BoundedDeque<T, N>::getBack() const
return this->buffer[this->head];
}

// ----------------------------------------------------------------------------

template<typename T, std::size_t N>
T&
xpcc::BoundedDeque<T, N>::get(Index n)
{
// From http://stackoverflow.com/a/856839
// as we want to provide a const and non-const getter without having duplicate logic
return const_cast<T&>(static_cast<const BoundedDeque*>(this)->get(n));
}

template<typename T, std::size_t N>
const T&
xpcc::BoundedDeque<T, N>::get(Index n) const
{
if (this->tail + n > N-1) {
return this->buffer[this->tail + n - N];
}
else {
return this->buffer[this->tail + n];
}
}

template<typename T, std::size_t N>
T&
xpcc::BoundedDeque<T, N>::operator[](Index n)
{
return this->get(n);
}

template<typename T, std::size_t N>
const T&
xpcc::BoundedDeque<T, N>::operator[](Index n) const
{
return this->get(n);
}


// ----------------------------------------------------------------------------

template<typename T, std::size_t N>
T&
xpcc::BoundedDeque<T, N>::rget(Index n)
{
// From http://stackoverflow.com/a/856839
// as we want to provide a const and non-const getter without having duplicate logic
return const_cast<T&>(static_cast<const BoundedDeque*>(this)->rget(n));
}

template<typename T, std::size_t N>
const T&
xpcc::BoundedDeque<T, N>::rget(Index n) const
{
if (this->head < n) {
return this->buffer[N - (n - this->head)];
}
else {
return this->buffer[this->head - n];
}
}


// ----------------------------------------------------------------------------

template<typename T, std::size_t N>
Expand All @@ -137,6 +199,35 @@ xpcc::BoundedDeque<T, N>::append(const T& value)
return true;
}

template<typename T, std::size_t N>
void
xpcc::BoundedDeque<T, N>::appendOverwrite(const T& value)
{
if (this->isFull()) {
if (this->tail >= (N - 1)) {
this->tail = 0;
}
else {
this->tail++;
}
}
else {
this->size++;
}

if (this->head >= (N - 1)) {
this->head = 0;
}
else {
this->head++;
}

this->buffer[this->head] = value;
return;
}

// ----------------------------------------------------------------------------

template<typename T, std::size_t N>
void
xpcc::BoundedDeque<T, N>::removeBack()
Expand Down Expand Up @@ -172,6 +263,35 @@ xpcc::BoundedDeque<T, N>::prepend(const T& value)
return true;
}

template<typename T, std::size_t N>
void
xpcc::BoundedDeque<T, N>::prependOverwrite(const T& value)
{
if (this->isFull()) {
if (this->head == 0) {
this->head = N - 1;
}
else {
this->head--;
}
}
else {
this->size++;
}

if (this->tail == 0) {
this->tail = N - 1;
}
else {
this->tail--;
}

this->buffer[this->tail] = value;
return;
}

// ----------------------------------------------------------------------------

template<typename T, std::size_t N>
void
xpcc::BoundedDeque<T, N>::removeFront()
Expand Down
118 changes: 118 additions & 0 deletions src/xpcc/container/test/bounded_deque_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,121 @@ BoundedDequeTest::testOneElementQueue()
deque.removeBack();
TEST_ASSERT_TRUE(deque.isEmpty());
}

void
BoundedDequeTest::testOverwrite()
{
xpcc::BoundedDeque<int16_t, 3> deque;

TEST_ASSERT_TRUE(deque.isEmpty());

deque.appendOverwrite(1);
TEST_ASSERT_EQUALS(deque.getSize(), 1U);
TEST_ASSERT_TRUE(deque.append(2));
TEST_ASSERT_EQUALS(deque.getSize(), 2U);
deque.appendOverwrite(3);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);

TEST_ASSERT_FALSE(deque.append(4));
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getFront(), 1);
TEST_ASSERT_EQUALS(deque.getBack(), 3);
TEST_ASSERT_TRUE(deque.isFull());

deque.appendOverwrite(4);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getFront(), 2);
TEST_ASSERT_EQUALS(deque.getBack(), 4);
TEST_ASSERT_TRUE(deque.isFull());

deque.appendOverwrite(5);
deque.appendOverwrite(6);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getFront(), 4);
TEST_ASSERT_EQUALS(deque.getBack(), 6);
TEST_ASSERT_TRUE(deque.isFull());

deque.removeBack();
deque.removeFront();
TEST_ASSERT_EQUALS(deque.getFront(), 5);
TEST_ASSERT_EQUALS(deque.getBack(), 5);
deque.removeFront();


TEST_ASSERT_TRUE(deque.isEmpty());

deque.prependOverwrite(1);
TEST_ASSERT_EQUALS(deque.getSize(), 1U);
TEST_ASSERT_TRUE(deque.prepend(2));
TEST_ASSERT_EQUALS(deque.getSize(), 2U);
deque.prependOverwrite(3);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);

TEST_ASSERT_FALSE(deque.prepend(4));
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getBack(), 1);
TEST_ASSERT_EQUALS(deque.getFront(), 3);
TEST_ASSERT_TRUE(deque.isFull());

deque.prependOverwrite(4);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getBack(), 2);
TEST_ASSERT_EQUALS(deque.getFront(), 4);
TEST_ASSERT_TRUE(deque.isFull());

deque.prependOverwrite(5);
deque.prependOverwrite(6);
TEST_ASSERT_EQUALS(deque.getSize(), 3U);
TEST_ASSERT_EQUALS(deque.getBack(), 4);
TEST_ASSERT_EQUALS(deque.getFront(), 6);
TEST_ASSERT_TRUE(deque.isFull());
}

void
BoundedDequeTest::testElementAccess()
{
xpcc::BoundedDeque<int16_t, 4> deque;

// Fill deque, but assure a wrap around
deque.append(0);
deque.append(0);
deque.append(1);
deque.append(2);
TEST_ASSERT_TRUE(deque.isFull());
deque.removeFront();
deque.removeFront();
deque.append(3);
deque.append(4);
TEST_ASSERT_TRUE(deque.isFull());

// Test const getter
TEST_ASSERT_EQUALS(deque.get(0), 1);
TEST_ASSERT_EQUALS(deque.get(1), 2);
TEST_ASSERT_EQUALS(deque.get(2), 3);
TEST_ASSERT_EQUALS(deque.get(3), 4);
TEST_ASSERT_EQUALS(deque[0], 1);
TEST_ASSERT_EQUALS(deque[1], 2);
TEST_ASSERT_EQUALS(deque[2], 3);
TEST_ASSERT_EQUALS(deque[3], 4);
TEST_ASSERT_EQUALS(deque.rget(0), 4);
TEST_ASSERT_EQUALS(deque.rget(1), 3);
TEST_ASSERT_EQUALS(deque.rget(2), 2);
TEST_ASSERT_EQUALS(deque.rget(3), 1);

// Test const getter
const auto& constDeque = deque;
TEST_ASSERT_EQUALS(constDeque[1], 2);
TEST_ASSERT_EQUALS(constDeque[3], 4);
TEST_ASSERT_EQUALS(constDeque.get(0), 1);
TEST_ASSERT_EQUALS(constDeque.rget(2), 2);

// Test non-full deque
deque.removeFront();
TEST_ASSERT_EQUALS(deque.get(0), 2);
TEST_ASSERT_EQUALS(deque.get(1), 3);
TEST_ASSERT_EQUALS(deque.get(2), 4);
TEST_ASSERT_EQUALS(deque.rget(0), 4);
TEST_ASSERT_EQUALS(deque.rget(1), 3);
TEST_ASSERT_EQUALS(deque.rget(2), 2);

}
6 changes: 6 additions & 0 deletions src/xpcc/container/test/bounded_deque_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,10 @@ class BoundedDequeTest : public unittest::TestSuite
// Test if queue of length one is handled properly
void
testOneElementQueue();

void
testOverwrite();

void
testElementAccess();
};

0 comments on commit 8f9b154

Please sign in to comment.