mirror of https://github.com/pvnis/srsRAN_4G.git
implementation of bounded_vector and respective unit test
parent
22229adf08
commit
fc451f17f0
@ -0,0 +1,225 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_BOUNDED_VECTOR_H
|
||||||
|
#define SRSLTE_BOUNDED_VECTOR_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace srslte {
|
||||||
|
|
||||||
|
template <typename T, std::size_t MAX_N>
|
||||||
|
class bounded_vector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator = T*;
|
||||||
|
using const_iterator = const T*;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
|
||||||
|
bounded_vector() = default;
|
||||||
|
template <typename std::enable_if<std::is_default_constructible<T>::value, int>::type = 0>
|
||||||
|
bounded_vector(size_type N)
|
||||||
|
{
|
||||||
|
append(N, T());
|
||||||
|
}
|
||||||
|
template <typename U, typename std::enable_if<std::is_constructible<T, U>::value, int>::type = 0>
|
||||||
|
bounded_vector(size_type N, const U& init_val)
|
||||||
|
{
|
||||||
|
append(N, T(init_val));
|
||||||
|
}
|
||||||
|
bounded_vector(const bounded_vector& other) { append(other.begin(), other.end()); }
|
||||||
|
bounded_vector(bounded_vector&& other) noexcept
|
||||||
|
{
|
||||||
|
for (size_type i = 0; i < other.size(); ++i) {
|
||||||
|
new (&buffer[i]) T(std::move(other[i]));
|
||||||
|
}
|
||||||
|
size_ = other.size();
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
bounded_vector(std::initializer_list<T> init) { append(init.begin(), init.end()); }
|
||||||
|
bounded_vector(const_iterator it_begin, const_iterator it_end) { append(it_begin, it_end); }
|
||||||
|
~bounded_vector() { destroy(begin(), end()); }
|
||||||
|
bounded_vector& operator=(const bounded_vector& other)
|
||||||
|
{
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
assign(other.begin(), other.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bounded_vector& operator=(bounded_vector&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
size_t min_common_size = std::min(other.size(), size());
|
||||||
|
if (min_common_size > 0) {
|
||||||
|
// move already constructed elements
|
||||||
|
auto it = std::move(other.begin(), other.begin() + min_common_size, begin());
|
||||||
|
destroy(it, end());
|
||||||
|
size_ = min_common_size;
|
||||||
|
}
|
||||||
|
// append the rest
|
||||||
|
for (size_t i = size_; i < other.size(); ++i) {
|
||||||
|
new (&buffer[i]) T(std::move(other[i]));
|
||||||
|
}
|
||||||
|
size_ = other.size();
|
||||||
|
other.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign(size_type nof_elems, const T& value)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
append(nof_elems, value);
|
||||||
|
}
|
||||||
|
void assign(const_iterator it_start, const_iterator it_end)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
append(it_start, it_end);
|
||||||
|
}
|
||||||
|
void assign(std::initializer_list<T> ilist) { assign(ilist.begin(), ilist.end()); }
|
||||||
|
|
||||||
|
// Element access
|
||||||
|
T& operator[](std::size_t i)
|
||||||
|
{
|
||||||
|
assert(i < size_);
|
||||||
|
return reinterpret_cast<T&>(buffer[i]);
|
||||||
|
}
|
||||||
|
const T& operator[](std::size_t i) const
|
||||||
|
{
|
||||||
|
assert(i < size_);
|
||||||
|
return reinterpret_cast<const T&>(buffer[i]);
|
||||||
|
}
|
||||||
|
T& back() { return (*this)[size_ - 1]; }
|
||||||
|
const T& back() const { return (*this)[size_ - 1]; }
|
||||||
|
T& front() { return (*this)[0]; }
|
||||||
|
const T& front() const { return (*this)[0]; }
|
||||||
|
T* data() { return &buffer[0]; }
|
||||||
|
const T* data() const { return &buffer[0]; }
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
iterator begin() { return reinterpret_cast<iterator>(&buffer[0]); }
|
||||||
|
iterator end() { return reinterpret_cast<iterator>(&buffer[size_]); }
|
||||||
|
const_iterator begin() const { return reinterpret_cast<const_iterator>(&buffer[0]); }
|
||||||
|
const_iterator end() const { return reinterpret_cast<const_iterator>(&buffer[size_]); }
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
bool empty() const { return size_ == 0; }
|
||||||
|
std::size_t size() const { return size_; }
|
||||||
|
std::size_t capacity() const { return MAX_N; }
|
||||||
|
|
||||||
|
// modifiers
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
destroy(begin(), end());
|
||||||
|
size_ = 0;
|
||||||
|
}
|
||||||
|
iterator erase(iterator pos)
|
||||||
|
{
|
||||||
|
assert(pos >= this->begin() && "Iterator to erase is out of bounds.");
|
||||||
|
assert(pos < this->end() && "Erasing at past-the-end iterator.");
|
||||||
|
iterator ret = pos;
|
||||||
|
std::move(pos + 1, end(), pos);
|
||||||
|
pop_back();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
iterator erase(iterator it_start, iterator it_end)
|
||||||
|
{
|
||||||
|
assert(it_start >= begin() && "Range to erase is out of bounds.");
|
||||||
|
assert(it_start <= it_end && "Trying to erase invalid range.");
|
||||||
|
assert(it_end <= end() && "Trying to erase past the end.");
|
||||||
|
|
||||||
|
iterator ret = it_start;
|
||||||
|
// Shift all elts down.
|
||||||
|
iterator new_end = std::move(it_end, end(), it_start);
|
||||||
|
destroy(new_end, end());
|
||||||
|
size_ = new_end - begin();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void push_back(const T& value)
|
||||||
|
{
|
||||||
|
size_++;
|
||||||
|
assert(size_ <= MAX_N);
|
||||||
|
new (&back()) T(value);
|
||||||
|
}
|
||||||
|
void push_back(T&& value)
|
||||||
|
{
|
||||||
|
size_++;
|
||||||
|
assert(size_ <= MAX_N);
|
||||||
|
new (&back()) T(std::move(value));
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
typename std::enable_if<std::is_constructible<T, Args...>::value>::type emplace_back(Args&&... args)
|
||||||
|
{
|
||||||
|
size_++;
|
||||||
|
assert(size_ <= MAX_N);
|
||||||
|
new (&back()) T(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
assert(size_ > 0);
|
||||||
|
back().~T();
|
||||||
|
size_--;
|
||||||
|
}
|
||||||
|
typename std::enable_if<std::is_default_constructible<T>::value>::type resize(size_type count) { resize(count, T()); }
|
||||||
|
void resize(size_type count, const T& value)
|
||||||
|
{
|
||||||
|
if (size_ > count) {
|
||||||
|
destroy(begin() + count, end());
|
||||||
|
size_ = count;
|
||||||
|
} else if (size_ < count) {
|
||||||
|
append(count - size_, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const bounded_vector& other) const
|
||||||
|
{
|
||||||
|
return other.size() == size() and std::equal(begin(), end(), other.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void destroy(iterator it_start, iterator it_end)
|
||||||
|
{
|
||||||
|
for (auto it = it_start; it != it_end; ++it) {
|
||||||
|
it->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void construct_(iterator it_start, iterator it_end, const T& value)
|
||||||
|
{
|
||||||
|
for (auto it = it_start; it != it_end; ++it) {
|
||||||
|
new (it) T(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void append(const_iterator it_begin, const_iterator it_end)
|
||||||
|
{
|
||||||
|
size_type N = std::distance(it_begin, it_end);
|
||||||
|
assert(N + size_ <= MAX_N);
|
||||||
|
std::uninitialized_copy(it_begin, it_end, end());
|
||||||
|
size_ += N;
|
||||||
|
}
|
||||||
|
void append(size_type N, const T& element)
|
||||||
|
{
|
||||||
|
assert(N + size_ <= MAX_N);
|
||||||
|
std::uninitialized_fill_n(end(), N, element);
|
||||||
|
size_ += N;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size_ = 0;
|
||||||
|
typename std::aligned_storage<sizeof(T), alignof(T)>::type buffer[MAX_N];
|
||||||
|
};
|
||||||
|
} // namespace srslte
|
||||||
|
|
||||||
|
#endif // SRSLTE_BOUNDED_VECTOR_H
|
@ -0,0 +1,161 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srslte/adt/bounded_vector.h"
|
||||||
|
#include "srslte/common/test_common.h"
|
||||||
|
|
||||||
|
namespace srslte {
|
||||||
|
|
||||||
|
struct C {
|
||||||
|
static int nof_copy_ctor;
|
||||||
|
static int nof_value_ctor;
|
||||||
|
static int nof_move_ctor;
|
||||||
|
static int nof_dtor;
|
||||||
|
|
||||||
|
C(int val_ = 0) : val(val_) { nof_value_ctor++; }
|
||||||
|
C(const C& v) : val(v.val) { nof_copy_ctor++; }
|
||||||
|
C(C&& v) : val(v.val)
|
||||||
|
{
|
||||||
|
v.val = 0;
|
||||||
|
nof_move_ctor++;
|
||||||
|
}
|
||||||
|
~C() { nof_dtor++; }
|
||||||
|
C& operator=(const C&) = default;
|
||||||
|
C& operator =(C&& other)
|
||||||
|
{
|
||||||
|
val = other.val;
|
||||||
|
other.val = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(const C& other) const { return val == other.val; }
|
||||||
|
bool operator!=(const C& other) const { return not(*this == other); }
|
||||||
|
|
||||||
|
int val = 0;
|
||||||
|
};
|
||||||
|
int C::nof_copy_ctor = 0;
|
||||||
|
int C::nof_value_ctor = 0;
|
||||||
|
int C::nof_move_ctor = 0;
|
||||||
|
int C::nof_dtor = 0;
|
||||||
|
|
||||||
|
int test_ctor()
|
||||||
|
{
|
||||||
|
// TEST: default ctor
|
||||||
|
bounded_vector<int, 10> a;
|
||||||
|
TESTASSERT(a.size() == 0);
|
||||||
|
TESTASSERT(a.capacity() == 10);
|
||||||
|
TESTASSERT(a.empty());
|
||||||
|
|
||||||
|
// TEST: copy ctor
|
||||||
|
a.push_back(1);
|
||||||
|
bounded_vector<int, 10> a2(a);
|
||||||
|
TESTASSERT(a2.size() == a.size());
|
||||||
|
TESTASSERT(std::equal(a.begin(), a.end(), a2.begin()));
|
||||||
|
|
||||||
|
// TEST: size ctor
|
||||||
|
bounded_vector<int, 5> a3(2);
|
||||||
|
TESTASSERT(a3.size() == 2);
|
||||||
|
|
||||||
|
// TEST: size+value ctor
|
||||||
|
bounded_vector<int, 15> a4(10, 5);
|
||||||
|
TESTASSERT(a4.size() == 10);
|
||||||
|
for (auto& v : a4) {
|
||||||
|
TESTASSERT(v == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST: initializer_list ctor
|
||||||
|
bounded_vector<int, 20> a5({0, 2, 4, 6, 8, 10, 12});
|
||||||
|
TESTASSERT(a5.size() == 7);
|
||||||
|
for (size_t i = 0; i < a5.size(); ++i) {
|
||||||
|
TESTASSERT(a5[i] == (int)i * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST: move ctor
|
||||||
|
bounded_vector<int, 20> a6(std::move(a5));
|
||||||
|
TESTASSERT(a6.size() == 7);
|
||||||
|
TESTASSERT(a5.size() == 0);
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_obj_add_rem()
|
||||||
|
{
|
||||||
|
// TEST: push_back / emplace_back
|
||||||
|
bounded_vector<C, 10> a;
|
||||||
|
TESTASSERT(a.size() == 0);
|
||||||
|
TESTASSERT(a.empty());
|
||||||
|
a.push_back(1);
|
||||||
|
a.emplace_back(2);
|
||||||
|
TESTASSERT(a.size() == 2);
|
||||||
|
TESTASSERT(not a.empty());
|
||||||
|
|
||||||
|
// TEST: resize with size growth
|
||||||
|
a.resize(10, 3);
|
||||||
|
TESTASSERT(a.size() == 10);
|
||||||
|
TESTASSERT(a[0] == 1);
|
||||||
|
TESTASSERT(a[1] == 2);
|
||||||
|
TESTASSERT(a[2] == 3 and a.back() == 3);
|
||||||
|
|
||||||
|
// TEST: copy ctor correct insertion
|
||||||
|
bounded_vector<C, 10> a2(a);
|
||||||
|
TESTASSERT(a2.size() == a.size());
|
||||||
|
TESTASSERT(std::equal(a.begin(), a.end(), a2.begin()));
|
||||||
|
|
||||||
|
// TEST: back() access
|
||||||
|
a.back() = 4;
|
||||||
|
TESTASSERT(not std::equal(a.begin(), a.end(), a2.begin()));
|
||||||
|
a2 = a;
|
||||||
|
TESTASSERT(std::equal(a.begin(), a.end(), a2.begin()));
|
||||||
|
|
||||||
|
// TEST: assign
|
||||||
|
a.resize(5);
|
||||||
|
a2.assign(a.begin(), a.end());
|
||||||
|
TESTASSERT(a2.size() == 5);
|
||||||
|
TESTASSERT(std::equal(a.begin(), a.end(), a2.begin()));
|
||||||
|
|
||||||
|
// TEST: pop_back
|
||||||
|
int last_nof_dtor = C::nof_dtor;
|
||||||
|
a.pop_back();
|
||||||
|
TESTASSERT(a.size() == 4 and last_nof_dtor == C::nof_dtor - 1);
|
||||||
|
|
||||||
|
// TEST: erase
|
||||||
|
a.erase(a.begin() + 1);
|
||||||
|
TESTASSERT(std::equal(a.begin(), a.end(), std::initializer_list<C>{1, 3, 3}.begin()));
|
||||||
|
|
||||||
|
// TEST: clear
|
||||||
|
last_nof_dtor = C::nof_dtor;
|
||||||
|
a.clear();
|
||||||
|
TESTASSERT(a.size() == 0 and a.empty() and last_nof_dtor == C::nof_dtor - 3);
|
||||||
|
|
||||||
|
// TEST: move assignment
|
||||||
|
TESTASSERT(a2.size() == 5);
|
||||||
|
a = std::move(a2);
|
||||||
|
TESTASSERT(a.size() == 5 and a2.empty());
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int assert_dtor_consistency()
|
||||||
|
{
|
||||||
|
TESTASSERT(C::nof_dtor == C::nof_copy_ctor + C::nof_value_ctor + C::nof_move_ctor);
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srslte
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TESTASSERT(srslte::test_ctor() == SRSLTE_SUCCESS);
|
||||||
|
TESTASSERT(srslte::test_obj_add_rem() == SRSLTE_SUCCESS);
|
||||||
|
TESTASSERT(srslte::assert_dtor_consistency() == SRSLTE_SUCCESS);
|
||||||
|
printf("Success\n");
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue