Commit 573a695c by Maarten L. Hekkelman

small steps

parent a76bef0d
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#pragma once #pragma once
#include <charconv>
#include <cmath>
#include <filesystem> #include <filesystem>
#include <set> #include <set>
#include <vector> #include <vector>
...@@ -205,6 +207,129 @@ inline auto coloured(std::basic_string<CharT, Traits, Alloc> &s, StringColour fo ...@@ -205,6 +207,129 @@ inline auto coloured(std::basic_string<CharT, Traits, Alloc> &s, StringColour fo
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/// std::from_chars for floating point types.
template <typename FloatType>
std::from_chars_result from_chars(const char *first, const char *last, FloatType &value)
{
std::from_chars_result result{first, {}};
enum State {
IntegerSign,
Integer,
Fraction,
ExponentSign,
Exponent
} state = IntegerSign;
int sign = 1;
unsigned long long vi = 0;
long double f = 1;
int exponent_sign = 1;
int exponent = 0;
bool done = false;
while (not done and result.ec == std::errc())
{
char ch = result.ptr != last ? *result.ptr : 0;
++result.ptr;
switch (state)
{
case IntegerSign:
if (ch == '-')
{
sign = -1;
state = Integer;
}
else if (ch == '+')
state = Integer;
else if (ch >= '0' and ch <= '9')
{
vi = ch - '0';
state = Integer;
}
else if (ch == '.')
state = Fraction;
else
result.ec = std::errc::invalid_argument;
break;
case Integer:
if (ch >= '0' and ch <= '9')
vi = 10 * vi + (ch - '0');
else if (ch == 'e' or ch == 'E')
state = ExponentSign;
else if (ch == '.')
state = Fraction;
else
{
done = true;
--result.ptr;
}
break;
case Fraction:
if (ch >= '0' and ch <= '9')
{
vi = 10 * vi + (ch - '0');
f /= 10;
}
else if (ch == 'e' or ch == 'E')
state = ExponentSign;
else
{
done = true;
--result.ptr;
}
break;
case ExponentSign:
if (ch == '-')
{
exponent_sign = -1;
state = Exponent;
}
else if (ch == '+')
state = Exponent;
else if (ch >= '0' and ch <= '9')
{
exponent = ch - '0';
state = Exponent;
}
else
result.ec = std::errc::invalid_argument;
break;
case Exponent:
if (ch >= '0' and ch <= '9')
exponent = 10 * exponent + (ch - '0');
else
{
done = true;
--result.ptr;
}
break;
}
}
if (result.ec == std::errc())
{
long double v = f * vi * sign;
if (exponent != 0)
v *= std::pow(10, exponent * exponent_sign);
if (std::isnan(v))
result.ec = std::errc::invalid_argument;
else if (std::abs(v) > std::numeric_limits<FloatType>::max())
result.ec = std::errc::result_out_of_range;
value = static_cast<FloatType>(v);
}
return result;
}
// --------------------------------------------------------------------
// A progress bar // A progress bar
class Progress class Progress
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#pragma once #pragma once
#include "iterator.hpp"
#include "row.hpp" #include "row.hpp"
#include "validate.hpp" #include "validate.hpp"
...@@ -46,8 +47,17 @@ class category_t ...@@ -46,8 +47,17 @@ class category_t
return m_allocator; return m_allocator;
} }
struct iterator {}; template <typename>
friend class row_handle;
template <typename, typename...>
friend class iterator_impl;
using value_type = row_handle<category_t>;
using reference = value_type;
using const_reference = const value_type;
using iterator = iterator_impl<category_t>;
using const_iterator = iterator_impl<const category_t>;
category_t() = default; category_t() = default;
...@@ -101,6 +111,56 @@ class category_t ...@@ -101,6 +111,56 @@ class category_t
const std::string &name() const { return m_name; } const std::string &name() const { return m_name; }
reference front()
{
return {*this, *m_head};
}
const_reference front() const
{
return {*this, *m_head};
}
reference back()
{
return {*this, *m_tail};
}
const_reference back() const
{
return {*this, *m_tail};
}
iterator begin()
{
return {*this, m_head};
}
iterator end()
{
return {*this, nullptr};
}
const_iterator begin() const
{
return {*this, m_head};
}
const_iterator end() const
{
return {*this, nullptr};
}
const_iterator cbegin() const
{
return {*this, m_head};
}
const_iterator cend() const
{
return {*this, nullptr};
}
iterator emplace(std::initializer_list<item> items) iterator emplace(std::initializer_list<item> items)
{ {
return this->emplace(items.begin(), items.end()); return this->emplace(items.begin(), items.end());
...@@ -157,49 +217,33 @@ class category_t ...@@ -157,49 +217,33 @@ class category_t
// } // }
// } // }
auto r = create_row(); row *r = this->create_row();
if (m_head == nullptr)
m_head = m_tail = r;
else
m_tail = m_tail->m_next = r;
for (auto i = b; i != e; ++i) try
{ {
std::unique_ptr<item_value> new_item(this->create_item(*i)); for (auto i = b; i != e; ++i)
{
item_value *new_item = this->create_item(*i);
if (r->m_head == nullptr) if (r->m_head == nullptr)
r->m_head = r->m_tail = new_item.release(); r->m_head = r->m_tail = new_item;
else else
r->m_tail = r->m_tail->m_next = new_item.release(); r->m_tail = r->m_tail->m_next = new_item;
}
}
catch (...)
{
if (r != nullptr)
this->delete_row(r);
throw;
} }
if (m_head == nullptr)
m_head = m_tail = r;
else
m_tail = m_tail->m_next = r;
return {}; return { *this, r };
// return iterator{ create_row() };
// Row r(nr);
// for (auto v = b; v != e; ++v)
// r.assign(*v, true);
// // if (isOrphan(r))
// // throw std::runtime_error("Cannot insert row in category " + mName + " since it would be an orphan");
// if (mHead == nullptr)
// {
// assert(mTail == nullptr);
// mHead = mTail = nr;
// }
// else
// {
// assert(mTail != nullptr);
// assert(mHead != nullptr);
// mTail->mNext = nr;
// mTail = nr;
// }
// result = r; // result = r;
...@@ -207,11 +251,6 @@ class category_t ...@@ -207,11 +251,6 @@ class category_t
// mIndex->insert(nr); // mIndex->insert(nr);
} }
// void emplace(std::initializer_list<item> items)
// {
// this->emplace_back(value_type{items, this->get_allocator()});
// }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/// \brief Return the index number for \a column_name /// \brief Return the index number for \a column_name
...@@ -258,9 +297,7 @@ class category_t ...@@ -258,9 +297,7 @@ class category_t
return result; return result;
} }
private: private:
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Internal storage, strictly forward linked list with minimal space // Internal storage, strictly forward linked list with minimal space
// requirements. Strings of size 7 or shorter are stored internally. // requirements. Strings of size 7 or shorter are stored internally.
...@@ -271,6 +308,7 @@ class category_t ...@@ -271,6 +308,7 @@ class category_t
{ {
item_value(uint16_t column_ix, uint16_t length) item_value(uint16_t column_ix, uint16_t length)
: m_next(nullptr) : m_next(nullptr)
, m_column_ix(column_ix)
, m_length(length) , m_length(length)
{ {
} }
...@@ -292,9 +330,9 @@ class category_t ...@@ -292,9 +330,9 @@ class category_t
std::string_view text() const std::string_view text() const
{ {
return { m_length >= kBufferSize ? m_data : m_local_data, m_length }; return {m_length >= kBufferSize ? m_data : m_local_data, m_length};
} }
const char *c_str() const const char *c_str() const
{ {
return m_length >= kBufferSize ? m_data : m_local_data; return m_length >= kBufferSize ? m_data : m_local_data;
...@@ -342,7 +380,7 @@ class category_t ...@@ -342,7 +380,7 @@ class category_t
item_value *create_item(const item &i) item_value *create_item(const item &i)
{ {
uint16_t ix = get_column_ix(i.name()); uint16_t ix = add_column(i.name());
return create_item(ix, i.value()); return create_item(ix, i.value());
} }
...@@ -422,20 +460,18 @@ class category_t ...@@ -422,20 +460,18 @@ class category_t
row_allocator_traits::deallocate(ra, r, 1); row_allocator_traits::deallocate(ra, r, 1);
} }
struct item_column struct item_column
{ {
std::string m_name; std::string m_name;
ValidateItem *m_validator; const ValidateItem *m_validator;
item_column(std::string_view name, ValidateItem *validator) item_column(std::string_view name, const ValidateItem *validator)
: m_name(name) : m_name(name)
, m_validator(validator) , m_validator(validator)
{ {
} }
}; };
allocator_type m_allocator; allocator_type m_allocator;
std::string m_name; std::string m_name;
std::vector<item_column, typename std::allocator_traits<allocator_type>::template rebind_alloc<item_column>> m_columns; std::vector<item_column, typename std::allocator_traits<allocator_type>::template rebind_alloc<item_column>> m_columns;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "row.hpp"
namespace cif::v2
{
// --------------------------------------------------------------------
template <typename CategoryType, typename... Ts>
class iterator_impl
{
public:
template <typename, typename...>
friend class iterator_impl;
static constexpr size_t N = sizeof...(Ts);
using category_type = CategoryType;
using row_type = typename category_type::row;
// using row_type = std::conditional_t<std::is_const_v<category_type>, const category_type::row, category_type::row>;
// using row_impl_type = std::conditional_t<std::is_const_v<category_type>, typename const category_type::row, typename category_type::row>;
using iterator_category = std::forward_iterator_tag;
using value_type = std::conditional_t<N == 0, row_handle<category_type>, std::tuple<Ts...>>;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = std::conditional_t<N == 0, row_handle<category_type>, value_type>;
friend class Category;
// // default constructor, equal to end()
// iterator_impl() {}
iterator_impl(const iterator_impl &rhs)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_value(rhs.m_value)
, m_column_ix(rhs.m_column_ix)
{
}
iterator_impl(category_type &cat, row_type *current)
: m_category(&cat)
, m_current(current)
, m_value(cat, *current)
{
static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator");
}
// iterator_impl(ItemRow *data)
// : m_current(data)
// {
// static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator");
// }
// iterator_impl(ItemRow *data, const std::array<size_t, N> &cix)
// : m_current(data)
// , m_column_ix(cix)
// {
// }
template <typename IRowType>
iterator_impl(iterator_impl<IRowType, Ts...> &rhs)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(rhs.m_column_ix)
{
if constexpr (N > 0)
m_value = get(m_current, std::make_index_sequence<N>());
}
template <typename IRowType>
iterator_impl(const iterator_impl<IRowType> &rhs, const std::array<size_t, N> &cix)
: m_category(rhs.m_category)
, m_current(rhs.m_current)
, m_column_ix(cix)
{
if constexpr (N > 0)
m_value = get(m_current, std::make_index_sequence<N>());
}
iterator_impl &operator=(const iterator_impl &i)
{
m_category = i.m_category;
m_current = i.m_current;
if constexpr (N != 0)
{
m_column_ix = i.m_column_ix;
m_value = i.m_value;
}
return *this;
}
virtual ~iterator_impl() = default;
reference operator*()
{
if constexpr (N == 0)
return { *m_category, *m_current };
else
return m_value;
}
pointer operator->()
{
if constexpr (N == 0)
return &m_current;
else
return &m_value;
}
row_type row() const
{
return m_current;
}
iterator_impl &operator++()
{
if (m_current != nullptr)
m_current = m_current->m_next;
if constexpr (N != 0)
m_value = get(m_current, std::make_index_sequence<N>());
return *this;
}
iterator_impl operator++(int)
{
iterator_impl result(*this);
this->operator++();
return result;
}
bool operator==(const iterator_impl &rhs) const { return m_current == rhs.m_current; }
bool operator!=(const iterator_impl &rhs) const { return m_current != rhs.m_current; }
template <typename IRowType, typename... ITs>
bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current == rhs.m_current;
}
template <typename IRowType, typename... ITs>
bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const
{
return m_current != rhs.m_current;
}
private:
template <std::size_t... Is>
std::tuple<Ts...> get(row_type row, std::index_sequence<Is...>) const
{
if (row)
return std::tuple<Ts...>{row[m_column_ix[Is]].template as<Ts>()...};
return {};
}
category_type *m_category;
row_type *m_current;
value_type m_value;
std::array<size_t, N> m_column_ix;
};
} // namespace cif::v2
\ No newline at end of file
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <memory>
namespace cif::v2
{
// --------------------------------------------------------------------
template<typename Allocator = std::allocator<void>>
class list
{
public:
protected:
struct list_item
{
list_item *m_next = nullptr;
};
using list_item_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<list_item>;
using list_item_allocator_traits = std::allocator_traits<item_allocator_type>;
list_item_allocator_traits::pointer get_item()
{
list_item_allocator_type ia(get_allocator());
return list_item_allocator_traits::allocate(ia, 1);
}
template<typename ...Arguments>
list_item *create_list_item(uint16_t column_ix, Arguments... args)
{
auto p = this->get_item();
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::construct(ia, p, std::forward<Arguments>(args)...);
return p;
}
void delete_list_item(list_item *iv)
{
list_item_allocator_type ia(get_allocator());
list_item_allocator_traits::destroy(ia, iv);
list_item_allocator_traits::deallocate(ia, iv, 1);
}
list_item *m_head = nullptr, *m_tail = nullptr;
};
} // namespace cif::v2
...@@ -40,29 +40,31 @@ class row_handle ...@@ -40,29 +40,31 @@ class row_handle
public: public:
using category_type = Category; using category_type = Category;
using row_type = category_type::row;
row_handle(Category &cat) row_handle(category_type &cat, row_type &row)
: m_cat(cat) {} : m_cat(cat)
, m_row(row) {}
// item_handle<row_t> operator[](uint32_t column_ix) item_handle<row_type> operator[](uint32_t column_ix)
// { {
// return item_handle<row_t>(column_ix, *this); return item_handle<row_type>(column_ix, m_row);
// } }
// const item_handle<const row_t> operator[](uint32_t column_ix) const const item_handle<const row_type> operator[](uint32_t column_ix) const
// { {
// return item_handle<const row_t>(column_ix, *this); return item_handle<const row_type>(column_ix, m_row);
// } }
// item_handle<row_t> operator[](std::string_view column_name) item_handle<row_type> operator[](std::string_view column_name)
// { {
// return item_handle<row_t>(column_name, get_column_ix(column_name), *this); return item_handle<row_type>(get_column_ix(column_name), m_row);
// } }
// const item_handle<const row_t> operator[](std::string_view column_name) const const item_handle<const row_type> operator[](std::string_view column_name) const
// { {
// return item_handle<const row_t>(column_name, get_column_ix(column_name), *this); return item_handle<const row_type>(get_column_ix(column_name), m_row);
// } }
...@@ -76,6 +78,7 @@ class row_handle ...@@ -76,6 +78,7 @@ class row_handle
category_type &m_cat; category_type &m_cat;
row_type &m_row;
}; };
......
...@@ -76,6 +76,38 @@ bool init_unit_test() ...@@ -76,6 +76,38 @@ bool init_unit_test()
// -------------------------------------------------------------------- // --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(cc_1)
{
std::tuple<std::string_view,float,char> tests[] = {
{ "1.0", 1.0, 0 },
{ "1.0e10", 1.0e10, 0 },
{ "-1.1e10", -1.1e10, 0 },
{ "-.2e11", -.2e11, 0 },
{ "1.3e-10", 1.3e-10, 0 },
{ "1.0 ", 1.0, ' ' },
{ "1.0e10 ", 1.0e10, ' ' },
{ "-1.1e10 ", -1.1e10, ' ' },
{ "-.2e11 ", -.2e11, ' ' },
{ "1.3e-10 ", 1.3e-10, ' ' },
};
for (const auto &[txt, val, ch] : tests)
{
float tv;
const auto &[ptr, ec] = cif::from_chars(txt.begin(), txt.end(), tv);
BOOST_CHECK(ec == std::errc());
BOOST_CHECK_EQUAL(tv, val);
if (ch != 0)
BOOST_CHECK_EQUAL(*ptr, ch);
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(r_1) BOOST_AUTO_TEST_CASE(r_1)
{ {
cif::v2::category c("foo"); cif::v2::category c("foo");
...@@ -86,9 +118,9 @@ BOOST_AUTO_TEST_CASE(r_1) ...@@ -86,9 +118,9 @@ BOOST_AUTO_TEST_CASE(r_1)
}); });
auto row = c.front(); auto row = c.front();
BOOST_CHECK_EQUAL(row["f-1"].compare(1), 0;) BOOST_CHECK_EQUAL(row["f-1"].compare(1), 0);
BOOST_CHECK_EQUAL(row["f-2"].compare(1), "two";) BOOST_CHECK_EQUAL(row["f-2"].compare("two"), 0);
BOOST_CHECK_EQUAL(row["f-3"].compare(1), 3.0;) BOOST_CHECK_EQUAL(row["f-3"].compare(3.0), 0);
} }
...@@ -106,6 +138,26 @@ BOOST_AUTO_TEST_CASE(r_2) ...@@ -106,6 +138,26 @@ BOOST_AUTO_TEST_CASE(r_2)
} }
BOOST_AUTO_TEST_CASE(c_1)
{
cif::v2::category c("foo");
c.emplace({ { "id", 1 }, { "s", "aap" } });
c.emplace({ { "id", 2 }, { "s", "noot" } });
c.emplace({ { "id", 3 }, { "s", "mies" } });
int n = 1;
const char *ts[] = { "aap", "noot", "mies" };
for (auto r : c)
{
BOOST_CHECK_EQUAL(r["id"].as<int>(), n);
BOOST_CHECK_EQUAL(r["s"].compare(ts[n - 1]), 0);
++n;
}
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// BOOST_AUTO_TEST_CASE(f_1) // BOOST_AUTO_TEST_CASE(f_1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment