summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/plib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/plib')
-rw-r--r--src/lib/netlist/plib/gmres.h499
-rw-r--r--src/lib/netlist/plib/mat_cr.h529
-rw-r--r--src/lib/netlist/plib/palloc.h729
-rw-r--r--src/lib/netlist/plib/parray.h185
-rw-r--r--src/lib/netlist/plib/pchrono.cpp51
-rw-r--r--src/lib/netlist/plib/pchrono.h447
-rw-r--r--src/lib/netlist/plib/pconfig.h242
-rw-r--r--src/lib/netlist/plib/pdynlib.cpp118
-rw-r--r--src/lib/netlist/plib/pdynlib.h155
-rw-r--r--src/lib/netlist/plib/penum.h55
-rw-r--r--src/lib/netlist/plib/pexception.cpp89
-rw-r--r--src/lib/netlist/plib/pexception.h74
-rw-r--r--src/lib/netlist/plib/pfmtlog.cpp49
-rw-r--r--src/lib/netlist/plib/pfmtlog.h693
-rw-r--r--src/lib/netlist/plib/pfunction.cpp602
-rw-r--r--src/lib/netlist/plib/pfunction.h217
-rw-r--r--src/lib/netlist/plib/pgsl.h181
-rw-r--r--src/lib/netlist/plib/plists.h480
-rw-r--r--src/lib/netlist/plib/pmain.cpp79
-rw-r--r--src/lib/netlist/plib/pmain.h49
-rw-r--r--src/lib/netlist/plib/pmath.h422
-rw-r--r--src/lib/netlist/plib/pmatrix2d.h227
-rw-r--r--src/lib/netlist/plib/pmatrix_cr.h648
-rw-r--r--src/lib/netlist/plib/pmempool.h283
-rw-r--r--src/lib/netlist/plib/pmulti_threading.h89
-rw-r--r--src/lib/netlist/plib/pomp.h65
-rw-r--r--src/lib/netlist/plib/poptions.cpp99
-rw-r--r--src/lib/netlist/plib/poptions.h398
-rw-r--r--src/lib/netlist/plib/pparser.cpp531
-rw-r--r--src/lib/netlist/plib/pparser.h279
-rw-r--r--src/lib/netlist/plib/ppmf.cpp109
-rw-r--r--src/lib/netlist/plib/ppmf.h721
-rw-r--r--src/lib/netlist/plib/ppreprocessor.cpp766
-rw-r--r--src/lib/netlist/plib/ppreprocessor.h127
-rw-r--r--src/lib/netlist/plib/prandom.h233
-rw-r--r--src/lib/netlist/plib/psource.h173
-rw-r--r--src/lib/netlist/plib/pstate.cpp57
-rw-r--r--src/lib/netlist/plib/pstate.h173
-rw-r--r--src/lib/netlist/plib/pstonum.h145
-rw-r--r--src/lib/netlist/plib/pstream.cpp55
-rw-r--r--src/lib/netlist/plib/pstream.h322
-rw-r--r--src/lib/netlist/plib/pstring.cpp120
-rw-r--r--src/lib/netlist/plib/pstring.h595
-rw-r--r--src/lib/netlist/plib/pstrutil.h358
-rw-r--r--src/lib/netlist/plib/ptests.h313
-rw-r--r--src/lib/netlist/plib/ptime.h186
-rw-r--r--src/lib/netlist/plib/ptimed_queue.h357
-rw-r--r--src/lib/netlist/plib/ptokenizer.cpp371
-rw-r--r--src/lib/netlist/plib/ptokenizer.h277
-rw-r--r--src/lib/netlist/plib/ptypes.h451
-rw-r--r--src/lib/netlist/plib/putil.cpp153
-rw-r--r--src/lib/netlist/plib/putil.h353
-rw-r--r--src/lib/netlist/plib/vector_ops.h97
53 files changed, 10022 insertions, 5054 deletions
diff --git a/src/lib/netlist/plib/gmres.h b/src/lib/netlist/plib/gmres.h
index 2c357e97624..6ce96ed2ae6 100644
--- a/src/lib/netlist/plib/gmres.h
+++ b/src/lib/netlist/plib/gmres.h
@@ -1,36 +1,50 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * gmres.h
- *
- */
#ifndef PLIB_GMRES_H_
#define PLIB_GMRES_H_
-#include "mat_cr.h"
+// Names
+// spell-checker: words Burkardt, Saad, Yousef, Givens
+//
+
+///
+/// \file gmres.h
+///
+
#include "parray.h"
#include "pconfig.h"
+#include "pmatrix_cr.h"
#include "vector_ops.h"
#include <algorithm>
-#include <cmath>
-
namespace plib
{
- template <typename FT, int SIZE>
+ template <int k>
+ struct do_k_helper
+ {
+ static constexpr bool value = true;
+ };
+
+ template <>
+ struct do_k_helper<-1>
+ {
+ static constexpr float value = 0.0;
+ };
+
+ template <typename ARENA, typename FT, int SIZE>
struct mat_precondition_ILU
{
- using mat_type = plib::matrix_compressed_rows_t<FT, SIZE>;
-
- mat_precondition_ILU(std::size_t size, int ilu_scale = 4
- , std::size_t bw = plib::matrix_compressed_rows_t<FT, SIZE>::FILL_INFINITY)
- : m_mat(static_cast<typename mat_type::index_type>(size))
- , m_LU(static_cast<typename mat_type::index_type>(size))
- , m_use_iLU_preconditioning(ilu_scale >= 0)
- , m_ILU_scale(static_cast<std::size_t>(ilu_scale))
+ using mat_type = plib::pmatrix_cr<ARENA, FT, SIZE>;
+ using matLU_type = plib::pLUmatrix_cr<mat_type>;
+
+ mat_precondition_ILU(ARENA &arena, std::size_t size, std::size_t ilu_scale = 4
+ , std::size_t bw = plib::pmatrix_cr<ARENA, FT, SIZE>::FILL_INFINITY)
+ : m_mat(arena, narrow_cast<typename mat_type::index_type>(size))
+ , m_LU(arena, narrow_cast<typename mat_type::index_type>(size))
+ , m_ILU_scale(narrow_cast<std::size_t>(ilu_scale))
, m_band_width(bw)
{
}
@@ -39,16 +53,11 @@ namespace plib
void build(M &fill)
{
m_mat.build_from_fill_mat(fill, 0);
- if (m_use_iLU_preconditioning)
- {
- m_LU.gaussian_extend_fill_mat(fill);
- m_LU.build_from_fill_mat(fill, m_ILU_scale, m_band_width); // ILU(2)
- //m_LU.build_from_fill_mat(fill, 9999, 20); // Band matrix width 20
- }
+ m_LU.build(fill, m_ILU_scale);
}
- template<typename R, typename V>
+ template <typename R, typename V>
void calc_rhs(R &rhs, const V &v)
{
m_mat.mult_vec(rhs, v);
@@ -56,41 +65,30 @@ namespace plib
void precondition()
{
- if (m_use_iLU_preconditioning)
- {
- if (m_ILU_scale < 1)
- m_LU.raw_copy_from(m_mat);
- else
- m_LU.reduction_copy_from(m_mat);
- m_LU.incomplete_LU_factorization();
- }
+ m_LU.incomplete_LU_factorization(m_mat);
}
- template<typename V>
- void solve_LU_inplace(V &v)
+ template <typename V>
+ void solve_inplace(V &v)
{
- if (m_use_iLU_preconditioning)
- {
- m_LU.solveLUx(v);
- }
+ m_LU.solveLU(v);
}
PALIGNAS_VECTOROPT()
mat_type m_mat;
PALIGNAS_VECTOROPT()
- mat_type m_LU;
- bool m_use_iLU_preconditioning;
+ matLU_type m_LU;
std::size_t m_ILU_scale;
std::size_t m_band_width;
};
- template <typename FT, int SIZE>
- struct mat_precondition_diag
+ template <typename ARENA, typename FT, int SIZE>
+ struct mat_precondition_diagonal
{
- mat_precondition_diag(std::size_t size)
- : m_mat(size)
- , m_diag(size)
- , m_use_iLU_preconditioning(true)
+ mat_precondition_diagonal(ARENA &arena, std::size_t size, [[maybe_unused]] int dummy = 0)
+ : m_mat(arena, size)
+ , m_diagonal(size)
+ , nz_col(size)
{
}
@@ -98,9 +96,21 @@ namespace plib
void build(M &fill)
{
m_mat.build_from_fill_mat(fill, 0);
+ for (std::size_t i = 0; i< m_diagonal.size(); i++)
+ {
+ for (std::size_t j = 0; j< m_diagonal.size(); j++)
+ {
+ std::size_t k=m_mat.row_idx[j];
+ while (m_mat.col_idx[k] < i && k < m_mat.row_idx[j+1])
+ k++;
+ if (m_mat.col_idx[k] == i && k < m_mat.row_idx[j+1])
+ nz_col[i].push_back(k);
+ }
+ nz_col[i].push_back(narrow_cast<std::size_t>(-1));
+ }
}
- template<typename R, typename V>
+ template <typename R, typename V>
void calc_rhs(R &rhs, const V &v)
{
m_mat.mult_vec(rhs, v);
@@ -108,87 +118,148 @@ namespace plib
void precondition()
{
- if (m_use_iLU_preconditioning)
+ for (std::size_t i = 0; i< m_diagonal.size(); i++)
{
- for (std::size_t i = 0; i< m_diag.size(); i++)
+ // ILUT: 265%
+ FT v(0.0);
+#if 0
+ // doesn't works, Mame performance drops significantly%
+ // 136%
+ for (std::size_t j = m_mat.row_idx[i]; j< m_mat.row_idx[i+1]; j++)
+ v += m_mat.A[j] * m_mat.A[j];
+ m_diagonal[i] = reciprocal(std::sqrt(v));
+#elif 0
+ // works halfway, i.e. Mame performance 50%
+ // 147% - lowest average solution time with 7.094
+ for (std::size_t j = m_mat.row_idx[i]; j< m_mat.row_idx[i+1]; j++)
+ v += m_mat.A[j] * m_mat.A[j];
+ m_diagonal[i] = m_mat.A[m_mat.diagonal[i]] / v;
+#elif 0
+ // works halfway, i.e. Mame performance 50%
+ // sum over column i
+ // 344% - lowest average solution time with 3.06
+ std::size_t nzcolp = 0;
+ const auto &nz = nz_col[i];
+ std::size_t j;
+
+ while ((j = nz[nzcolp++])!=narrow_cast<std::size_t>(-1)) // NOLINT(bugprone-infinite-loop)
{
- m_diag[i] = 1.0 / m_mat.A[m_mat.diag[i]];
+ v += m_mat.A[j] * m_mat.A[j];
}
+ m_diagonal[i] = m_mat.A[m_mat.diagonal[i]] / v;
+#elif 0
+ // works halfway, i.e. Mame performance 50%
+ // 151%
+ for (std::size_t j = m_mat.row_idx[i]; j< m_mat.row_idx[i+1]; j++)
+ v += plib::abs(m_mat.A[j]);
+ m_diagonal[i] = reciprocal(v);
+#else
+ // 124%
+ for (std::size_t j = m_mat.row_idx[i]; j< m_mat.row_idx[i+1]; j++)
+ v = std::max(v, plib::abs(m_mat.A[j]));
+ m_diagonal[i] = reciprocal(v);
+#endif
+ //m_diagonal[i] = reciprocal(m_mat.A[m_mat.diagonal[i]]);
}
}
- template<typename V>
- void solve_LU_inplace(V &v)
+ template <typename V>
+ void solve_inplace(V &v)
{
- if (m_use_iLU_preconditioning)
- {
- for (std::size_t i = 0; i< m_diag.size(); i++)
- v[i] = v[i] * m_diag[i];
- }
+ for (std::size_t i = 0; i< m_diagonal.size(); i++)
+ v[i] = v[i] * m_diagonal[i];
}
- plib::matrix_compressed_rows_t<FT, SIZE> m_mat;
- plib::parray<FT, SIZE> m_diag;
- bool m_use_iLU_preconditioning;
+ plib::pmatrix_cr<ARENA, FT, SIZE> m_mat;
+ plib::parray<FT, SIZE> m_diagonal;
+ plib::parray<std::vector<std::size_t>, SIZE > nz_col;
};
- /* FIXME: hardcoding RESTART to 20 becomes an issue on very large
- * systems.
- */
- template <typename FT, int SIZE, int RESTART = 20>
+ template <typename ARENA, typename FT, int SIZE>
+ struct mat_precondition_none
+ {
+ mat_precondition_none(std::size_t size, [[maybe_unused]] int dummy = 0)
+ : m_mat(size)
+ {
+ }
+
+ template <typename M>
+ void build(M &fill)
+ {
+ m_mat.build_from_fill_mat(fill, 0);
+ }
+
+ template <typename R, typename V>
+ void calc_rhs(R &rhs, const V &v)
+ {
+ m_mat.mult_vec(rhs, v);
+ }
+
+ void precondition()
+ {
+ }
+
+ template <typename V>
+ void solve_inplace([[maybe_unused]] V &v)
+ {
+ }
+
+ plib::pmatrix_cr<ARENA, FT, SIZE> m_mat;
+ };
+
+ // FIXME: hard coding RESTART to 20 becomes an issue on very large
+ // systems.
+
+ template <typename FT, int SIZE, int RESTARTMAX = 16>
struct gmres_t
{
public:
using float_type = FT;
- // FIXME: dirty hack to make this compile
- static constexpr const std::size_t storage_N = plib::sizeabs<FT, SIZE>::ABS();
- gmres_t(std::size_t size)
+ //constexpr static int RESTART = RESTARTMAX;
+ constexpr static const int RESTART = (SIZE > 0) ? ((SIZE < RESTARTMAX) ? SIZE : RESTARTMAX)
+ : ((SIZE < 0) ? ((-SIZE < RESTARTMAX) ? -SIZE : RESTARTMAX) : RESTARTMAX);
+
+ explicit gmres_t(std::size_t size)
: residual(size)
, Ax(size)
+ , m_ht(RESTART +1, RESTART)
+ , m_v(RESTART + 1, size)
, m_size(size)
, m_use_more_precise_stop_condition(false)
{
}
- void givens_mult( const FT c, const FT s, FT & g0, FT & g1 )
- {
- const FT g0_last(g0);
-
- g0 = c * g0 - s * g1;
- g1 = s * g0_last + c * g1;
- }
-
- std::size_t size() const { return (SIZE<=0) ? m_size : static_cast<std::size_t>(SIZE); }
+ std::size_t size() const { return (SIZE<=0) ? m_size : narrow_cast<std::size_t>(SIZE); }
template <typename OPS, typename VT, typename VRHS>
std::size_t solve(OPS &ops, VT &x, const VRHS & rhs, const std::size_t itr_max, float_type accuracy)
{
- /*-------------------------------------------------------------------------
- * The code below was inspired by code published by John Burkardt under
- * the LPGL here:
- *
- * http://people.sc.fsu.edu/~jburkardt/cpp_src/mgmres/mgmres.html
- *
- * The code below was completely written from scratch based on the pseudo code
- * found here:
- *
- * http://de.wikipedia.org/wiki/GMRES-Verfahren
- *
- * The Algorithm itself is described in
- *
- * Yousef Saad,
- * Iterative Methods for Sparse Linear Systems,
- * Second Edition,
- * SIAM, 20003,
- * ISBN: 0898715342,
- * LC: QA188.S17.
- *
- *------------------------------------------------------------------------*/
+ // -------------------------------------------------------------------------
+ // The code below was inspired by code published by John Burkardt under
+ // the LPGL here:
+ //
+ // http://people.sc.fsu.edu/~jburkardt/cpp_src/mgmres/mgmres.html
+ //
+ // The code below was completely written from scratch based on the pseudo code
+ // found here:
+ //
+ // http://de.wikipedia.org/wiki/GMRES-Verfahren
+ //
+ // The Algorithm itself is described in
+ //
+ // Yousef Saad,
+ // Iterative Methods for Sparse Linear Systems,
+ // Second Edition,
+ // SIAM, 20003,
+ // ISBN: 0898715342,
+ // LC: QA188.S17.
+ //
+ //------------------------------------------------------------------------
std::size_t itr_used = 0;
- double rho_delta = 0.0;
+ float_type rho_delta(plib::constants<float_type>::zero());
const std::size_t n = size();
@@ -196,165 +267,174 @@ namespace plib
if (m_use_more_precise_stop_condition)
{
- /* derive residual for a given delta x
- *
- * LU y = A dx
- *
- * ==> rho / accuracy = sqrt(y * y)
- *
- * This approach will approximate the iterative stop condition
- * based |xnew - xold| pretty precisely. But it is slow, or expressed
- * differently: The invest doesn't pay off.
- */
-
- vec_set_scalar(n, residual, accuracy);
+ // derive residual for a given delta x
+ //
+ // LU y = A dx
+ //
+ // ==> rho / accuracy = sqrt(y * y)
+ //
+ // This approach will approximate the iterative stop condition
+ // based `|xnew - xold|` pretty precisely. But it is slow, or expressed
+ // differently: The invest doesn't pay off.
+ //
+
+ vec_set_scalar(residual, accuracy);
ops.calc_rhs(Ax, residual);
- ops.solve_LU_inplace(Ax);
+ ops.solve_inplace(Ax);
- const float_type rho_to_accuracy = std::sqrt(vec_mult2<FT>(n, Ax)) / accuracy;
+ const float_type rho_to_accuracy = plib::sqrt(vec_mult2<FT>(Ax)) / accuracy;
rho_delta = accuracy * rho_to_accuracy;
}
else
- rho_delta = accuracy * std::sqrt(static_cast<FT>(n));
-
- /*
- * Using
- *
- * vec_set(n, x, rhs);
- * ops.solve_LU_inplace(x);
- *
- * to get a starting point for x degrades convergence speed compared
- * to using the last solution for x.
- *
- * LU x = b; solve for x;
- *
- */
+ //rho_delta = accuracy * plib::sqrt(vec_mult2<FT>(n, rhs))
+ // + 1e-4 * std::sqrt(n);
+ rho_delta = accuracy * plib::sqrt(narrow_cast<FT>(n));
+
+ //
+ // LU x = b; solve for x;
+ //
+ // Using
+ //
+ // vec_set(n, x, rhs);
+ // ops.solve_inplace(x);
+ //
+ // to get a starting point for x degrades convergence speed compared
+ // to using the last solution for x.
while (itr_used < itr_max)
{
- std::size_t last_k = RESTART;
float_type rho;
ops.calc_rhs(Ax, x);
- vec_sub(n, residual, rhs, Ax);
+ vec_sub(residual, rhs, Ax);
- ops.solve_LU_inplace(residual);
+ ops.solve_inplace(residual);
- rho = std::sqrt(vec_mult2<FT>(n, residual));
+ rho = plib::sqrt(vec_mult2<FT>(residual));
if (rho < rho_delta)
return itr_used + 1;
- /* FIXME: The "+" is necessary to avoid link issues
- * on some systems / compiler versions. Issue reported by
- * AJR, no details known yet.
- */
- vec_set_scalar(RESTART+1, m_g, +constants<FT>::zero());
+ // FIXME: The "+" is necessary to avoid link issues
+ // on some systems / compiler versions. Issue reported by
+ // AJR, no details known yet.
+
+ vec_set_scalar(m_g, +constants<FT>::zero());
m_g[0] = rho;
- //for (std::size_t i = 0; i < mr + 1; i++)
- // vec_set_scalar(mr, m_ht[i], NL_FCONST(0.0));
+ vec_mult_scalar(m_v[0], residual, plib::reciprocal(rho));
- vec_mult_scalar(n, m_v[0], residual, constants<FT>::one() / rho);
+ if (do_k<RESTART-1>(ops, x, itr_used, rho_delta, true))
+ // converged
+ break;
+ }
+ return itr_used;
+ }
- for (std::size_t k = 0; k < RESTART; k++)
- {
- const std::size_t kp1 = k + 1;
+ private:
- ops.calc_rhs(m_v[kp1], m_v[k]);
- ops.solve_LU_inplace(m_v[kp1]);
+ static void givens_mult(FT c, FT s, FT & g0, FT & g1 )
+ {
+ const FT g0_last(g0);
+
+ g0 = c * g0 - s * g1;
+ g1 = s * g0_last + c * g1;
+ }
- for (std::size_t j = 0; j <= k; j++)
- {
- m_ht[j][k] = vec_mult<FT>(n, m_v[kp1], m_v[j]);
- vec_add_mult_scalar(n, m_v[kp1], m_v[j], -m_ht[j][k]);
- }
- m_ht[kp1][k] = std::sqrt(vec_mult2<FT>(n, m_v[kp1]));
+ template <int k, typename OPS, typename VT>
+ bool do_k(OPS &ops, VT &x, std::size_t &itr_used, FT rho_delta, [[maybe_unused]] bool dummy)
+ {
+ if (do_k<k-1, OPS>(ops, x, itr_used, rho_delta, do_k_helper<k-1>::value))
+ return true;
+
+ constexpr const std::size_t kp1 = k + 1;
+ //const std::size_t n = size();
- if (m_ht[kp1][k] != 0.0)
- vec_scale(n, m_v[kp1], constants<FT>::one() / m_ht[kp1][k]);
+ ops.calc_rhs(m_v[kp1], m_v[k]);
+ ops.solve_inplace(m_v[kp1]);
- for (std::size_t j = 0; j < k; j++)
- givens_mult(m_c[j], m_s[j], m_ht[j][k], m_ht[j+1][k]);
+ for (std::size_t j = 0; j <= k; j++)
+ {
+ m_ht[j][k] = vec_mult<FT>(m_v[kp1], m_v[j]);
+ vec_add_mult_scalar(m_v[kp1], m_v[j], -m_ht[j][k]);
+ }
+ m_ht[kp1][k] = plib::sqrt(vec_mult2<FT>(m_v[kp1]));
- const float_type mu = 1.0 / std::hypot(m_ht[k][k], m_ht[kp1][k]);
+ // FIXME: comparison to zero
+ if (m_ht[kp1][k] != plib::constants<FT>::zero())
+ vec_scale(m_v[kp1], reciprocal(m_ht[kp1][k]));
- m_c[k] = m_ht[k][k] * mu;
- m_s[k] = -m_ht[kp1][k] * mu;
- m_ht[k][k] = m_c[k] * m_ht[k][k] - m_s[k] * m_ht[kp1][k];
- m_ht[kp1][k] = 0.0;
+ for (std::size_t j = 0; j < k; j++)
+ givens_mult(m_c[j], m_s[j], m_ht[j][k], m_ht[j+1][k]);
- givens_mult(m_c[k], m_s[k], m_g[k], m_g[kp1]);
+ const float_type mu = reciprocal(plib::hypot(m_ht[k][k], m_ht[kp1][k]));
- rho = std::abs(m_g[kp1]);
+ m_c[k] = m_ht[k][k] * mu;
+ m_s[k] = -m_ht[kp1][k] * mu;
+ m_ht[k][k] = m_c[k] * m_ht[k][k] - m_s[k] * m_ht[kp1][k];
+ m_ht[kp1][k] = plib::constants<FT>::zero();
- itr_used = itr_used + 1;
+ givens_mult(m_c[k], m_s[k], m_g[k], m_g[kp1]);
- if (rho <= rho_delta)
- {
- last_k = k;
- break;
- }
- }
+ const float_type rho = plib::abs(m_g[kp1]);
- if (last_k >= RESTART)
- /* didn't converge within accuracy */
- last_k = RESTART - 1;
+ // FIXME ..
+ itr_used = itr_used + 1;
- /* Solve the system H * y = g */
- /* x += m_v[j] * m_y[j] */
- for (std::size_t i = last_k + 1; i-- > 0;)
+ if (rho <= rho_delta || k == RESTART-1)
+ {
+ // Solve the system H * y = g
+ // x += m_v[j] * m_y[j]
+ for (std::size_t i = k + 1; i-- > 0;)
{
- double tmp = m_g[i];
- for (std::size_t j = i + 1; j <= last_k; j++)
+ auto tmp = m_g[i];
+ const auto ht_i_i = plib::reciprocal(m_ht[i][i]);
+ for (std::size_t j = i + 1; j <= k; j++)
tmp -= m_ht[i][j] * m_y[j];
- m_y[i] = tmp / m_ht[i][i];
+ m_y[i] = tmp * ht_i_i;
+ vec_add_mult_scalar(x, m_v[i], m_y[i]);
}
- for (std::size_t i = 0; i <= last_k; i++)
- vec_add_mult_scalar(n, x, m_v[i], m_y[i]);
-
- if (rho <= rho_delta)
- break;
-
+ //for (std::size_t i = 0; i <= k; i++)
+ // vec_add_mult_scalar(n, x, m_v[i], m_y[i]);
+ return true;
}
- return itr_used;
+ return false;
}
- private:
-
- //typedef typename plib::mat_cr_t<FT, SIZE>::index_type mattype;
+ template <int k, typename OPS, typename VT>
+ bool do_k(OPS &ops, VT &x, std::size_t &itr_used, FT rho_delta, float dummy)
+ {
+ plib::unused_var(ops, x, itr_used, rho_delta, dummy);
+ return false;
+ }
plib::parray<float_type, SIZE> residual;
plib::parray<float_type, SIZE> Ax;
- plib::parray<float_type, RESTART + 1> m_c; /* mr + 1 */
- plib::parray<float_type, RESTART + 1> m_g; /* mr + 1 */
- plib::parray<plib::parray<float_type, RESTART>, RESTART + 1> m_ht; /* (mr + 1), mr */
- plib::parray<float_type, RESTART + 1> m_s; /* mr + 1 */
- plib::parray<float_type, RESTART + 1> m_y; /* mr + 1 */
+ plib::parray<float_type, RESTART + 1> m_c; // mr + 1
+ plib::parray<float_type, RESTART + 1> m_g; // mr + 1
+ plib::parray2D<float_type, RESTART + 1, RESTART> m_ht; // (mr + 1), mr
+ plib::parray<float_type, RESTART + 1> m_s; // mr + 1
+ plib::parray<float_type, RESTART + 1> m_y; // mr + 1
- //plib::parray<float_type, SIZE> m_v[RESTART + 1]; /* mr + 1, n */
- plib::parray<plib::parray<float_type, storage_N>, RESTART + 1> m_v; /* mr + 1, n */
+ plib::parray2D<float_type, RESTART + 1, SIZE> m_v; // mr + 1, n
std::size_t m_size;
bool m_use_more_precise_stop_condition;
-
-
};
#if 0
- /* Example of a Chebyshev iteration solver. This one doesn't work yet,
- * it needs to be extended for non-symmetric matrix operation and
- * depends on spectral radius estimates - which we don't have.
- *
- * Left here as another example.
- */
+ // Example of a Chebyshev iteration solver. This one doesn't work yet,
+ // it needs to be extended for non-symmetric matrix operation and
+ // depends on spectral radius estimates - which we don't have.
+ //
+ // Left here as another example.
template <typename FT, int SIZE>
struct ch_t
@@ -375,16 +455,11 @@ namespace plib
{
}
- std::size_t size() const { return (SIZE<=0) ? m_size : static_cast<std::size_t>(SIZE); }
+ std::size_t size() const { return (SIZE<=0) ? m_size : narrow_cast<std::size_t>(SIZE); }
template <typename OPS, typename VT, typename VRHS>
std::size_t solve(OPS &ops, VT &x0, const VRHS & rhs, const std::size_t iter_max, float_type accuracy)
{
- /*-------------------------------------------------------------------------
- *
- *
- *------------------------------------------------------------------------*/
-
ops.precondition();
const FT lmax = 20.0;
@@ -404,13 +479,13 @@ namespace plib
ops.calc_rhs(Ax, x);
vec_sub(size(), rhs, Ax, residual);
- FT rho_delta = accuracy * std::sqrt(static_cast<FT>(size()));
+ FT rho_delta = accuracy * std::sqrt(narrow_cast<FT>(size()));
rho_delta = 1e-9;
for (int i = 0; i < iter_max; i++)
{
- ops.solve_LU_inplace(residual);
+ ops.solve_inplace(residual);
if (i==0)
{
vec_set(size(), p, residual);
@@ -419,7 +494,7 @@ namespace plib
else
{
beta = alpha * ( c / 2.0)*( c / 2.0);
- alpha = 1.0 / (d - beta);
+ alpha = reciprocal(d - beta);
for (std::size_t k = 0; k < size(); k++)
p[k] = residual[k] + beta * p[k];
}
@@ -435,7 +510,7 @@ namespace plib
}
private:
- //typedef typename plib::mat_cr_t<FT, SIZE>::index_type mattype;
+ //#typedef typename plib::mat_cr_t<FT, SIZE>::index_type mattype;
plib::parray<float_type, SIZE> residual;
plib::parray<float_type, SIZE> Ax;
@@ -447,4 +522,4 @@ namespace plib
} // namespace plib
-#endif /* PLIB_GMRES_H_ */
+#endif // PLIB_GMRES_H_
diff --git a/src/lib/netlist/plib/mat_cr.h b/src/lib/netlist/plib/mat_cr.h
deleted file mode 100644
index db791752cf1..00000000000
--- a/src/lib/netlist/plib/mat_cr.h
+++ /dev/null
@@ -1,529 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * mat_cr.h
- *
- * Compressed row format matrices
- *
- */
-
-#ifndef MAT_CR_H_
-#define MAT_CR_H_
-
-#include "palloc.h"
-#include "parray.h"
-#include "pconfig.h"
-#include "pomp.h"
-#include "pstate.h"
-#include "ptypes.h"
-#include "putil.h"
-
-#include <algorithm>
-#include <array>
-#include <cmath>
-#include <type_traits>
-#include <vector>
-
-namespace plib
-{
-
- // FIXME: causes a crash with GMRES handler
- // template<typename T, int N, typename C = std::size_t>
-
- template<typename T, int N, typename C = uint16_t>
- struct matrix_compressed_rows_t
- {
- using index_type = C;
- using value_type = T;
-
- COPYASSIGNMOVE(matrix_compressed_rows_t, default)
-
- enum constants_e
- {
- FILL_INFINITY = 9999999
- };
-
- parray<index_type, N> diag; // diagonal index pointer n
- parray<index_type, (N == 0) ? 0 : (N < 0 ? N - 1 : N + 1)> row_idx; // row index pointer n + 1
- parray<index_type, N < 0 ? -N * N : N *N> col_idx; // column index array nz_num, initially (n * n)
- parray<value_type, N < 0 ? -N * N : N *N> A; // Matrix elements nz_num, initially (n * n)
- //parray<C, N < 0 ? -N * (N-1) / 2 : N * (N+1) / 2 > nzbd; // Support for gaussian elimination
- parray<std::vector<index_type>, N > nzbd; // Support for gaussian elimination
- // contains elimination rows below the diagonal
- // FIXME: convert to pvector
- std::vector<std::vector<index_type>> m_ge_par;
-
- index_type nz_num;
-
- explicit matrix_compressed_rows_t(const index_type n)
- : diag(n)
- , row_idx(n+1)
- , col_idx(n*n)
- , A(n*n)
- //, nzbd(n * (n+1) / 2)
- , nzbd(n)
- , nz_num(0)
- , m_size(n)
- {
- for (index_type i=0; i<n+1; i++)
- row_idx[i] = 0;
- }
-
- ~matrix_compressed_rows_t() = default;
-
- constexpr index_type size() const { return static_cast<index_type>((N>0) ? N : m_size); }
-
- void clear()
- {
- nz_num = 0;
- for (index_type i=0; i < size() + 1; i++)
- row_idx[i] = 0;
- }
-
- void set_scalar(const T scalar)
- {
- for (index_type i=0, e=nz_num; i<e; i++)
- A[i] = scalar;
- }
-
- void set(C r, C c, T val)
- {
- C ri = row_idx[r];
- while (ri < row_idx[r+1] && col_idx[ri] < c)
- ri++;
- // we have the position now;
- if (ri < row_idx[r+1] && col_idx[ri] == c)
- A[ri] = val;
- else
- {
- for (C i = nz_num; i>ri; i--)
- {
- A[i] = A[i-1];
- col_idx[i] = col_idx[i-1];
- }
- A[ri] = val;
- col_idx[ri] = c;
- for (C i = r + 1; i < size() + 1; i++)
- row_idx[i]++;
- nz_num++;
- if (c==r)
- diag[r] = ri;
- }
- }
-
- template <typename M>
- std::pair<std::size_t, std::size_t> gaussian_extend_fill_mat(M &fill)
- {
- std::size_t ops = 0;
- std::size_t fill_max = 0;
-
- for (std::size_t k = 0; k < fill.size(); k++)
- {
- ops++; // 1/A(k,k)
- for (std::size_t row = k + 1; row < fill.size(); row++)
- {
- if (fill[row][k] < FILL_INFINITY)
- {
- ops++;
- for (std::size_t col = k + 1; col < fill[row].size(); col++)
- //if (fill[k][col] < FILL_INFINITY)
- {
- auto f = std::min(fill[row][col], 1 + fill[row][k] + fill[k][col]);
- if (f < FILL_INFINITY)
- {
- if (f > fill_max)
- fill_max = f;
- ops += 2;
- }
- fill[row][col] = f;
- }
- }
- }
- }
- build_parallel_gaussian_execution_scheme(fill);
- return { fill_max, ops };
- }
-
- template <typename M>
- void build_from_fill_mat(const M &f, std::size_t max_fill = FILL_INFINITY - 1,
- std::size_t band_width = FILL_INFINITY)
- {
- C nz = 0;
- if (nz_num != 0)
- throw pexception("build_from_mat only allowed on empty CR matrix");
- for (std::size_t k=0; k < size(); k++)
- {
- row_idx[k] = nz;
-
- for (std::size_t j=0; j < size(); j++)
- if (f[k][j] <= max_fill && std::abs(static_cast<int>(k)-static_cast<int>(j)) <= static_cast<int>(band_width))
- {
- col_idx[nz] = static_cast<C>(j);
- if (j == k)
- diag[k] = nz;
- nz++;
- }
- }
-
- row_idx[size()] = nz;
- nz_num = nz;
- /* build nzbd */
-
- for (std::size_t k=0; k < size(); k++)
- {
- for (std::size_t j=k + 1; j < size(); j++)
- if (f[j][k] < FILL_INFINITY)
- nzbd[k].push_back(static_cast<C>(j));
- nzbd[k].push_back(0); // end of sequence
- }
- }
-
- template <typename V>
- void gaussian_elimination(V & RHS)
- {
- const std::size_t iN = size();
-
- for (std::size_t i = 0; i < iN - 1; i++)
- {
- std::size_t nzbdp = 0;
- std::size_t pi = diag[i];
- const value_type f = 1.0 / A[pi++];
- const std::size_t piie = row_idx[i+1];
- const auto &nz = nzbd[i];
-
- while (auto j = nz[nzbdp++])
- {
- // proceed to column i
-
- std::size_t pj = row_idx[j];
-
- while (col_idx[pj] < i)
- pj++;
-
- const value_type f1 = - A[pj++] * f;
-
- // subtract row i from j
- // fill-in available assumed, i.e. matrix was prepared
-
- for (std::size_t pii = pi; pii<piie; pii++)
- {
- while (col_idx[pj] < col_idx[pii])
- pj++;
- if (col_idx[pj] == col_idx[pii])
- A[pj++] += A[pii] * f1;
- }
-
- RHS[j] += f1 * RHS[i];
- }
- }
- }
-
- template <typename V>
- void gaussian_elimination_parallel(V & RHS)
- {
- // FIXME: move into solver creation ...
- plib::omp::set_num_threads(4);
- for (auto l = 0ul; l < m_ge_par.size(); l++)
- plib::omp::for_static(0ul, m_ge_par[l].size(), [this, &RHS, &l] (unsigned ll)
- {
- auto &i = m_ge_par[l][ll];
- {
- std::size_t nzbdp = 0;
- std::size_t pi = diag[i];
- const value_type f = 1.0 / A[pi++];
- const std::size_t piie = row_idx[i+1];
-
- while (auto j = nzbd[i][nzbdp++])
- {
- // proceed to column i
-
- std::size_t pj = row_idx[j];
-
- while (col_idx[pj] < i)
- pj++;
-
- const value_type f1 = - A[pj++] * f;
-
- // subtract row i from j
- // fill-in available assumed, i.e. matrix was prepared
- for (std::size_t pii = pi; pii<piie; pii++)
- {
- while (col_idx[pj] < col_idx[pii])
- pj++;
- if (col_idx[pj] == col_idx[pii])
- A[pj++] += A[pii] * f1;
- }
- RHS[j] += f1 * RHS[i];
- }
- }
- });
- }
-
- template <typename V1, typename V2>
- void gaussian_back_substitution(V1 &V, const V2 &RHS)
- {
- const std::size_t iN = size();
- /* row n-1 */
- V[iN - 1] = RHS[iN - 1] / A[diag[iN - 1]];
-
- for (std::size_t j = iN - 1; j-- > 0;)
- {
- value_type tmp = 0;
- const auto jdiag = diag[j];
- const std::size_t e = row_idx[j+1];
- for (std::size_t pk = jdiag + 1; pk < e; pk++)
- tmp += A[pk] * V[col_idx[pk]];
- V[j] = (RHS[j] - tmp) / A[jdiag];
- }
- }
-
- template <typename V1>
- void gaussian_back_substitution(V1 &V)
- {
- const std::size_t iN = size();
- /* row n-1 */
- V[iN - 1] = V[iN - 1] / A[diag[iN - 1]];
-
- for (std::size_t j = iN - 1; j-- > 0;)
- {
- value_type tmp = 0;
- const auto jdiag = diag[j];
- const std::size_t e = row_idx[j+1];
- for (std::size_t pk = jdiag + 1; pk < e; pk++)
- tmp += A[pk] * V[col_idx[pk]];
- V[j] = (V[j] - tmp) / A[jdiag];
- }
- }
-
-
- template <typename VTV, typename VTR>
- void mult_vec(VTR & res, const VTV & x)
- {
- /*
- * res = A * x
- */
-
- std::size_t row = 0;
- std::size_t k = 0;
- const std::size_t oe = nz_num;
-
- while (k < oe)
- {
- T tmp = 0.0;
- const std::size_t e = row_idx[row+1];
- for (; k < e; k++)
- tmp += A[k] * x[col_idx[k]];
- res[row++] = tmp;
- }
- }
-
- /* throws error if P(source)>P(destination) */
- template <typename LUMAT>
- void slim_copy_from(LUMAT & src)
- {
- for (std::size_t r=0; r<src.size(); r++)
- {
- C dp = row_idx[r];
- for (C sp = src.row_idx[r]; sp < src.row_idx[r+1]; sp++)
- {
- /* advance dp to source column and fill 0s if necessary */
- while (col_idx[dp] < src.col_idx[sp])
- A[dp++] = 0;
- if (row_idx[r+1] <= dp || col_idx[dp] != src.col_idx[sp])
- throw plib::pexception("slim_copy_from error");
- A[dp++] = src.A[sp];
- }
- /* fill remaining elements in row */
- while (dp < row_idx[r+1])
- A[dp++] = 0;
- }
- }
-
- /* only copies common elements */
- template <typename LUMAT>
- void reduction_copy_from(LUMAT & src)
- {
- C sp = 0;
- for (std::size_t r=0; r<src.size(); r++)
- {
- C dp = row_idx[r];
- while(sp < src.row_idx[r+1])
- {
- /* advance dp to source column and fill 0s if necessary */
- if (col_idx[dp] < src.col_idx[sp])
- A[dp++] = 0;
- else if (col_idx[dp] == src.col_idx[sp])
- A[dp++] = src.A[sp++];
- else
- sp++;
- }
- /* fill remaining elements in row */
- while (dp < row_idx[r+1])
- A[dp++] = 0;
- }
- }
-
- /* checks at all - may crash */
- template <typename LUMAT>
- void raw_copy_from(LUMAT & src)
- {
- for (std::size_t k = 0; k < nz_num; k++)
- A[k] = src.A[k];
- }
-
- void incomplete_LU_factorization()
- {
- /*
- * incomplete LU Factorization according to http://de.wikipedia.org/wiki/ILU-Zerlegung
- *
- * Result is stored in matrix LU
- *
- * For i = 1,...,N-1
- * For k = 0, ... , i - 1
- * If a[i,k] != 0
- * a[i,k] = a[i,k] / a[k,k]
- * For j = k + 1, ... , N - 1
- * If a[i,j] != 0
- * a[i,j] = a[i,j] - a[i,k] * a[k,j]
- * j=j+1
- * k=k+1
- * i=i+1
- *
- */
-
- for (std::size_t i = 1; i < size(); i++) // row i
- {
- const std::size_t p_i_end = row_idx[i + 1];
- // loop over all columns k left of diag in row i
- for (std::size_t i_k = row_idx[i]; i_k < diag[i]; i_k++)
- {
- const std::size_t k = col_idx[i_k];
- const std::size_t p_k_end = row_idx[k + 1];
- const T LUp_i_k = A[i_k] = A[i_k] / A[diag[k]];
-
- std::size_t k_j = diag[k] + 1;
- std::size_t i_j = i_k + 1;
-
- while (i_j < p_i_end && k_j < p_k_end ) // pj = (i, j)
- {
- // we can assume that within a row ja increases continuously */
- const std::size_t c_i_j = col_idx[i_j]; // row i, column j
- const std::size_t c_k_j = col_idx[k_j]; // row i, column j
- if (c_k_j < c_i_j)
- k_j++;
- else if (c_k_j == c_i_j)
- A[i_j++] -= LUp_i_k * A[k_j++];
- else
- i_j++;
- }
- }
- }
- }
-
- template <typename R>
- void solveLUx (R &r)
- {
- /*
- * Solve a linear equation Ax = r
- * where
- * A = L*U
- *
- * L unit lower triangular
- * U upper triangular
- *
- * ==> LUx = r
- *
- * ==> Ux = L⁻¹ r = w
- *
- * ==> r = Lw
- *
- * This can be solved for w using backwards elimination in L.
- *
- * Now Ux = w
- *
- * This can be solved for x using backwards elimination in U.
- *
- */
- for (std::size_t i = 1; i < size(); ++i )
- {
- T tmp = 0.0;
- const std::size_t j1 = row_idx[i];
- const std::size_t j2 = diag[i];
-
- for (std::size_t j = j1; j < j2; ++j )
- tmp += A[j] * r[col_idx[j]];
- r[i] -= tmp;
- }
- // i now is equal to n;
- for (std::size_t i = size(); i-- > 0; )
- {
- T tmp = 0.0;
- const std::size_t di = diag[i];
- const std::size_t j2 = row_idx[i+1];
- for (std::size_t j = di + 1; j < j2; j++ )
- tmp += A[j] * r[col_idx[j]];
- r[i] = (r[i] - tmp) / A[di];
- }
- }
- private:
- template <typename M>
- void build_parallel_gaussian_execution_scheme(const M &fill)
- {
- // calculate parallel scheme for gaussian elimination
- std::vector<std::vector<index_type>> rt(size());
- for (index_type k = 0; k < size(); k++)
- {
- for (index_type j = k+1; j < size(); j++)
- {
- if (fill[j][k] < FILL_INFINITY)
- {
- rt[k].push_back(j);
- }
- }
- }
-
- std::vector<index_type> levGE(size(), 0);
- index_type cl = 0;
-
- for (index_type k = 0; k < size(); k++ )
- {
- if (levGE[k] >= cl)
- {
- std::vector<index_type> t = rt[k];
- for (index_type j = k+1; j < size(); j++ )
- {
- bool overlap = false;
- // is there overlap
- if (plib::container::contains(t, j))
- overlap = true;
- for (auto &x : rt[j])
- if (plib::container::contains(t, x))
- {
- overlap = true;
- break;
- }
- if (overlap)
- levGE[j] = cl + 1;
- else
- {
- t.push_back(j);
- for (auto &x : rt[j])
- t.push_back(x);
- }
- }
- cl++;
- }
- }
-
- m_ge_par.clear();
- m_ge_par.resize(cl+1);
- for (index_type k = 0; k < size(); k++)
- m_ge_par[levGE[k]].push_back(k);
- }
-
- index_type m_size;
- };
-
-} // namespace plib
-
-#endif /* MAT_CR_H_ */
diff --git a/src/lib/netlist/plib/palloc.h b/src/lib/netlist/plib/palloc.h
index a9b03353b37..1eff726d45d 100644
--- a/src/lib/netlist/plib/palloc.h
+++ b/src/lib/netlist/plib/palloc.h
@@ -1,19 +1,21 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * palloc.h
- *
- */
#ifndef PALLOC_H_
#define PALLOC_H_
+///
+/// \file palloc.h
+///
+
#include "pconfig.h"
-#include "pstring.h"
+#include "pgsl.h"
+#include "pgsl.h"
+#include "pmath.h" // FIXME: only uses lcm ... move to ptypes.
#include "ptypes.h"
+#include <algorithm>
#include <cstddef> // for std::max_align_t (usually long long)
-//#include <cstdlib>
#include <memory>
#include <type_traits>
#include <utility>
@@ -29,36 +31,82 @@ namespace plib {
// Standard arena_deleter
//============================================================
- template <typename P, typename T>
- struct arena_deleter
+ template <typename P, typename T, std::size_t ALIGN, bool X>
+ struct arena_deleter_base
+ {
+ };
+
+ struct deleter_info_t
+ {
+ std::size_t alignment;
+ std::size_t size;
+ };
+
+ template <typename P, typename T, std::size_t ALIGN>
+ struct arena_deleter_base<P, T, ALIGN, false>
{
- //using arena_storage_type = P *;
- using arena_storage_type = typename std::conditional<P::is_stateless, P, P *>::type;
- template <typename X, typename Y = void>
- typename std::enable_if<!X::is_stateless, X&>::type getref(X *x) { return *x;}
- template <typename X, typename Y = void *>
- typename std::enable_if<std::remove_pointer<X>::type::is_stateless, X&>::type
- getref(X &x, Y y = nullptr)
+ constexpr arena_deleter_base(P *a = nullptr) noexcept
+ : m_arena(a), m_info({ALIGN ? ALIGN : alignof(T), sizeof(T)} ) { }
+
+ template<typename U, typename =
+ std::enable_if_t<std::is_convertible_v<U *, T *>>>
+ constexpr arena_deleter_base(const arena_deleter_base<P, U, ALIGN, false> &rhs) noexcept
+ : m_arena(rhs.m_arena), m_info(rhs.m_info) { }
+
+ void operator()(T *p) noexcept
{
- unused_var(y);
- return x;
+ // call destructor
+ p->~T();
+ m_arena->deallocate(p, m_info.alignment, m_info.size);
}
- constexpr arena_deleter(arena_storage_type a = arena_storage_type()) noexcept
- : m_a(a) { }
+ P *m_arena;
+ deleter_info_t m_info;
+ };
- template<typename PU, typename U, typename = typename
- std::enable_if<std::is_convertible< U*, T*>::value>::type>
- arena_deleter(const arena_deleter<PU, U> &rhs) noexcept : m_a(rhs.m_a) { }
+ template <typename P, typename T, std::size_t ALIGN>
+ struct arena_deleter_base<P, T, ALIGN, true>
+ {
+ constexpr arena_deleter_base(/*[[maybe_unused]]*/ P *a = nullptr) noexcept
+ : m_info({ALIGN ? ALIGN : alignof(T), sizeof(T)})
+ {
+ plib::unused_var(a); // GCC 7.x does not like the maybe_unused
+ }
- void operator()(T *p) //const
+ template<typename U, typename =
+ std::enable_if_t<std::is_convertible_v<U *, T *>>>
+ constexpr arena_deleter_base(const arena_deleter_base<P, U, ALIGN, true> &rhs) noexcept
+ : m_info(rhs.m_info)
{
- /* call destructor */
+ }
+
+ void operator()(T *p) noexcept
+ {
+ // call destructor
p->~T();
- getref(m_a).deallocate(p);
+ P::deallocate(p, m_info.alignment, m_info.size);
}
- //private:
- arena_storage_type m_a;
+
+ deleter_info_t m_info;
+ };
+
+
+ ///
+ /// \brief alignment aware deleter class
+ ///
+ /// The deleter class expects the object to have been allocated with
+ /// `alignof(T)` alignment if the ALIGN parameter is omitted. If ALIGN is
+ /// given, this is used.
+ ///
+ /// \tparam A Arena type
+ /// \tparam T Object type
+ /// \tparam ALIGN alignment
+ ///
+ template <typename A, typename T, std::size_t ALIGN = 0>
+ struct arena_deleter : public arena_deleter_base<A, T, ALIGN, A::has_static_deallocator>
+ {
+ using base_type = arena_deleter_base<A, T, ALIGN, A::has_static_deallocator>;
+ using base_type::base_type;
};
//============================================================
@@ -80,27 +128,26 @@ namespace plib {
template <typename, typename>
friend class owned_ptr;
- owned_ptr(pointer p, bool owned) noexcept
+ owned_ptr(pointer p, bool owned)
: m_ptr(p), m_deleter(), m_is_owned(owned)
{ }
- owned_ptr(pointer p, bool owned, D deleter) noexcept
- : m_ptr(p), m_deleter(deleter), m_is_owned(owned)
+ owned_ptr(pointer p, bool owned, D deleter)
+ : m_ptr(p), m_deleter(std::move(deleter)), m_is_owned(owned)
{ }
owned_ptr(const owned_ptr &r) = delete;
owned_ptr & operator =(owned_ptr &r) = delete;
- template<typename DC, typename DC_D>
- owned_ptr & operator =(owned_ptr<DC, DC_D> &&r)
+ template <typename DC, typename DC_D>
+ owned_ptr & operator =(owned_ptr<DC, DC_D> &&r) noexcept
{
if (m_is_owned && (m_ptr != nullptr))
- //delete m_ptr;
m_deleter(m_ptr);
m_is_owned = r.m_is_owned;
m_ptr = r.m_ptr;
- m_deleter = r.m_deleter;
+ m_deleter = std::move(r.m_deleter);
r.m_is_owned = false;
r.m_ptr = nullptr;
return *this;
@@ -108,7 +155,7 @@ namespace plib {
owned_ptr(owned_ptr &&r) noexcept
: m_ptr(r.m_ptr)
- , m_deleter(r.m_deleter)
+ , m_deleter(std::move(r.m_deleter))
, m_is_owned(r.m_is_owned)
{
r.m_is_owned = false;
@@ -118,7 +165,6 @@ namespace plib {
owned_ptr &operator=(owned_ptr &&r) noexcept
{
if (m_is_owned && (m_ptr != nullptr))
- //delete m_ptr;
m_deleter(m_ptr);
m_is_owned = r.m_is_owned;
m_ptr = r.m_ptr;
@@ -131,7 +177,7 @@ namespace plib {
template<typename DC, typename DC_D>
owned_ptr(owned_ptr<DC, DC_D> &&r) noexcept
: m_ptr(static_cast<pointer >(r.get()))
- , m_deleter(r.m_deleter)
+ , m_deleter(std::move(r.m_deleter))
, m_is_owned(r.is_owned())
{
r.release();
@@ -148,9 +194,9 @@ namespace plib {
m_ptr = nullptr;
}
- /**
- * \brief Return @c true if the stored pointer is not null.
- */
+ ///
+ /// \brief Return \c true if the stored pointer is not null.
+ ///
explicit operator bool() const noexcept { return m_ptr != nullptr; }
pointer release()
@@ -180,73 +226,100 @@ namespace plib {
// Arena allocator for use with containers
//============================================================
- template <class ARENA, class T, std::size_t ALIGN = alignof(T)>
+ template <class ARENA, class T, std::size_t ALIGN, bool HSA>
class arena_allocator
{
public:
using value_type = T;
- static constexpr const std::size_t align_size = ALIGN;
+ using pointer = T *;
+ static constexpr const std::size_t align_size = ALIGN ? ALIGN : alignof(T);
using arena_type = ARENA;
- static_assert(align_size >= alignof(T) && (align_size % alignof(T)) == 0,
+ static_assert((align_size % alignof(T)) == 0,
"ALIGN must be greater than alignof(T) and a multiple");
+ template <typename U = int, typename = std::enable_if_t<HSA && sizeof(U)>>
+ //[[deprecated]]
arena_allocator() noexcept
: m_a(arena_type::instance())
{ }
~arena_allocator() noexcept = default;
- arena_allocator(const arena_allocator &rhs) noexcept = default;
- arena_allocator& operator=(const arena_allocator&) noexcept = delete;
+ arena_allocator(const arena_allocator &) = default;
+ arena_allocator &operator=(const arena_allocator &) = default;
+ arena_allocator(arena_allocator &&) noexcept = default;
+ arena_allocator &operator=(arena_allocator &&) noexcept = default;
- arena_allocator(arena_allocator&&) noexcept = default;
- arena_allocator& operator=(arena_allocator&&) = delete;
+ template <typename U = int>
+ arena_allocator(/*[[maybe_unused]]*/ std::enable_if_t<HSA && sizeof(U), arena_type> & a) noexcept
+ : m_a(arena_type::instance())
+ {
+ plib::unused_var(a); // GCC 7.x does not like the maybe_unused
+ }
- arena_allocator(arena_type & a) noexcept : m_a(a)
+ template <typename U = int>
+ arena_allocator(/*[[maybe_unused]]*/ std::enable_if_t<!HSA && sizeof(U), arena_type> & a) noexcept
+ : m_a(a)
{
+ plib::unused_var(a); // GCC 7.x does not like the maybe_unused
}
- template <class U>
- arena_allocator(const arena_allocator<ARENA, U, ALIGN>& rhs) noexcept
+ template <class U, typename = std::enable_if_t<!std::is_same_v<T, U>>>
+ arena_allocator(const arena_allocator<ARENA, U, ALIGN, HSA>& rhs) noexcept
: m_a(rhs.m_a)
{
}
- template <class U> struct rebind
+ template <class U>
+ struct rebind
{
- using other = arena_allocator<ARENA, U, ALIGN>;
+ using other = arena_allocator<ARENA, U, ALIGN, HSA>;
};
- T* allocate(std::size_t n)
+ pointer allocate(std::size_t n)
+ {
+ return reinterpret_cast<T *>(m_a.allocate(align_size, sizeof(T) * n)); //NOLINT
+ }
+
+ void deallocate(pointer p, std::size_t n) noexcept
+ {
+ m_a.deallocate(p, align_size, sizeof(T) * n);
+ }
+
+ template<typename U, typename... Args>
+ void construct(U* p, Args&&... args)
{
- return reinterpret_cast<T *>(m_a.allocate(ALIGN, sizeof(T) * n));
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
+ ::new (void_ptr_cast(p)) U(std::forward<Args>(args)...);
}
- void deallocate(T* p, std::size_t n) noexcept
+ template<typename U>
+ void destroy(U* p)
{
- unused_var(n);
- m_a.deallocate(p);
+ p->~U();
}
- template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2>
- friend bool operator==(const arena_allocator<AR1, T1, A1>& lhs,
- const arena_allocator<AR2, T2, A2>& rhs) noexcept;
+ template <class AR1, class T1, std::size_t A1, bool HSA1, class AR2, class T2, std::size_t A2, bool HSA2>
+ friend bool operator==(const arena_allocator<AR1, T1, A1, HSA1>& lhs, // NOLINT
+ const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept;
+
+ template <class AU1, class U1, std::size_t A1, bool HSA1>
+ friend class arena_allocator;
- template <class AU, class U, std::size_t A> friend class arena_allocator;
private:
arena_type &m_a;
};
- template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2>
- inline bool operator==(const arena_allocator<AR1, T1, A1>& lhs,
- const arena_allocator<AR2, T2, A2>& rhs) noexcept
+ template <class AR1, class T1, std::size_t A1, bool HSA1, class AR2, class T2, std::size_t A2, bool HSA2>
+ inline bool operator==(const arena_allocator<AR1, T1, A1, HSA1>& lhs,
+ const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept
{
return A1 == A2 && rhs.m_a == lhs.m_a;
}
- template <class AR1, class T1, std::size_t A1, class AR2, class T2, std::size_t A2>
- inline bool operator!=(const arena_allocator<AR1, T1, A1>& lhs,
- const arena_allocator<AR2, T2, A2>& rhs) noexcept
+ template <class AR1, class T1, std::size_t A1, bool HSA1,class AR2, class T2, std::size_t A2, bool HSA2>
+ inline bool operator!=(const arena_allocator<AR1, T1, A1, HSA1>& lhs,
+ const arena_allocator<AR2, T2, A2, HSA2>& rhs) noexcept
{
return !(lhs == rhs);
}
@@ -255,140 +328,376 @@ namespace plib {
// Memory allocation
//============================================================
- struct aligned_arena
+ //template <typename P, std::size_t MINALIGN, bool HSD, bool HSA>
+ //struct arena_base;
+
+ template <typename P, std::size_t MINALIGN, bool HSD, bool HSA>
+ struct arena_core
{
- static constexpr const bool is_stateless = true;
- template <class T, std::size_t ALIGN = alignof(T)>
- using allocator_type = arena_allocator<aligned_arena, T, ALIGN>;
+ static constexpr const bool has_static_deallocator = HSD;
+ static constexpr const bool has_static_allocator = HSA;
+ static constexpr const std::size_t min_align = MINALIGN;
+ using size_type = std::size_t;
+
+ template <class T, size_type ALIGN = 0>
+ using allocator_type = arena_allocator<P, T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN, HSA>;
+
+ template <class T, size_type ALIGN = 0>
+ using deleter_type = arena_deleter<P, T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>;
+
+ template <typename T, size_type ALIGN = 0>
+ using unique_ptr = std::unique_ptr<T, deleter_type<T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>>;
- template <typename T>
- using owned_pool_ptr = plib::owned_ptr<T, arena_deleter<aligned_arena, T>>;
+ template <typename T, size_type ALIGN = 0>
+ using owned_ptr = plib::owned_ptr<T, deleter_type<T, (ALIGN < MINALIGN) ? MINALIGN : ALIGN>>;
- static inline aligned_arena &instance()
+ static P &instance() noexcept;
+
+ template <class T, size_type ALIGN = 0>
+ allocator_type<T, ALIGN> get_allocator()
{
- static aligned_arena s_arena;
- return s_arena;
+ return *static_cast<P *>(this);
}
- static inline void *allocate( size_t alignment, size_t size )
+ protected:
+ size_t m_stat_cur_alloc = 0;
+ size_t m_stat_max_alloc = 0;
+
+ };
+
+ template <typename P, std::size_t MINALIGN, bool HSD, bool HSA>
+ inline P & arena_core<P, MINALIGN, HSD, HSA>::instance() noexcept
+ {
+ static P s_arena;
+ return s_arena;
+ }
+
+ template <typename P, std::size_t MINALIGN, bool HSD, bool HSA>
+ struct arena_base : public arena_core<P, MINALIGN, HSD, HSA>
+ {
+ using base_type = arena_core<P, MINALIGN, HSD, HSA>;
+ using size_type = typename base_type::size_type;
+
+ ~arena_base()
{
- #if (USE_ALIGNED_ALLOCATION)
- #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
- return _aligned_malloc(size, alignment);
- #elif defined(__APPLE__)
- void* p;
- if (::posix_memalign(&p, alignment, size) != 0) {
- p = nullptr;
- }
- return p;
- #else
- return aligned_alloc(alignment, size);
- #endif
- #else
- unused_var(alignment);
- return ::operator new(size);
- #endif
+ //printf("%s %lu %lu %lu\n", typeid(*this).name(), MINALIGN, cur_alloc(), max_alloc());
}
- static inline void deallocate( void *ptr )
+ static size_type cur_alloc() noexcept { return base_type::instance().m_stat_cur_alloc; }
+ static size_type max_alloc() noexcept { return base_type::instance().m_stat_max_alloc; }
+
+ static inline void inc_alloc_stat(size_type size)
{
- #if (USE_ALIGNED_ALLOCATION)
+ auto &i = base_type::instance();
+ i.m_stat_cur_alloc += size;
+ if (i.m_stat_max_alloc <i.m_stat_cur_alloc)
+ i.m_stat_max_alloc = i.m_stat_cur_alloc;
+ }
+ static inline void dec_alloc_stat(size_type size)
+ {
+ base_type::instance().m_stat_cur_alloc -= size;
+ }
+ };
+
+ template <typename P, std::size_t MINALIGN>
+ struct arena_base<P, MINALIGN, false, false> : public arena_core<P, MINALIGN, false, false>
+ {
+ using size_type = typename arena_core<P, MINALIGN, false, false>::size_type;
+
+ ~arena_base()
+ {
+ //printf("%s %lu %lu %lu\n", typeid(*this).name(), MINALIGN, cur_alloc(), max_alloc());
+ }
+
+ size_type cur_alloc() const noexcept { return this->m_stat_cur_alloc; }
+ size_type max_alloc() const noexcept { return this->m_stat_max_alloc; }
+
+ inline void inc_alloc_stat(size_type size)
+ {
+ this->m_stat_cur_alloc += size;
+ if (this->m_stat_max_alloc < this->m_stat_cur_alloc)
+ this->m_stat_max_alloc = this->m_stat_cur_alloc;
+ }
+ inline void dec_alloc_stat(size_type size)
+ {
+ this->m_stat_cur_alloc -= size;
+ }
+ };
+
+ template <std::size_t MINALIGN>
+ struct aligned_arena : public arena_base<aligned_arena<MINALIGN>, MINALIGN, true, true>
+ {
+ using base_type = arena_base<aligned_arena<MINALIGN>, MINALIGN, true, true>;
+
+ static inline gsl::owner<void *> allocate( size_t alignment, size_t size )
+ {
+ base_type::inc_alloc_stat(size);
+#if 0
+ #if (PUSE_ALIGNED_ALLOCATION)
+ #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
+ return _aligned_malloc(size, alignment);
+ #elif defined(__APPLE__) || defined(__ANDROID__)
+ void* p;
+ if (::posix_memalign(&p, alignment, size) != 0) {
+ p = nullptr;
+ }
+ return p;
+ #else
+ // see https://en.cppreference.com/w/c/memory/aligned_alloc
+ size = ((size + alignment - 1) / alignment) * alignment;
+ return static_cast<gsl::owner<void *>>(aligned_alloc(alignment, size));
+ #endif
+ #else
+ unused_var(alignment);
+ return ::operator new(size);
+ #endif
+#else
+ return ::operator new(size, std::align_val_t(alignment));
+#endif
+ }
+
+ static inline void deallocate(gsl::owner<void *> ptr, [[maybe_unused]] size_t alignment, size_t size ) noexcept
+ {
+ //unused_var(size);
+ base_type::dec_alloc_stat(size);
+#if 0
+ #if (PUSE_ALIGNED_ALLOCATION)
+ #if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
- free(ptr);
+ _aligned_free(ptr);
+ #else
+ // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
+ ::free(ptr);
+ #endif
#else
::operator delete(ptr);
#endif
+#else
+ ::operator delete(ptr, std::align_val_t(alignment));
+#endif
}
-#if 0
- template<typename T, typename... Args>
- owned_pool_ptr<T> make_poolptr(Args&&... args)
+ bool operator ==([[maybe_unused]] const aligned_arena &rhs) const noexcept
{
- auto *mem = allocate(alignof(T), sizeof(T));
- return owned_pool_ptr<T>(new (mem) T(std::forward<Args>(args)...), true, arena_deleter<aligned_arena, T>(*this));
+ return true;
}
-#endif
- template<typename T, typename... Args>
- owned_pool_ptr<T> make_poolptr(Args&&... args)
+
+ };
+
+ struct std_arena : public arena_base<std_arena, 0, true, true>
+ {
+ static inline void *allocate(size_t alignment, size_t size )
{
- auto *mem = allocate(alignof(T), sizeof(T));
+ inc_alloc_stat(size);
+ return ::operator new(size, static_cast<std::align_val_t>(alignment));
+ }
+
+ static inline void deallocate( void *ptr, size_t alignment, size_t size ) noexcept
+ {
+ dec_alloc_stat(size);
+ ::operator delete(ptr, static_cast<std::align_val_t>(alignment));
+ }
+
+ bool operator ==([[maybe_unused]] const std_arena &rhs) const noexcept
+ {
+ return true;
+ }
+ };
+
+ namespace detail
+ {
+ ///
+ /// \brief Create new object T with an aligned memory
+ ///
+ /// The create object can be deallocate using \ref free or
+ /// using the arena_deleter type. This is the specialization for arenas
+ /// which have no state.
+ ///
+ /// \tparam T Object type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam ARENA Arena type
+ /// \tparam Args Argument types
+ ///
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, std::size_t ALIGN, typename ARENA, typename... Args>
+ static inline T * alloc(Args&&... args)
+ {
+ //using alloc_type = typename ARENA :: template allocator_type<T, alignof(T)>;
+ auto *mem = ARENA::allocate(ALIGN ? ALIGN : alignof(T), sizeof(T));
try
{
- auto *mema = new (mem) T(std::forward<Args>(args)...);
- return owned_pool_ptr<T>(mema, true, arena_deleter<aligned_arena, T>(*this));
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
+ return new (mem) T(std::forward<Args>(args)...);
}
catch (...)
{
- deallocate(mem);
+ ARENA::deallocate(mem, ALIGN ? ALIGN : alignof(T), sizeof(T));
throw;
}
}
- };
-
- template <typename T, std::size_t ALIGN>
- /*inline */ C14CONSTEXPR T *assume_aligned_ptr(T *p) noexcept
- {
- static_assert(ALIGN >= alignof(T), "Alignment must be greater or equal to alignof(T)");
- static_assert(is_pow2(ALIGN), "Alignment must be a power of 2");
- //auto t = reinterpret_cast<std::uintptr_t>(p);
- //if (t & (ALIGN-1))
- // printf("alignment error!");
-#if (USE_ALIGNED_HINTS)
- return reinterpret_cast<T *>(__builtin_assume_aligned(p, ALIGN));
-#else
- return p;
-#endif
- }
+ template<std::size_t ALIGN, typename ARENA, typename T>
+ static inline void free(T *ptr) noexcept
+ {
+ ptr->~T();
+ ARENA::deallocate(ptr, ALIGN ? ALIGN : alignof(T), sizeof(T));
+ }
- template <typename T, std::size_t ALIGN>
- constexpr const T *assume_aligned_ptr(const T *p) noexcept
- {
- static_assert(ALIGN >= alignof(T), "Alignment must be greater or equal to alignof(T)");
- static_assert(is_pow2(ALIGN), "Alignment must be a power of 2");
-#if (USE_ALIGNED_HINTS)
- return reinterpret_cast<const T *>(__builtin_assume_aligned(p, ALIGN));
-#else
- return p;
-#endif
- }
+ ///
+ /// \brief Create new object T with an aligned memory
+ ///
+ /// The create object can be deallocate using \ref free or
+ /// using the arena_deleter type. This is the specialization for arenas
+ /// which do have state.
+ ///
+ /// \tparam T Object type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam ARENA Arena type
+ /// \tparam Args Argument types
+ ///
+ /// \param arena Arena to provide memory
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, std::size_t ALIGN, typename ARENA, typename... Args>
+ static inline T * alloc(ARENA &arena, Args&&... args)
+ {
+ auto *mem = arena.allocate(ALIGN ? ALIGN : alignof(T), sizeof(T));
+ try
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
+ return new (mem) T(std::forward<Args>(args)...);
+ }
+ catch (...)
+ {
+ arena.deallocate(mem, ALIGN ? ALIGN : alignof(T), sizeof(T));
+ throw;
+ }
+ }
- // FIXME: remove
- template<typename T, typename... Args>
- inline T *pnew(Args&&... args)
+ template<std::size_t ALIGN, typename ARENA, typename T>
+ static inline void free(ARENA &arena, T *ptr) noexcept
+ {
+ ptr->~T();
+ arena.deallocate(ptr, ALIGN ? ALIGN : alignof(T), sizeof(T));
+ }
+ } // namespace detail
+
+
+ ///
+ /// \brief Create new alignment and size aware std::unique_ptr
+ ///
+ /// `make_unique` creates a new shared pointer to the object it creates.
+ /// These version ensure that on deallocation the correct alignment and
+ /// size is used. Should the unique_ptr be down casted the deleter objects
+ /// used here track the size and alignment of the object created.
+ ///
+ /// std::standard_delete will use size and alignment of the base class.
+ ///
+ /// This function is deprecated since it hides the use of arenas.
+ ///
+ /// \tparam T Object type
+ /// \tparam ARENA Arena type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam Args Argument types
+ ///
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args>
+ //[[deprecated]]
+ std::enable_if_t<ARENA::has_static_allocator, typename ARENA::template unique_ptr<T, ALIGN>>
+ make_unique(Args&&... args)
{
- auto *p = aligned_arena::allocate(alignof(T), sizeof(T));
- return new(p) T(std::forward<Args>(args)...);
+ using up_type = typename ARENA::template unique_ptr<T, ALIGN>;
+ using deleter_type = typename ARENA::template deleter_type<T, ALIGN>;
+ auto *mem = detail::alloc<T, ALIGN, ARENA>(std::forward<Args>(args)...);
+ return up_type(mem, deleter_type());
}
- template<typename T>
- inline void pdelete(T *ptr)
+ ///
+ /// \brief Create new alignment and size aware std::unique_ptr
+ ///
+ /// `make_unique` creates a new shared pointer to the object it creates.
+ /// These version ensure that on deallocation the correct alignment and
+ /// size is used. Should the unique_ptr be down casted the deleter objects
+ /// used here track the size and alignment of the object created.
+ ///
+ /// std::standard_delete will use size and alignment of the base class.
+ ///
+ /// \tparam T Object type
+ /// \tparam ARENA Arena type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam Args Argument types
+ ///
+ /// \param arena Arena to provide memory
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args>
+ typename ARENA::template unique_ptr<T, ALIGN>
+ make_unique(ARENA &arena, Args&&... args)
{
- ptr->~T();
- aligned_arena::deallocate(ptr);
+ using up_type = typename ARENA::template unique_ptr<T, ALIGN>;
+ using deleter_type = typename ARENA::template deleter_type<T, ALIGN>;
+ auto *mem = detail::alloc<T, ALIGN>(arena, std::forward<Args>(args)...);
+ return up_type(mem, deleter_type(&arena));
}
-
- template <typename T>
- using unique_ptr = std::unique_ptr<T, arena_deleter<aligned_arena, T>>;
-
- template<typename T, typename... Args>
- plib::unique_ptr<T> make_unique(Args&&... args)
+ ///
+ /// \brief Create new alignment and size aware plib::owned_ptr
+ ///
+ /// `make_unique` creates a new shared pointer to the object it creates.
+ /// These version ensure that on deallocation the correct alignment and
+ /// size is used. Should the unique_ptr be down casted the deleter objects
+ /// used here track the size and alignment of the object created.
+ ///
+ /// std::standard_delete will use size and alignment of the base class.
+ ///
+ /// This function is deprecated since it hides the use of arenas.
+ ///
+ /// \tparam T Object type
+ /// \tparam ARENA Arena type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam Args Argument types
+ ///
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args>
+ //[[deprecated]]
+ std::enable_if_t<ARENA::has_static_allocator, typename ARENA::template owned_ptr<T, ALIGN>>
+ make_owned(Args&&... args)
{
- return plib::unique_ptr<T>(pnew<T>(std::forward<Args>(args)...));
+ using op_type = typename ARENA::template owned_ptr<T, ALIGN>;
+ using deleter_type = typename ARENA::template deleter_type<T, ALIGN>;
+ auto *mem = detail::alloc<T, ALIGN, ARENA>(std::forward<Args>(args)...);
+ return op_type(mem, true, deleter_type());
}
-#if 0
- template<typename T, typename... Args>
- static owned_ptr<T> make_owned(Args&&... args)
+ ///
+ /// \brief Create new alignment and size aware plib::owned_ptr
+ ///
+ /// `make_unique` creates a new shared pointer to the object it creates.
+ /// These version ensure that on deallocation the correct alignment and
+ /// size is used. Should the unique_ptr be down casted the deleter objects
+ /// used here track the size and alignment of the object created.
+ ///
+ /// std::standard_delete will use size and alignment of the base class.
+ ///
+ /// \tparam T Object type
+ /// \tparam ARENA Arena type
+ /// \tparam ALIGN Alignment of object to be created. If ALIGN equals 0, alignof(T) is used.
+ /// \tparam Args Argument types
+ ///
+ /// \param arena Arena to provide memory
+ /// \param args Arguments to be passed to constructor
+ ///
+ template<typename T, typename ARENA, std::size_t ALIGN = 0, typename... Args>
+ typename ARENA::template owned_ptr<T> make_owned(ARENA &arena, Args&&... args)
{
- return owned_ptr<T>(pnew<T>(std::forward<Args>(args)...), true);
+ using op_type = typename ARENA::template owned_ptr<T, ALIGN>;
+ using deleter_type = typename ARENA::template deleter_type<T, ALIGN>;
+ auto *mem = detail::alloc<T, ALIGN>(arena, std::forward<Args>(args)...);
+ return op_type(mem, true, deleter_type(&arena));
}
-#endif
-
-
- template <class T, std::size_t ALIGN = alignof(T)>
- using aligned_allocator = aligned_arena::allocator_type<T, ALIGN>;
//============================================================
// traits to determine alignment size and stride size
@@ -397,55 +706,103 @@ namespace plib {
PDEFINE_HAS_MEMBER(has_align, align_size);
- template <typename T, typename X = void>
- struct align_traits
+ template <typename T, bool X>
+ struct align_traits_base
{
+ static_assert(!has_align<T>::value, "no align");
static constexpr const std::size_t align_size = alignof(std::max_align_t);
static constexpr const std::size_t value_size = sizeof(typename T::value_type);
static constexpr const std::size_t stride_size = lcm(align_size, value_size) / value_size;
};
template <typename T>
- struct align_traits<T, typename std::enable_if<has_align<T>::value, void>::type>
+ struct align_traits_base<T, true>
{
+ static_assert(has_align<T>::value, "no align");
static constexpr const std::size_t align_size = T::align_size;
static constexpr const std::size_t value_size = sizeof(typename T::value_type);
static constexpr const std::size_t stride_size = lcm(align_size, value_size) / value_size;
};
- //============================================================
- // Aligned vector
- //============================================================
-
- // FIXME: needs a separate file
- template <class T, std::size_t ALIGN = alignof(T)>
- class aligned_vector : public std::vector<T, aligned_allocator<T, ALIGN>>
+ template <typename T>
+ struct align_traits : public align_traits_base<T, has_align<T>::value>
+ {};
+
+ ///
+ /// \brief Force BASEARENA to align memory allocations on page boundaries
+ ///
+ /// \tparam BASEARENA The base arena to use (optional, defaults to aligned_arena)
+ /// \tparam PG_SIZE The page size to use (optional, defaults to 1024)
+ ///
+ template <template<std::size_t> class BASEARENA = aligned_arena, std::size_t PG_SIZE = 1024>
+ using paged_arena = BASEARENA<PG_SIZE>;
+
+ ///
+ /// \brief Helper class to create arena versions of standard library sequences
+ ///
+ /// \ref arena_vector on how to use this class
+ ///
+ /// \tparam A Arena typeThe base arena to use (optional, defaults to aligned_arena)
+ /// \tparam T Object type of objects in sequence
+ /// \tparam S Sequence, e.g. std::vector, std::list
+ /// \tparam ALIGN Alignment to use
+ ///
+ template <typename A, typename T, template <typename, typename> class S, std::size_t ALIGN = PALIGN_VECTOROPT>
+ class arena_sequence : public S<T, typename A::template allocator_type<T, ALIGN>>
{
public:
- using base = std::vector<T, aligned_allocator<T, ALIGN>>;
+ using arena_allocator_type = typename A::template allocator_type<T, ALIGN>;
+ using arena_sequence_base = S<T, arena_allocator_type>;
+ using arena_sequence_base::arena_sequence_base;
- using reference = typename base::reference;
- using const_reference = typename base::const_reference;
- using pointer = typename base::pointer;
- using const_pointer = typename base::const_pointer;
- using size_type = typename base::size_type;
+ using size_type = typename arena_sequence_base::size_type;
- using base::base;
-
- C14CONSTEXPR reference operator[](size_type i) noexcept
+ arena_sequence(A &arena)
+ : arena_sequence_base(seq_alloc(arena))
{
- return assume_aligned_ptr<T, ALIGN>(&(base::operator[](0)))[i];
}
- constexpr const_reference operator[](size_type i) const noexcept
+
+ arena_sequence(A &arena, size_type n)
+ : arena_sequence_base(n, seq_alloc(arena))
{
- return assume_aligned_ptr<T, ALIGN>(&(base::operator[](0)))[i];
}
- pointer data() noexcept { return assume_aligned_ptr<T, ALIGN>(base::data()); }
- const_pointer data() const noexcept { return assume_aligned_ptr<T, ALIGN>(base::data()); }
+ private:
+ arena_allocator_type seq_alloc(A &arena) const { return arena.template get_allocator<T, ALIGN>(); }
+ };
+
+ ///
+ /// \brief Vector with arena allocations
+ ///
+ /// The Vector allocation will use the arena of type A of which an instance
+ /// has to be passed to the constructor. Should the minimum alignment exceed
+ /// the object size, min_align / sizeof(T) elements are reserved.
+ ///
+ /// \tparam A Arena type
+ /// \tparam T Object type of objects in sequence
+ /// \tparam ALIGN Alignment to use
+ ///
+ template <typename A, typename T, std::size_t ALIGN = PALIGN_VECTOROPT>
+ class arena_vector : public arena_sequence<A, T, std::vector, ALIGN>
+ {
+ public:
+ using arena_vector_base = arena_sequence<A, T, std::vector, ALIGN>;
+ using arena_vector_base::arena_vector_base;
+
+ ///
+ /// \brief Constructor
+ ///
+ /// \param arena Arena instance to use
+ ///
+ arena_vector(A &arena)
+ : arena_vector_base(arena)
+ {
+ if (A::min_align / sizeof(T) > 0)
+ this->reserve(A::min_align / sizeof(T));
+ }
};
} // namespace plib
-#endif /* PALLOC_H_ */
+#endif // PALLOC_H_
diff --git a/src/lib/netlist/plib/parray.h b/src/lib/netlist/plib/parray.h
index 1be37b908cd..94bb424998c 100644
--- a/src/lib/netlist/plib/parray.h
+++ b/src/lib/netlist/plib/parray.h
@@ -1,126 +1,183 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * parray.h
- *
- */
#ifndef PARRAY_H_
#define PARRAY_H_
+///
+/// \file parray.h
+///
+
#include "palloc.h"
#include "pconfig.h"
#include "pexception.h"
+#include "pfmtlog.h"
#include <array>
#include <memory>
+#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace plib {
- template <typename FT, int SIZE>
- struct sizeabs
+ template <typename FT, int SIZE, typename ARENA>
+ struct parray_traits
{
- static constexpr std::size_t ABS() { return (SIZE < 0) ? static_cast<std::size_t>(0 - SIZE) : static_cast<std::size_t>(SIZE); }
+ static constexpr std::size_t ABS() noexcept { return (SIZE < 0) ? narrow_cast<std::size_t>(0 - SIZE) : narrow_cast<std::size_t>(SIZE); }
using container = typename std::array<FT, ABS()> ;
};
- template <typename FT>
- struct sizeabs<FT, 0>
+ template <typename FT, typename ARENA>
+ struct parray_traits<FT, 0, ARENA>
{
- static constexpr const std::size_t ABS = 0;
- using container = typename std::vector<FT, aligned_allocator<FT, PALIGN_VECTOROPT>>;
+ static constexpr std::size_t ABS() noexcept { return 0; }
+ using allocator_type = typename ARENA::template allocator_type<FT, PALIGN_VECTOROPT>;
+ //using container = typename std::vector<FT, arena_allocator<mempool, FT, 64>>;
+ using container = typename std::vector<FT, allocator_type>;
};
- /**
- * \brief Array with preallocated or dynamic allocation
- *
- * Passing SIZE > 0 has the same functionality as a std::array.
- * SIZE = 0 is pure dynamic allocation, the actual array size is passed to the
- * constructor.
- * SIZE < 0 reserves std::abs(SIZE) elements statically in place allocated. The
- * actual size is passed in by the constructor.
- * This array is purely intended for HPC application where depending on the
- * architecture a preference dynamic/static has to be made.
- *
- * This struct is not intended to be a full replacement to std::array.
- * It is a subset to enable switching between dynamic and static allocation.
- * I consider > 10% performance difference to be a use case.
- */
-
- template <typename FT, int SIZE>
+ /// \brief Array with preallocated or dynamic allocation.
+ ///
+ /// Passing SIZE > 0 has the same functionality as a std::array.
+ /// SIZE = 0 is pure dynamic allocation, the actual array size is passed to the
+ /// constructor.
+ /// SIZE < 0 reserves abs(SIZE) elements statically in place allocated. The
+ /// actual size is passed in by the constructor.
+ /// This array is purely intended for HPC application where depending on the
+ /// architecture a preference dynamic/static has to be made.
+ ///
+ /// This struct is not intended to be a full replacement to std::array.
+ /// It is a subset to enable switching between dynamic and static allocation.
+ /// I consider > 10% performance difference to be a use case.
+ ///
+
+ template <typename FT, int SIZE, typename ARENA = aligned_arena<>>
struct parray
{
public:
- static constexpr std::size_t SIZEABS() { return sizeabs<FT, SIZE>::ABS(); }
+ static constexpr std::size_t SIZEABS() noexcept { return parray_traits<FT, SIZE, ARENA>::ABS(); }
- using base_type = typename sizeabs<FT, SIZE>::container;
+ using base_type = typename parray_traits<FT, SIZE, ARENA>::container;
using size_type = typename base_type::size_type;
- using reference = typename base_type::reference;
- using const_reference = typename base_type::const_reference;
- using value_type = typename base_type::value_type;
+ using value_type = FT;
+ using reference = FT &;
+ using const_reference = const FT &;
+
+ using pointer = FT *;
+ using const_pointer = const FT *;
template <int X = SIZE >
- parray(size_type size, typename std::enable_if<X==0, int>::type = 0)
+ parray(size_type size, std::enable_if_t<(X==0), int> = 0)
: m_a(size), m_size(size)
{
}
-#if 1
- /* allow construction in fixed size arrays */
template <int X = SIZE >
- parray(typename std::enable_if<(X > 0), int>::type = 0)
- : m_size(X)
+ parray(size_type size, const FT &val, std::enable_if_t<(X==0), int> = 0)
+ : m_a(size, val), m_size(size)
{
}
-#endif
+
template <int X = SIZE >
- parray(size_type size, typename std::enable_if<X!=0, int>::type = 0)
+ parray(size_type size, std::enable_if_t<(X != 0), int> = 0) noexcept(false)
: m_size(size)
{
- if (SIZE < 0 && size > SIZEABS())
- throw plib::pexception("parray: size error " + plib::to_string(size) + ">" + plib::to_string(SIZEABS()));
- else if (SIZE > 0 && size != SIZEABS())
- throw plib::pexception("parray: size error");
+ if ((SIZE < 0 && size > SIZEABS())
+ || (SIZE > 0 && size != SIZEABS()))
+ throw pexception(pfmt("parray: size error: {1} > {2}")(size, SIZE));
}
- inline size_type size() const noexcept { return SIZE <= 0 ? m_size : SIZEABS(); }
-
- constexpr size_type max_size() const noexcept { return base_type::max_size(); }
+ template <int X = SIZE >
+ parray(size_type size, const FT &val, std::enable_if_t<(X != 0), int> = 0) noexcept(false)
+ : m_size(size)
+ {
+ if ((SIZE < 0 && size > SIZEABS())
+ || (SIZE > 0 && size != SIZEABS()))
+ throw pexception(pfmt("parray: size error: {1} > {2}")(size, SIZE));
+ m_a.fill(val);
+ }
- bool empty() const noexcept { return size() == 0; }
-#if 0
- reference operator[](size_type i) /*noexcept*/
+ // allow construction in fixed size arrays
+ parray()
+ : m_size(SIZEABS())
{
- if (i >= m_size) throw plib::pexception("limits error " + to_string(i) + ">=" + to_string(m_size));
- return m_a[i];
}
- const_reference operator[](size_type i) const /*noexcept*/
+
+ parray(const parray &rhs) : m_a(rhs.m_a), m_size(rhs.m_size) {}
+ parray(parray &&rhs) noexcept : m_a(std::move(rhs.m_a)), m_size(std::move(rhs.m_size)) {}
+
+ parray &operator=(const parray &rhs) noexcept // NOLINT(bugprone-unhandled-self-assignment, cert-oop54-cpp)
{
- if (i >= m_size) throw plib::pexception("limits error " + to_string(i) + ">=" + to_string(m_size));
- return m_a[i];
+ if (this == &rhs)
+ return *this;
+
+ m_a = rhs.m_a;
+ m_size = rhs.m_size;
+ return *this;
}
-#else
- C14CONSTEXPR reference operator[](size_type i) noexcept
+
+ parray &operator=(parray &&rhs) noexcept { std::swap(m_a,rhs.m_a); std::swap(m_size, rhs.m_size); return *this; }
+
+ ~parray() noexcept = default;
+
+ constexpr base_type &as_base() noexcept { return m_a; }
+
+ constexpr size_type size() const noexcept { return SIZE <= 0 ? m_size : SIZEABS(); }
+
+ constexpr size_type max_size() const noexcept { return base_type::max_size(); }
+
+ constexpr bool empty() const noexcept { return size() == 0; }
+
+ constexpr reference operator[](size_type i) noexcept
{
- return assume_aligned_ptr<FT, PALIGN_VECTOROPT>(&m_a[0])[i];
+ return m_a[i];
}
constexpr const_reference operator[](size_type i) const noexcept
{
- return assume_aligned_ptr<FT, PALIGN_VECTOROPT>(&m_a[0])[i];
+ return m_a[i];
}
-#endif
- FT * data() noexcept { return assume_aligned_ptr<FT, PALIGN_VECTOROPT>(m_a.data()); }
- const FT * data() const noexcept { return assume_aligned_ptr<FT, PALIGN_VECTOROPT>(m_a.data()); }
+
+ constexpr pointer data() noexcept { return m_a.data(); }
+ constexpr const_pointer data() const noexcept { return m_a.data(); }
private:
PALIGNAS_VECTOROPT()
base_type m_a;
size_type m_size;
};
+
+ template <typename FT, int SIZE1, int SIZE2>
+ struct parray2D : public parray<parray<FT, SIZE2>, SIZE1>
+ {
+ public:
+
+ using size_type = std::size_t;
+ using base_type = parray<parray<FT, SIZE2>, SIZE1>;
+
+ parray2D(size_type size1, size_type size2)
+ : parray<parray<FT, SIZE2>, SIZE1>(size1)
+ {
+ if (SIZE2 <= 0)
+ {
+ for (size_type i=0; i < this->size(); i++)
+ (*this)[i] = parray<FT, SIZE2>(size2);
+ }
+ }
+
+ parray2D(const parray2D &) = default;
+ parray2D &operator=(const parray2D &) = default;
+ parray2D(parray2D &&) noexcept(std::is_nothrow_move_constructible<base_type>::value) = default;
+ parray2D &operator=(parray2D &&) noexcept(std::is_nothrow_move_assignable<base_type>::value) = default;
+
+ ~parray2D() noexcept = default;
+
+ };
+
} // namespace plib
-#endif /* PARRAY_H_ */
+
+
+#endif // PARRAY_H_
diff --git a/src/lib/netlist/plib/pchrono.cpp b/src/lib/netlist/plib/pchrono.cpp
deleted file mode 100644
index f89df7dba07..00000000000
--- a/src/lib/netlist/plib/pchrono.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-
-#include "pchrono.h"
-
-namespace plib {
-namespace chrono {
-#if defined(__x86_64__) && !defined(_clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
-
-fast_ticks::type fast_ticks::per_second()
-{
- static type persec = 0;
- if (persec == 0)
- {
- type x = 0;
- system_ticks::type t = system_ticks::start();
- system_ticks::type e;
- x = -start();
- do {
- e = system_ticks::stop();
- } while (e - t < system_ticks::per_second() / 100 );
- x += stop();
- persec = (type)(double)((double) x * (double) system_ticks::per_second() / double (e - t));
- }
- return persec;
-}
-
-#if PUSE_ACCURATE_STATS && PHAS_RDTSCP
-exact_ticks::type exact_ticks::per_second()
-{
- static type persec = 0;
- if (persec == 0)
- {
- type x = 0;
- system_ticks::type t = system_ticks::start();
- system_ticks::type e;
- x = -start();
- do {
- e = system_ticks::stop();
- } while (e - t < system_ticks::per_second() / 100 );
- x += stop();
- persec = (type)(double)((double) x * (double) system_ticks::per_second() / double (e - t));
- }
- return persec;
-}
-#endif
-
-#endif
-
-} // namespace chrono
-} // namespace plib
diff --git a/src/lib/netlist/plib/pchrono.h b/src/lib/netlist/plib/pchrono.h
index 711e8908dfd..1c2e4b13057 100644
--- a/src/lib/netlist/plib/pchrono.h
+++ b/src/lib/netlist/plib/pchrono.h
@@ -1,239 +1,278 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pchrono.h
- *
- */
#ifndef PCHRONO_H_
#define PCHRONO_H_
+///
+/// \file pchrono.h
+///
+
#include "pconfig.h"
+#include "pgsl.h"
#include "ptypes.h"
#include <chrono>
-//#include <cstdint>
namespace plib {
-namespace chrono {
- template <typename T>
- struct sys_ticks
- {
- using type = typename T::rep;
- static inline type start() { return T::now().time_since_epoch().count(); }
- static inline type stop() { return T::now().time_since_epoch().count(); }
- static inline constexpr type per_second() { return T::period::den / T::period::num; }
- };
-
- using hires_ticks = sys_ticks<std::chrono::high_resolution_clock>;
- using steady_ticks = sys_ticks<std::chrono::steady_clock>;
- using system_ticks = sys_ticks<std::chrono::system_clock>;
-
- #if defined(__x86_64__) && !defined(_clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
-
- #if PHAS_RDTSCP
- struct fast_ticks
- {
- typedef int64_t type;
- static inline type start()
- {
- int64_t v;
- __asm__ __volatile__ (
- "rdtscp;"
- "shl $32, %%rdx;"
- "or %%rdx, %%rax;"
- : "=a"(v) /* outputs */
- : /* inputs */
- : "%rcx", "%rdx" /* clobbers */
- );
- return v;
- }
- static inline type stop()
- {
- return start();
- }
- static type per_second();
- };
-
- #else
- struct fast_ticks
- {
- typedef int64_t type;
- static inline type start()
+ namespace chrono {
+ template <typename T>
+ struct sys_ticks
{
- int64_t v;
- __asm__ __volatile__ (
- "rdtsc;"
- "shl $32, %%rdx;"
- "or %%rdx, %%rax;"
- : "=a"(v) /* outputs */
- : /* inputs */
- : "%rdx" /* clobbers */
- );
- return v;
- }
- static inline type stop()
+ using type = typename T::rep;
+ static constexpr type start() noexcept { return T::now().time_since_epoch().count(); }
+ static constexpr type stop() noexcept { return T::now().time_since_epoch().count(); }
+ static constexpr type per_second() noexcept { return T::period::den / T::period::num; }
+ };
+
+ using hires_ticks = sys_ticks<std::chrono::high_resolution_clock>;
+ using steady_ticks = sys_ticks<std::chrono::steady_clock>;
+ using system_ticks = sys_ticks<std::chrono::system_clock>;
+
+ #if defined(__x86_64__) && !defined(_clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
+
+ template <typename T, typename R>
+ struct base_ticks
{
- return start();
- }
- static type per_second();
- };
-
- #endif
-
-
- /* Based on "How to Benchmark Code Execution Times on Intel?? IA-32 and IA-64
- * Instruction Set Architectures", Intel, 2010
- *
- */
- #if PUSE_ACCURATE_STATS && PHAS_RDTSCP
- /*
- * kills performance completely, but is accurate
- * cpuid serializes, but clobbers ebx and ecx
- *
- */
-
- struct exact_ticks
- {
- typedef int64_t type;
-
- static inline type start()
+ using ret_type = R;
+ static ret_type per_second() noexcept
+ {
+ static ret_type per_second = 0;
+ if (per_second == 0)
+ {
+ ret_type x = 0;
+ system_ticks::type t = system_ticks::start();
+ system_ticks::type e;
+ x = - T :: start();
+ do {
+ e = system_ticks::stop();
+ } while (e - t < system_ticks::per_second() / 100 );
+ x += T :: stop();
+ per_second = (ret_type)(double)((double) x * (double) system_ticks::per_second() / double (e - t));
+ }
+ return per_second;
+ }
+ };
+
+
+ #if PHAS_RDTSCP
+ struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
- int64_t v;
- __asm__ __volatile__ (
- "cpuid;"
- //"xor %%eax, %%eax\n\t"
- "rdtsc;"
- "shl $32, %%rdx;"
- "or %%rdx, %%rax;"
- : "=a"(v) /* outputs */
- : "a"(0x0) /* inputs */
- : "%ebx", "%ecx", "%rdx" /* clobbers*/
- );
- return v;
- }
- static inline type stop()
+ typedef int64_t type;
+ static inline type start() noexcept
+ {
+ int64_t v;
+ __asm__ __volatile__ (
+ "rdtscp;"
+ "shl $32, %%rdx;"
+ "or %%rdx, %%rax;"
+ : "=a"(v) // outputs
+ : // inputs
+ : "%rcx", "%rdx" // clobbers
+ );
+ return v;
+ }
+ static inline type stop() noexcept
+ {
+ return start();
+ }
+ };
+
+ #else
+ struct fast_ticks : public base_ticks<fast_ticks, int64_t>
{
- int64_t v;
- __asm__ __volatile__ (
- "rdtscp;"
- "shl $32, %%rdx;"
- "or %%rax, %%rdx;"
- "mov %%rdx, %%r10;"
- "xor %%eax, %%eax\n\t"
- "cpuid;"
- "mov %%r10, %%rax;"
- : "=a" (v)
- :
- : "%ebx", "%ecx", "%rdx", "%r10"
- );
- return v;
- }
-
- static type per_second();
- };
- #else
- using exact_ticks = fast_ticks;
- #endif
-
-
- #else
- using fast_ticks = hires_ticks;
- using exact_ticks = fast_ticks;
- #endif
-
- template<bool enabled_>
- struct counter
- {
- counter() : m_count(0) { }
- using type = uint_least64_t;
- type operator()() const { return m_count; }
- void inc() { ++m_count; }
- void reset() { m_count = 0; }
- constexpr static bool enabled = enabled_;
- private:
- type m_count;
- };
-
- template<>
- struct counter<false>
- {
- using type = uint_least64_t;
- constexpr type operator()() const { return 0; }
- void inc() const { }
- void reset() const { }
- constexpr static bool enabled = false;
- };
-
-
- template< typename T, bool enabled_ = true>
- struct timer
- {
- using type = typename T::type;
- using ctype = uint_least64_t;
- constexpr static bool enabled = enabled_;
-
- struct guard_t
+ typedef int64_t type;
+ static inline type start() noexcept
+ {
+ int64_t v;
+ __asm__ __volatile__ (
+ "rdtsc;"
+ "shl $32, %%rdx;"
+ "or %%rdx, %%rax;"
+ : "=a"(v) // outputs
+ : // inputs
+ : "%rdx" // clobbers
+ );
+ return v;
+ }
+ static inline type stop() noexcept
+ {
+ return start();
+ }
+ };
+
+ #endif
+
+
+ // Based on "How to Benchmark Code Execution Times on Intel?? IA-32 and IA-64
+ // Instruction Set Architectures", Intel, 2010
+
+ #if PUSE_ACCURATE_STATS && PHAS_RDTSCP
+ //
+ // kills performance completely, but is accurate
+ // `cpuid` serializes, but clobbers ebx and ecx
+ //
+
+ struct exact_ticks : public base_ticks<exact_ticks, int64_t>
{
- guard_t() = delete;
- guard_t(timer &m) noexcept : m_m(m) { m_m.m_time -= T::start(); }
- ~guard_t() { m_m.m_time += T::stop(); ++m_m.m_count; }
+ typedef int64_t type;
+
+ static inline type start() noexcept
+ {
+ int64_t v;
+ __asm__ __volatile__ (
+ "cpuid;"
+ //"xor %%eax, %%eax\n\t"
+ "rdtsc;"
+ "shl $32, %%rdx;"
+ "or %%rdx, %%rax;"
+ : "=a"(v) // outputs
+ : "a"(0x0) // inputs
+ : "%ebx", "%ecx", "%rdx" // clobbers
+ );
+ return v;
+ }
+ static inline type stop() noexcept
+ {
+ int64_t v;
+ __asm__ __volatile__ (
+ "rdtscp;"
+ "shl $32, %%rdx;"
+ "or %%rax, %%rdx;"
+ "mov %%rdx, %%r10;"
+ "xor %%eax, %%eax\n\t"
+ "cpuid;"
+ "mov %%r10, %%rax;"
+ : "=a" (v)
+ :
+ : "%ebx", "%ecx", "%rdx", "%r10"
+ );
+ return v;
+ }
+ };
+ #else
+ using exact_ticks = fast_ticks;
+ #endif
+
- COPYASSIGNMOVE(guard_t, default)
+ #else
+ using fast_ticks = hires_ticks;
+ using exact_ticks = fast_ticks;
+ #endif
+ template<bool enabled_>
+ struct counter
+ {
+ constexpr counter() : m_count(0) { }
+ using type = uint_least64_t;
+ constexpr type operator()() const noexcept { return m_count; }
+ constexpr void inc() noexcept { ++m_count; }
+ constexpr void reset() noexcept { m_count = 0; }
+ constexpr static bool enabled = enabled_;
private:
- timer &m_m;
+ type m_count;
};
- friend struct guard_t;
+ template<>
+ struct counter<false>
+ {
+ using type = uint_least64_t;
+ constexpr type operator()() const noexcept { return 0; }
+ constexpr void inc() const noexcept { }
+ constexpr void reset() const noexcept { }
+ constexpr static bool enabled = false;
+ };
+
+
+ template <typename T, bool enabled_ = true>
+ struct timer
+ {
+ using type = typename T::type;
+ using ctype = uint_least64_t;
+ constexpr static const bool enabled = enabled_;
+
+ struct guard_t
+ {
+ guard_t() = delete;
+ explicit constexpr guard_t(timer &m) noexcept : m_m(&m) { m_m->m_time -= T::start(); }
+ ~guard_t() noexcept { m_m->m_time += T::stop(); ++m_m->m_count; }
- timer() : m_time(0), m_count(0) { }
+ constexpr guard_t(const guard_t &) = default;
+ constexpr guard_t &operator=(const guard_t &) = default;
+ constexpr guard_t(guard_t &&) noexcept = default;
+ constexpr guard_t &operator=(guard_t &&) noexcept = default;
- type operator()() const { return m_time; }
+ private:
+ timer *m_m;
+ };
- void reset() { m_time = 0; m_count = 0; }
- type average() const { return (m_count == 0) ? 0 : m_time / m_count; }
- type total() const { return m_time; }
- ctype count() const { return m_count; }
+ constexpr timer() : m_time(0), m_count(0) { }
- double as_seconds() const { return static_cast<double>(total())
- / static_cast<double>(T::per_second()); }
+ constexpr type operator()() const { return m_time; }
- guard_t guard() { return guard_t(*this); }
- private:
- type m_time;
- ctype m_count;
- };
+ constexpr void reset() noexcept { m_time = 0; m_count = 0; }
+ constexpr type average() const noexcept { return (m_count == 0) ? 0 : m_time / m_count; }
+ constexpr type total() const noexcept { return m_time; }
+ constexpr ctype count() const noexcept { return m_count; }
- template<typename T>
- struct timer<T, false>
- {
- using type = typename T::type;
- using ctype = uint_least64_t;
+ template <typename S>
+ constexpr S as_seconds() const noexcept { return narrow_cast<S>(total())
+ / narrow_cast<S>(T::per_second()); }
- struct guard_t
+ constexpr guard_t guard() noexcept { return guard_t(*this); }
+
+ void stop() noexcept { m_time += T::stop(); }
+ void start() noexcept { m_time -= T::start(); }
+
+
+ private:
+ type m_time;
+ ctype m_count;
+ };
+
+ template<typename T>
+ struct timer<T, false>
{
- guard_t() = default;
- COPYASSIGNMOVE(guard_t, default)
- /* using default constructor will trigger warning on
- * unused local variable.
- */
- // NOLINTNEXTLINE(modernize-use-equals-default)
- ~guard_t() { }
+ using type = typename T::type;
+ using ctype = uint_least64_t;
+
+ struct guard_t
+ {
+ constexpr guard_t() = default;
+
+ constexpr guard_t(const guard_t &) = default;
+ constexpr guard_t &operator=(const guard_t &) = default;
+ constexpr guard_t(guard_t &&) noexcept = default;
+ constexpr guard_t &operator=(guard_t &&) noexcept = default;
+
+ // using default constructor will trigger warning on
+ // unused local variable.
+
+ // NOLINTNEXTLINE(modernize-use-equals-default)
+ ~guard_t() noexcept { }
+ };
+
+ constexpr type operator()() const noexcept { return 0; }
+ void reset() const noexcept { }
+ constexpr type average() const noexcept { return 0; }
+ constexpr type total() const noexcept { return 0; }
+ constexpr ctype count() const noexcept { return 0; }
+ template <typename S>
+ constexpr S as_seconds() const noexcept { return narrow_cast<S>(0); }
+ constexpr static bool enabled = false;
+ guard_t guard() { return guard_t(); }
};
- constexpr type operator()() const { return 0; }
- void reset() const { }
- constexpr type average() const { return 0; }
- constexpr type total() const { return 0; }
- constexpr ctype count() const { return 0; }
- constexpr double as_seconds() const { return 0.0; }
- constexpr static bool enabled = false;
- guard_t guard() { return guard_t(); }
- };
+ } // namespace chrono
+ //============================================================
+ // Performance tracking
+ //============================================================
+ template <bool enabled_>
+ using pperftime_t = plib::chrono::timer<plib::chrono::exact_ticks, enabled_>;
- } // namespace chrono
+ template <bool enabled_>
+ using pperfcount_t = plib::chrono::counter<enabled_>;
} // namespace plib
-#endif /* PCHRONO_H_ */
+#endif // PCHRONO_H_
diff --git a/src/lib/netlist/plib/pconfig.h b/src/lib/netlist/plib/pconfig.h
index 4ce5aea4cdb..2d0ab0003d2 100644
--- a/src/lib/netlist/plib/pconfig.h
+++ b/src/lib/netlist/plib/pconfig.h
@@ -1,112 +1,131 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pconfig.h
- *
- */
#ifndef PCONFIG_H_
#define PCONFIG_H_
-/*
- * Define this for more accurate measurements if you processor supports
- * RDTSCP.
- */
+///
+/// \file pconfig.h
+///
+
+/// \brief More accurate measurements the processor supports RDTSCP.
+///
#ifndef PHAS_RDTSCP
#define PHAS_RDTSCP (0)
#endif
-/*
- * Define this to use accurate timing measurements. Only works
- * if PHAS_RDTSCP == 1
- */
+/// \brief Use accurate timing measurements.
+///
+/// Only works if \ref PHAS_RDTSCP == 1
+///
#ifndef PUSE_ACCURATE_STATS
#define PUSE_ACCURATE_STATS (0)
#endif
-/*
- * Set this to one if you want to use 128 bit int for ptime.
- * This is about 5% slower on a kaby lake processor.
- */
-
-#ifndef PHAS_INT128
-#define PHAS_INT128 (0)
+/// \brief Add support for the __float128 floating point type.
+///
+#ifndef PUSE_FLOAT128
+#define PUSE_FLOAT128 (0)
#endif
-/*
- * OpenMP adds about 10% to 20% performance for analog
- * netlists like kidniki.
- */
-
-#ifndef USE_OPENMP
-#define USE_OPENMP (0)
+/// \brief Compile with support for OPENMP
+///
+/// OpenMP adds about 10% to 20% performance for analog netlists.
+///
+#ifndef PUSE_OPENMP
+#define PUSE_OPENMP (1)
#endif
-/*
- * Set this to one if you want to use aligned storage optimizations.
- */
-
-#ifndef USE_ALIGNED_OPTIMIZATIONS
-#define USE_ALIGNED_OPTIMIZATIONS (0)
+/// \brief Use aligned optimizations.
+///
+/// Set this to one if you want to use aligned storage optimizations.
+///
+#ifndef PUSE_ALIGNED_OPTIMIZATIONS
+#if defined(__EMSCRIPTEN__)
+#define PUSE_ALIGNED_OPTIMIZATIONS (0)
+#else
+#define PUSE_ALIGNED_OPTIMIZATIONS (1)
+#endif
#endif
-#define USE_ALIGNED_ALLOCATION (USE_ALIGNED_OPTIMIZATIONS)
-#define USE_ALIGNED_HINTS (USE_ALIGNED_OPTIMIZATIONS)
-/*
- * Standard alignment macros
- */
+/// \brief Use aligned allocations.
+///
+/// Set this to one if you want to use aligned storage optimizations.
+///
+/// Defaults to \ref PUSE_ALIGNED_OPTIMIZATIONS.
+///
+#define PUSE_ALIGNED_ALLOCATION (PUSE_ALIGNED_OPTIMIZATIONS)
+/// \brief Use aligned hints.
+///
+/// Some compilers support special functions to mark a pointer as being
+/// aligned. Set this to one if you want to use these functions.
+///
+/// Defaults to \ref PUSE_ALIGNED_OPTIMIZATIONS.
+///
+#define PUSE_ALIGNED_HINTS (PUSE_ALIGNED_OPTIMIZATIONS)
+
+/// \brief Number of bytes for cache line alignment
+///
#define PALIGN_CACHELINE (64)
-#define PALIGN_VECTOROPT (64)
+/// \brief Number of bytes for vector alignment
+///
+#define PALIGN_VECTOROPT (32)
+
+#define PALIGN_MIN_SIZE (16)
+
+#if (PUSE_ALIGNED_OPTIMIZATIONS)
#define PALIGNAS_CACHELINE() PALIGNAS(PALIGN_CACHELINE)
#define PALIGNAS_VECTOROPT() PALIGNAS(PALIGN_VECTOROPT)
+#else
+#define PALIGNAS_CACHELINE()
+#define PALIGNAS_VECTOROPT()
+#endif
-/* Breaks mame build on windows due to -Wattribute
- * FIXME: no error on cross-compile - need further checks */
-#if defined(_WIN32) && defined(__GNUC__)
+// FIXME: Breaks mame build on windows mingw due to `-Wattribute`
+// also triggers `-Wattribute` on ARM
+// This is fixed on mingw version 10
+// FIXME: no error on cross-compile - need further checks
+#if defined(__GNUC__) && ((defined(_WIN32) && __GNUC__ < 10) || defined(__arm__) || defined(__ARMEL__))
#define PALIGNAS(x)
#else
#define PALIGNAS(x) alignas(x)
#endif
-/*============================================================
- * Check for CPP Version
- *
- * C++11: __cplusplus is 201103L.
- * C++14: __cplusplus is 201402L.
- * c++17/c++1z__cplusplus is 201703L.
- *
- * VS2015 returns 199711L here. This is the bug filed in
- * 2012 which obviously never was picked up by MS:
- * https://connect.microsoft.com/VisualStudio/feedback/details/763051/a-value-of-predefined-macro-cplusplus-is-still-199711l
- *
- *
- *============================================================*/
-
-#ifndef NVCCBUILD
-#define NVCCBUILD (0)
-#endif
+// ============================================================
+// Check for CPP Version
+//
+// C++11: __cplusplus is 201103L.
+// C++14: __cplusplus is 201402L.
+// c++17/c++1z__cplusplus is 201703L.
+//
+// VS2015 returns 199711L here. This is the bug filed in
+// 2012 which obviously never was picked up by MS:
+// https://connect.microsoft.com/VisualStudio/feedback/details/763051/a-value-of-predefined-macro-cplusplus-is-still-199711l
+//
+//
+//============================================================
-#if NVCCBUILD
-#define C14CONSTEXPR
-#else
-#if __cplusplus == 201103L
-#define C14CONSTEXPR
+#if defined(_MSC_VER)
+ // Ok
+#elif __cplusplus == 201103L
+ #error c++11 not supported - you need c++17
#elif __cplusplus == 201402L
-#define C14CONSTEXPR constexpr
+ #error c++14 not supported - you need c++17
#elif __cplusplus == 201703L
-#define C14CONSTEXPR constexpr
-#elif defined(_MSC_VER)
-#define C14CONSTEXPR
+ // Ok
+#elif __cplusplus == 201709L
+ // Ok g++-9 -std=c++2a
+#elif __cplusplus == 202002L
+ // Ok clang++-13 -std=c++2a
#else
-#error "C++ version not supported"
-#endif
+ #error "C++ version not supported"
#endif
-#if (PHAS_INT128)
-typedef __uint128_t UINT128;
-typedef __int128_t INT128;
+
+#if (PUSE_FLOAT128)
+typedef __float128 FLOAT128;
#endif
//============================================================
@@ -114,70 +133,43 @@ typedef __int128_t INT128;
//============================================================
#if defined(OPENMP)
-#define HAS_OPENMP ( OPENMP >= 200805 )
-#elif defined(_OPENMP)
-#define HAS_OPENMP ( _OPENMP >= 200805 )
+#if ( OPENMP >= 200805 )
+#define PHAS_OPENMP (1)
#else
-#define HAS_OPENMP (0)
+#define PHAS_OPENMP (0)
#endif
-
-//============================================================
-// Pointer to Member Function
-//============================================================
-
-// This will be autodetected
-//#define PPMF_TYPE 0
-
-#define PPMF_TYPE_PMF 0
-#define PPMF_TYPE_GNUC_PMF_CONV 1
-#define PPMF_TYPE_INTERNAL 2
-
-#if defined(__GNUC__)
- /* does not work in versions over 4.7.x of 32bit MINGW */
- #if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)))
- #define PHAS_PMF_INTERNAL 0
- #elif defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
- #define PHAS_PMF_INTERNAL 1
- #define MEMBER_ABI _thiscall
- #elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
- #define PHAS_PMF_INTERNAL 0
- #elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__)
- #define PHAS_PMF_INTERNAL 2
- #else
- #define PHAS_PMF_INTERNAL 1
- #endif
-#elif defined(_MSC_VER) && defined (_M_X64)
- #define PHAS_PMF_INTERNAL 3
+#elif defined(_OPENMP)
+#if ( _OPENMP >= 200805 )
+#define PHAS_OPENMP (1)
#else
- #define PHAS_PMF_INTERNAL 0
+#define PHAS_OPENMP (0)
#endif
-
-#ifndef MEMBER_ABI
- #define MEMBER_ABI
-#endif
-
-#ifndef PPMF_TYPE
- #if (PHAS_PMF_INTERNAL > 0)
- #define PPMF_TYPE PPMF_TYPE_INTERNAL
- #else
- #define PPMF_TYPE PPMF_TYPE_PMF
- #endif
#else
- #undef PHAS_PMF_INTERNAL
- #define PHAS_PMF_INTERNAL 0
- #undef MEMBER_ABI
- #define MEMBER_ABI
+#define PHAS_OPENMP (0)
#endif
+
//============================================================
// WARNINGS
//============================================================
-#if (USE_OPENMP)
-#if (!(HAS_OPENMP))
-#error To use openmp compile and link with "-fopenmp"
+#if (PUSE_OPENMP)
+#if (!(PHAS_OPENMP))
+//#error To use openmp compile and link with "-fopenmp"
+#undef PUSE_OPENMP
+#define PUSE_OPENMP (0)
+#endif
+#endif
+
+#if (PUSE_FLOAT128)
+#if defined(__has_include)
+#if !__has_include(<quadmath.h>)
+//#pragma message "disabling PUSE_FLOAT128 due to missing quadmath.h"
+#undef PUSE_FLOAT128
+#define PUSE_FLOAT128 (0)
+#endif
#endif
#endif
-#endif /* PCONFIG_H_ */
+#endif // PCONFIG_H_
diff --git a/src/lib/netlist/plib/pdynlib.cpp b/src/lib/netlist/plib/pdynlib.cpp
index 828096d1b0c..cdc25dc71b4 100644
--- a/src/lib/netlist/plib/pdynlib.cpp
+++ b/src/lib/netlist/plib/pdynlib.cpp
@@ -1,130 +1,73 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * dynlib.c
- *
- */
#include "pdynlib.h"
#ifdef _WIN32
#include "windows.h"
-#include "palloc.h"
-
-namespace plib {
-CHAR *astring_from_utf8(const char *utf8string)
-{
- WCHAR *wstring;
- int char_count;
- CHAR *result;
-
- // convert MAME string (UTF-8) to UTF-16
- char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, nullptr, 0);
- wstring = (WCHAR *)alloca(char_count * sizeof(*wstring));
- MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, wstring, char_count);
-
- // convert UTF-16 to "ANSI code page" string
- char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, nullptr, 0, nullptr, nullptr);
- result = new CHAR[char_count];
- if (result != nullptr)
- WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, nullptr, nullptr);
-
- return result;
-}
-
-WCHAR *wstring_from_utf8(const char *utf8string)
-{
- int char_count;
- WCHAR *result;
-
- // convert MAME string (UTF-8) to UTF-16
- char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, nullptr, 0);
- result = new WCHAR[char_count];
- if (result != nullptr)
- MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count);
-
- return result;
-}
-}
-
-#ifdef UNICODE
-#define tstring_from_utf8 plib::wstring_from_utf8
-#else // !UNICODE
-#define tstring_from_utf8 plib::astring_from_utf8
-#endif // UNICODE
-
#else
#include <dlfcn.h>
#endif
-namespace plib {
-dynlib::dynlib(const pstring &libname)
-: m_isLoaded(false), m_lib(nullptr)
+#include <type_traits>
+
+namespace plib
+{
+ using winapi_string = std::conditional<compile_info::unicode::value,
+ pwstring, pu8string>::type;
+
+dynamic_library::dynamic_library(const pstring &libname)
+: m_lib(nullptr)
{
#ifdef _WIN32
//fprintf(stderr, "win: loading <%s>\n", libname.c_str());
- TCHAR *buffer = tstring_from_utf8(libname.c_str());
- if (libname != "")
- m_lib = LoadLibrary(buffer);
+ if (!libname.empty())
+ m_lib = LoadLibrary(winapi_string(putf8string(libname)).c_str());
else
m_lib = GetModuleHandle(nullptr);
- if (m_lib != nullptr)
- m_isLoaded = true;
- //else
- // fprintf(stderr, "win: library <%s> not found!\n", libname.c_str());
- delete [] buffer;
#elif defined(__EMSCRIPTEN__)
//no-op
#else
//printf("loading <%s>\n", libname.c_str());
- if (libname != "")
- m_lib = dlopen(libname.c_str(), RTLD_LAZY);
+ if (!libname.empty())
+ m_lib = dlopen(putf8string(libname).c_str(), RTLD_LAZY);
else
m_lib = dlopen(nullptr, RTLD_LAZY);
+#endif
if (m_lib != nullptr)
- m_isLoaded = true;
+ set_loaded(true);
//else
// printf("library <%s> not found: %s\n", libname.c_str(), dlerror());
-#endif
}
-dynlib::dynlib(const pstring &path, const pstring &libname)
-: m_isLoaded(false), m_lib(nullptr)
+dynamic_library::dynamic_library([[maybe_unused]] const pstring &path, const pstring &libname)
+: m_lib(nullptr)
{
// FIXME: implement path search
- plib::unused_var(path);
// printf("win: loading <%s>\n", libname.c_str());
#ifdef _WIN32
- TCHAR *buffer = tstring_from_utf8(libname.c_str());
- if (libname != "")
- m_lib = LoadLibrary(buffer);
+ if (!libname.empty())
+ m_lib = LoadLibrary(winapi_string(putf8string(libname)).c_str());
else
m_lib = GetModuleHandle(nullptr);
- if (m_lib != nullptr)
- m_isLoaded = true;
- else
- {
- //printf("win: library <%s> not found!\n", libname.c_str());
- }
- delete [] buffer;
#elif defined(__EMSCRIPTEN__)
//no-op
#else
//printf("loading <%s>\n", libname.c_str());
- if (libname != "")
- m_lib = dlopen(libname.c_str(), RTLD_LAZY);
+ if (!libname.empty())
+ m_lib = dlopen(putf8string(libname).c_str(), RTLD_LAZY);
else
m_lib = dlopen(nullptr, RTLD_LAZY);
+#endif
if (m_lib != nullptr)
- m_isLoaded = true;
+ set_loaded(true);
else
{
//printf("library <%s> not found!\n", libname.c_str());
}
-#endif
}
-dynlib::~dynlib()
+dynamic_library::~dynamic_library()
{
if (m_lib != nullptr)
{
@@ -136,17 +79,12 @@ dynlib::~dynlib()
}
}
-bool dynlib::isLoaded() const
-{
- return m_isLoaded;
-}
-
-void *dynlib::getsym_p(const pstring &name)
+void *dynamic_library::get_symbol_pointer(const pstring &name) const noexcept
{
#ifdef _WIN32
- return (void *) GetProcAddress((HMODULE) m_lib, name.c_str());
+ return (void *) GetProcAddress((HMODULE) m_lib, putf8string(name).c_str());
#else
- return dlsym(m_lib, name.c_str());
+ return dlsym(m_lib, putf8string(name).c_str());
#endif
}
diff --git a/src/lib/netlist/plib/pdynlib.h b/src/lib/netlist/plib/pdynlib.h
index 1454c053298..8d8e3377e3a 100644
--- a/src/lib/netlist/plib/pdynlib.h
+++ b/src/lib/netlist/plib/pdynlib.h
@@ -1,72 +1,131 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pdynlib.h
- */
#ifndef PDYNLIB_H_
#define PDYNLIB_H_
+///
+/// \file pdynlib.h
+///
+/// Dynamic loading of libraries
+///
+
#include "pstring.h"
#include "ptypes.h"
namespace plib {
-// ----------------------------------------------------------------------------------------
-// pdynlib: dynamic loading of libraries ...
-// ----------------------------------------------------------------------------------------
-class dynlib : public nocopyassignmove
-{
-public:
- explicit dynlib(const pstring &libname);
- dynlib(const pstring &path, const pstring &libname);
+ class dynamic_library_base
+ {
+ public:
- ~dynlib();
- COPYASSIGNMOVE(dynlib, delete)
+ explicit dynamic_library_base() : m_is_loaded(false) { }
- bool isLoaded() const;
+ virtual ~dynamic_library_base() = default;
- template <typename T>
- T getsym(const pstring &name)
- {
- return reinterpret_cast<T>(getsym_p(name));
- }
-private:
- void *getsym_p(const pstring &name);
+ dynamic_library_base(const dynamic_library_base &) = delete;
+ dynamic_library_base &operator=(const dynamic_library_base &) = delete;
- bool m_isLoaded;
- void *m_lib;
-};
+ dynamic_library_base(dynamic_library_base &&) noexcept = default;
+ dynamic_library_base &operator=(dynamic_library_base &&) noexcept = default;
-template <typename R, typename... Args>
-class dynproc
-{
-public:
- using calltype = R(*) (Args... args);
+ template <typename R, typename... Args>
+ class function
+ {
+ public:
+ using calltype = R(*) (Args... args);
- dynproc() : m_sym(nullptr) { }
+ function() : m_sym(nullptr) { }
- dynproc(dynlib &dl, const pstring &name)
- {
- m_sym = dl.getsym<calltype>(name);
- }
+ function(dynamic_library_base &dl, const pstring &name) noexcept
+ : m_sym(dl.get_symbol<calltype>(name))
+ {
+ }
- void load(dynlib &dl, const pstring &name)
- {
- m_sym = dl.getsym<calltype>(name);
- }
+ void load(dynamic_library_base &dl, const pstring &name) noexcept
+ {
+ m_sym = dl.get_symbol<calltype>(name);
+ }
+
+ R operator ()(Args&&... args) const
+ {
+ return m_sym(std::forward<Args>(args)...);
+ //return m_sym(args...);
+ }
+
+ bool resolved() const noexcept { return m_sym != nullptr; }
+ private:
+ calltype m_sym;
+ };
+
+ bool isLoaded() const { return m_is_loaded; }
- R operator ()(Args&&... args) const
+ template <typename T>
+ T get_symbol(const pstring &name) const noexcept
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast<T>(get_symbol_pointer(name));
+ }
+
+ protected:
+ void set_loaded(bool v) noexcept { m_is_loaded = v; }
+ virtual void *get_symbol_pointer(const pstring &name) const noexcept = 0;
+ private:
+ bool m_is_loaded;
+ };
+
+ class dynamic_library : public dynamic_library_base
{
- return m_sym(std::forward<Args>(args)...);
- //return m_sym(args...);
- }
+ public:
+ explicit dynamic_library(const pstring &libname);
+ dynamic_library(const pstring &path, const pstring &libname);
+
+ ~dynamic_library() override;
+
+ PCOPYASSIGN(dynamic_library, delete)
+ PMOVEASSIGN(dynamic_library, default)
- bool resolved() { return m_sym != nullptr; }
-private:
- calltype m_sym;
-};
+ protected:
+ void *get_symbol_pointer(const pstring &name) const noexcept override;
+
+ private:
+ void *m_lib;
+ };
+
+ class static_library : public dynamic_library_base
+ {
+ public:
+ struct symbol
+ {
+ const char *name;
+ void *addr;
+ };
+
+
+ explicit static_library(const symbol *symbols)
+ : m_syms(symbols)
+ {
+ if (symbols != nullptr)
+ set_loaded(true);
+ }
+
+ protected:
+ void *get_symbol_pointer(const pstring &name) const noexcept override
+ {
+ const symbol *p = m_syms;
+ while (p->name[0] != 0)
+ {
+ if (name == pstring(p->name))
+ return p->addr;
+ p++;
+ }
+ return nullptr;
+ }
+
+ private:
+ const symbol *m_syms;
+ };
} // namespace plib
-#endif /* PSTRING_H_ */
+#endif // PDYNLIB_H_
diff --git a/src/lib/netlist/plib/penum.h b/src/lib/netlist/plib/penum.h
new file mode 100644
index 00000000000..5a1db765ba0
--- /dev/null
+++ b/src/lib/netlist/plib/penum.h
@@ -0,0 +1,55 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PENUM_H_
+#define PENUM_H_
+
+///
+/// \file penum.h
+///
+
+#include "pstring.h"
+
+namespace plib
+{
+
+ ///
+ /// \brief strongly typed enumeration
+ ///
+ ///
+ struct penum_base
+ {
+ protected:
+ // Implementation in putil.cpp.
+ // Putting the code here leads to a performance decrease.
+ static int from_string_int(const pstring &str, const pstring &x);
+ static pstring nthstr(std::size_t n, const pstring &str);
+ };
+
+} // namespace plib
+
+#define PENUM(ename, ...) \
+ struct ename : public plib::penum_base { \
+ enum E { __VA_ARGS__ }; \
+ constexpr ename (const E &v) : m_v(v) { } \
+ template <typename T> explicit constexpr ename(const T &val) : m_v(static_cast<E>(val)) { } \
+ bool set_from_string (const pstring &s) { \
+ int f = from_string_int(strings(), s); \
+ if (f>=0) { m_v = static_cast<E>(f); return true; } \
+ return false;\
+ } \
+ constexpr operator E() const noexcept {return m_v;} \
+ constexpr bool operator==(const ename &rhs) const noexcept {return m_v == rhs.m_v;} \
+ constexpr bool operator==(const E &rhs) const noexcept {return m_v == rhs;} \
+ pstring name() const { \
+ return nthstr(m_v, strings()); \
+ } \
+ template <typename S> void save_state(S &saver) { saver.save_item(m_v, "m_v"); } \
+ private: E m_v; \
+ static pstring strings() {\
+ static const char * static_strings = # __VA_ARGS__; \
+ return pstring(static_strings); \
+ } \
+ };
+
+#endif // PENUM_H_
diff --git a/src/lib/netlist/plib/pexception.cpp b/src/lib/netlist/plib/pexception.cpp
index 8d6907d66f2..b358f3ad6ba 100644
--- a/src/lib/netlist/plib/pexception.cpp
+++ b/src/lib/netlist/plib/pexception.cpp
@@ -1,17 +1,14 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * palloc.c
- *
- */
#include "pexception.h"
#include "pfmtlog.h"
#include <cfenv>
+#include <cfloat>
#include <iostream>
-#if (defined(__x86_64__) || defined(__i386__)) && defined(__linux__)
+#if (defined(__x86_64__) || defined(__i386__)) && defined(__GLIBC__)
#define HAS_FEENABLE_EXCEPT (1)
#else
#define HAS_FEENABLE_EXCEPT (0)
@@ -23,12 +20,35 @@ namespace plib {
// terminate
//============================================================
- void terminate(const pstring &msg) noexcept
+ [[noreturn]] void terminate(const char *msg) noexcept
{
- std::cerr << msg.c_str() << "\n";
+ try
+ {
+ std::cerr << msg << "\n";
+ }
+ catch (...)
+ {
+ // ignore
+ }
std::terminate();
}
+ void passert_fail(const char *assertion, const char *file, int lineno, const char *msg) noexcept
+ {
+ try
+ {
+ std::cerr << file << ":" << lineno << ": ";
+ if (msg != nullptr)
+ std::cerr << msg << "\n";
+ else
+ std::cerr << "Assertion '" << assertion << "' failed.\n";
+ }
+ catch (...)
+ {
+ // ignore
+ }
+ std::terminate();
+ }
//============================================================
// Exceptions
@@ -76,34 +96,48 @@ namespace plib {
}
- fpexception_e::fpexception_e(const pstring &text)
- : pexception(pfmt("Out of memory: {}")(text))
+ fp_exception_e::fp_exception_e(const pstring &text)
+ : pexception(pfmt("Exception error: {}")(text))
{
}
- bool fpsignalenabler::m_enable = false;
+ bool fp_signal_enabler::m_enable = false; // NOLINT
+
+ //FIXME: mingw needs to be compiled with `-fnon-call-exceptions`
- fpsignalenabler::fpsignalenabler(unsigned fpexceptions)
+ fp_signal_enabler::fp_signal_enabler(unsigned fp_exceptions)
{
#if HAS_FEENABLE_EXCEPT
if (m_enable)
{
int b = 0;
- if (fpexceptions & plib::FP_INEXACT) b = b | FE_INEXACT;
- if (fpexceptions & plib::FP_DIVBYZERO) b = b | FE_DIVBYZERO;
- if (fpexceptions & plib::FP_UNDERFLOW) b = b | FE_UNDERFLOW;
- if (fpexceptions & plib::FP_OVERFLOW) b = b | FE_OVERFLOW;
- if (fpexceptions & plib::FP_INVALID) b = b | FE_INVALID;
- m_last_enabled = feenableexcept(b);
+ if (fp_exceptions & plib::FP_INEXACT) b = b | FE_INEXACT;
+ if (fp_exceptions & plib::FP_DIVBYZERO) b = b | FE_DIVBYZERO;
+ if (fp_exceptions & plib::FP_UNDERFLOW) b = b | FE_UNDERFLOW;
+ if (fp_exceptions & plib::FP_OVERFLOW) b = b | FE_OVERFLOW;
+ if (fp_exceptions & plib::FP_INVALID) b = b | FE_INVALID;
+ if ((b & m_last_enabled) != b)
+ m_last_enabled = feenableexcept(b);
+ }
+ #elif defined(_WIN32) && defined(_EM_INEXACT)
+ if (m_enable)
+ {
+ int b = _EM_DENORMAL | _EM_INEXACT | _EM_ZERODIVIDE | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID;
+ if (fp_exceptions & plib::FP_INEXACT) b &= ~_EM_INEXACT;
+ if (fp_exceptions & plib::FP_DIVBYZERO) b &= ~_EM_ZERODIVIDE;
+ if (fp_exceptions & plib::FP_UNDERFLOW) b &= ~_EM_UNDERFLOW;
+ if (fp_exceptions & plib::FP_OVERFLOW) b &= ~_EM_OVERFLOW;
+ if (fp_exceptions & plib::FP_INVALID) b &= ~_EM_INVALID;
+ m_last_enabled = _controlfp(0, 0);
+ _controlfp(b, _MCW_EM );
}
#else
m_last_enabled = 0;
#endif
}
-
- fpsignalenabler::~fpsignalenabler()
+ fp_signal_enabler::~fp_signal_enabler()
{
#if HAS_FEENABLE_EXCEPT
if (m_enable)
@@ -111,15 +145,26 @@ namespace plib {
fedisableexcept(FE_ALL_EXCEPT); // Enable all floating point exceptions but FE_INEXACT
feenableexcept(m_last_enabled); // Enable all floating point exceptions but FE_INEXACT
}
+ #elif defined(_WIN32) && defined(_EM_INEXACT)
+ if (m_enable)
+ {
+ _controlfp(m_last_enabled, _MCW_EM);
+ }
#endif
}
- bool fpsignalenabler::supported()
+ bool fp_signal_enabler::supported()
{
+ #if HAS_FEENABLE_EXCEPT
+ return true;
+ #elif defined(_WIN32) && defined(_EM_INEXACT)
return true;
+ #else
+ return false;
+ #endif
}
- bool fpsignalenabler::global_enable(bool enable)
+ bool fp_signal_enabler::global_enable(bool enable)
{
bool old = m_enable;
m_enable = enable;
diff --git a/src/lib/netlist/plib/pexception.h b/src/lib/netlist/plib/pexception.h
index 28a3ac1adf1..bf2a286e81e 100644
--- a/src/lib/netlist/plib/pexception.h
+++ b/src/lib/netlist/plib/pexception.h
@@ -1,29 +1,34 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * palloc.h
- *
- */
#ifndef PEXCEPTION_H_
#define PEXCEPTION_H_
+///
+/// \file: pexception.h
+///
+
#include "pstring.h"
#include "ptypes.h"
#include <exception>
+#define passert_always(expr) \
+ ((expr) ? static_cast<void>(0) : plib::passert_fail (#expr, __FILE__, __LINE__, nullptr))
+
+#define passert_always_msg(expr, msg) \
+ ((expr) ? static_cast<void>(0) : plib::passert_fail (#expr, __FILE__, __LINE__, msg))
+
namespace plib {
- //============================================================
- // terminate
- //============================================================
+ /// \brief Terminate the program.
+ ///
+ /// \note could be enhanced by setting a termination handler
+ ///
+ [[noreturn]] void terminate(const char *msg) noexcept;
- /*! Terminate the program
- *
- * \note could be enhanced by setting a termination handler
- */
- [[noreturn]] void terminate(const pstring &msg) noexcept;
+ [[noreturn]] void passert_fail(const char *assertion,
+ const char *file, int lineno, const char *msg) noexcept;
//============================================================
// exception base
@@ -34,11 +39,11 @@ namespace plib {
public:
explicit pexception(const pstring &text);
- const pstring &text() { return m_text; }
+ const putf8string &text() const noexcept { return m_text; }
const char* what() const noexcept override { return m_text.c_str(); }
private:
- pstring m_text;
+ putf8string m_text;
};
class file_e : public plib::pexception
@@ -77,14 +82,14 @@ namespace plib {
explicit out_of_mem_e(const pstring &location);
};
- /* FIXME: currently only a stub for later use. More use could be added by
- * using “-fnon-call-exceptions" and sigaction to enable c++ exception supported.
- */
+ // FIXME: currently only a stub for later use. More use could be added by
+ // using `-fnon-call-exceptions` and sigaction to enable c++ exception supported.
+ //
- class fpexception_e : public pexception
+ class fp_exception_e : public pexception
{
public:
- explicit fpexception_e(const pstring &text);
+ explicit fp_exception_e(const pstring &text);
};
static constexpr unsigned FP_INEXACT = 0x0001;
@@ -94,31 +99,36 @@ namespace plib {
static constexpr unsigned FP_INVALID = 0x00010;
static constexpr unsigned FP_ALL = 0x0001f;
- /*
- * Catch SIGFPE on linux for debugging purposes.
- */
-
- class fpsignalenabler
+ /// \brief Catch SIGFPE on linux for debugging purposes.
+ ///
+ class fp_signal_enabler
{
public:
- explicit fpsignalenabler(unsigned fpexceptions);
+ explicit fp_signal_enabler(unsigned fp_exceptions);
- COPYASSIGNMOVE(fpsignalenabler, delete)
+ PCOPYASSIGNMOVE(fp_signal_enabler, delete)
- ~fpsignalenabler();
+ ~fp_signal_enabler();
- /* is the functionality supported ? */
+ /// \brief is the functionality supported.
+ ///
+ /// \return current status
+ ///
static bool supported();
- /* returns last global enable state */
+ /// \brief get/set global enable status
+ ///
+ /// \param enable new status
+ /// \return returns last global enable state
+ ///
static bool global_enable(bool enable);
private:
int m_last_enabled;
- static bool m_enable;
+ static bool m_enable; // NOLINT
};
} // namespace plib
-#endif /* PEXCEPTION_H_ */
+#endif // PEXCEPTION_H_
diff --git a/src/lib/netlist/plib/pfmtlog.cpp b/src/lib/netlist/plib/pfmtlog.cpp
index ef12e05fb1d..188c04f4cf7 100644
--- a/src/lib/netlist/plib/pfmtlog.cpp
+++ b/src/lib/netlist/plib/pfmtlog.cpp
@@ -1,16 +1,13 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nl_string.c
- *
- */
#include "pfmtlog.h"
#include "palloc.h"
+#include "pstonum.h"
+#include "pstrutil.h"
#include <algorithm>
#include <array>
-#include <cmath>
#include <iomanip>
#include <iostream>
@@ -42,14 +39,15 @@ private:
};
#endif
-pfmt::rtype pfmt::setfmt(std::stringstream &strm, char32_t cfmt_spec)
+pfmt::rtype pfmt::set_format(std::stringstream &strm, char32_t char_format)
{
pstring fmt;
pstring search("{");
search += plib::to_string(m_arg);
+
rtype r;
- r.sl = search.size();
+ r.sl = search.length();
r.p = m_str.find(search + ':');
r.sl++; // ":"
if (r.p == pstring::npos) // no further specifiers
@@ -92,33 +90,40 @@ pfmt::rtype pfmt::setfmt(std::stringstream &strm, char32_t cfmt_spec)
if (r.p != pstring::npos)
{
// a.b format here ...
- if (fmt != "" && pstring("duxofge").find(static_cast<pstring::value_type>(cfmt_spec)) != pstring::npos)
+ char32_t pend(0);
+ int width(0);
+ if (!fmt.empty() && pstring("duxofge").find(static_cast<pstring::value_type>(char_format)) != pstring::npos)
{
- r.pend = static_cast<char32_t>(fmt.at(fmt.size() - 1));
- if (pstring("duxofge").find(static_cast<pstring::value_type>(r.pend)) == pstring::npos)
- r.pend = cfmt_spec;
+ //pend = static_cast<char32_t>(fmt.at(fmt.size() - 1));
+ pend = plib::right(fmt, 1).at(0);
+ if (pstring("duxofge").find(static_cast<pstring::value_type>(pend)) == pstring::npos)
+ pend = char_format;
else
- fmt = plib::left(fmt, fmt.size() - 1);
+ fmt = plib::left(fmt, fmt.length() - 1);
}
else
// FIXME: Error
- r.pend = cfmt_spec;
+ pend = char_format;
auto pdot(fmt.find('.'));
if (pdot==0)
- strm << std::setprecision(pstonum<int>(fmt.substr(1)));
+ strm << std::setprecision(pstonum_ne_def<int>(fmt.substr(1), 6));
else if (pdot != pstring::npos)
{
- //strm << std::setprecision(pstonum<int>(fmt.substr(pdot + 1))) << std::setw(pstonum<int>(left(fmt,pdot)));
- strm << std::setprecision(pstonum<int>(fmt.substr(pdot + 1)));
- r.width = pstonum<pstring::size_type>(left(fmt,pdot));
+ strm << std::setprecision(pstonum_ne_def<int>(fmt.substr(pdot + 1), 6));
+ width = pstonum_ne_def<int>(left(fmt,pdot), 0);
}
- else if (fmt != "")
- //strm << std::setw(pstonum<int>(fmt));
- r.width = pstonum<pstring::size_type>(fmt);
+ else if (!fmt.empty())
+ width = pstonum_ne_def<int>(fmt, 0);
+
+ auto aw(plib::abs(width));
+
+ strm << std::setw(aw);
+ if (width < 0)
+ strm << std::left;
- switch (r.pend)
+ switch (pend)
{
case 'x':
strm << std::hex;
diff --git a/src/lib/netlist/plib/pfmtlog.h b/src/lib/netlist/plib/pfmtlog.h
index 531b76fafe7..2e45498c637 100644
--- a/src/lib/netlist/plib/pfmtlog.h
+++ b/src/lib/netlist/plib/pfmtlog.h
@@ -1,372 +1,483 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pfmtlog.h
- */
-#ifndef PFMT_H_
-#define PFMT_H_
+///
+/// \file pfmtlog.h
+///
+#ifndef PFMTLOG_H_
+#define PFMTLOG_H_
+
+#include "penum.h"
+#include "pgsl.h"
+#include "ppmf.h"
#include "pstring.h"
#include "ptypes.h"
-#include "putil.h"
#include <limits>
#include <locale>
#include <sstream>
+#define PERRMSGV(name, argument_count, str) \
+ struct name : public plib::perrmsg \
+ { \
+ template<typename... Args> explicit name(Args&&... args) \
+ : plib::perrmsg(str, std::forward<Args>(args)...) \
+ { static_assert((argument_count) == sizeof...(args), "Argument count mismatch"); } \
+ };
+
namespace plib {
-P_ENUM(plog_level,
- DEBUG,
- VERBOSE,
- INFO,
- WARNING,
- ERROR,
- FATAL)
-
-template <typename T>
-struct ptype_traits_base
-{
- static const T cast(const T &x) { return x; }
- static const bool is_signed = std::numeric_limits<T>::is_signed;
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template <>
-struct ptype_traits_base<bool>
-{
- static unsigned int cast(const bool &x) { return static_cast<unsigned int>(x); }
- static const bool is_signed = std::numeric_limits<bool>::is_signed;
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template <typename T>
-struct ptype_traits;
-
-template<>
-struct ptype_traits<bool> : ptype_traits_base<bool>
-{
-};
-
-template<>
-struct ptype_traits<char> : ptype_traits_base<char>
-{
- static char32_t fmt_spec() { return is_signed ? 'd' : 'u'; }
-};
-
-template<>
-struct ptype_traits<short> : ptype_traits_base<short>
-{
- static char32_t fmt_spec() { return 'd'; }
-};
-
-template<>
-struct ptype_traits<int> : ptype_traits_base<int>
-{
- static char32_t fmt_spec() { return 'd'; }
-};
-
-template<>
-struct ptype_traits<long> : ptype_traits_base<long>
-{
- static char32_t fmt_spec() { return 'd'; }
-};
-
-template<>
-struct ptype_traits<long long> : ptype_traits_base<long long>
-{
- static char32_t fmt_spec() { return 'd'; }
-};
-
-template<>
-struct ptype_traits<signed char> : ptype_traits_base<signed char>
-{
- static char32_t fmt_spec() { return 'd'; }
-};
-
-template<>
-struct ptype_traits<unsigned char> : ptype_traits_base<unsigned char>
-{
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template<>
-struct ptype_traits<unsigned short> : ptype_traits_base<unsigned short>
-{
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template<>
-struct ptype_traits<unsigned int> : ptype_traits_base<unsigned int>
-{
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template<>
-struct ptype_traits<unsigned long> : ptype_traits_base<unsigned long>
-{
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template<>
-struct ptype_traits<unsigned long long> : ptype_traits_base<unsigned long long>
-{
- static char32_t fmt_spec() { return 'u'; }
-};
-
-template<>
-struct ptype_traits<float> : ptype_traits_base<float>
-{
- static char32_t fmt_spec() { return 'f'; }
-};
-
-template<>
-struct ptype_traits<double> : ptype_traits_base<double>
-{
- static char32_t fmt_spec() { return 'f'; }
-};
-
-template<>
-struct ptype_traits<char *> : ptype_traits_base<char *>
-{
- static const char *cast(const char *x) { return x; }
- static char32_t fmt_spec() { return 's'; }
-};
-
-template<>
-struct ptype_traits<std::string> : ptype_traits_base<char *>
-{
- static const char *cast(const std::string &x) { return x.c_str(); }
- static char32_t fmt_spec() { return 's'; }
-};
-
-class pfmt
-{
-public:
- explicit pfmt(const pstring &fmt)
- : m_str(fmt), m_locale(std::locale::classic()), m_arg(0)
- {
- }
- explicit pfmt(const std::locale &loc, const pstring &fmt)
- : m_str(fmt), m_locale(loc), m_arg(0)
- {
- }
-
- COPYASSIGNMOVE(pfmt, default)
-
- ~pfmt() noexcept = default;
-
- operator pstring() const { return m_str; }
+ PENUM(plog_level,
+ DEBUG,
+ VERBOSE,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL)
template <typename T>
- typename std::enable_if<std::is_floating_point<T>::value, pfmt &>::type
- f(const T &x) {return format_element('f', x); }
+ struct format_traits_base
+ {
+ static constexpr const bool is_signed = std::numeric_limits<T>::is_signed;
+ static char32_t fmt_spec() { return 'u'; }
+ static void streamify(std::ostream &s, const T &v)
+ {
+ s << v;
+ }
+ };
- template <typename T>
- typename std::enable_if<std::is_floating_point<T>::value, pfmt &>::type
- e(const T &x) {return format_element('e', x); }
+ #if (PUSE_FLOAT128)
+ template <>
+ struct format_traits_base<FLOAT128>
+ {
+ // FIXME: need native support at some time
+ static constexpr const bool is_signed = true;
+ static char32_t fmt_spec() { return 'f'; }
+ static void streamify(std::ostream &s, const FLOAT128 &v)
+ {
+ s << narrow_cast<long double>(v);
+ }
+ };
+ #endif
template <typename T>
- typename std::enable_if<std::is_floating_point<T>::value, pfmt &>::type
- g(const T &x) {return format_element('g', x); }
-
- pfmt &operator ()(const void *x) {return format_element('p', x); }
- pfmt &operator ()(const pstring &x) {return format_element('s', x.c_str() ); }
+ struct format_traits;
- template<typename T>
- pfmt &operator ()(const T &x)
+ template<>
+ struct format_traits<compile_info::int128_type>
{
- return format_element(ptype_traits<T>::fmt_spec(), ptype_traits<T>::cast(x));
- }
+ // FIXME: need native support at some time
+ static constexpr const bool is_signed = true;
+ static char32_t fmt_spec() { return 'd'; }
+ template <typename T, typename = std::enable_if_t<plib::is_arithmetic<T>::value>>
+ static void streamify(std::ostream &s, const T &v)
+ {
+ s << narrow_cast<long long>(v);
+ }
+ };
- template<typename T>
- pfmt &operator ()(const T *x)
+ template<>
+ struct format_traits<bool> : format_traits_base<bool>
{
- return format_element(ptype_traits<T *>::fmt_spec(), ptype_traits<T *>::cast(x));
- }
+ };
- pfmt &operator ()()
+ template<>
+ struct format_traits<char> : format_traits_base<char>
{
- return *this;
- }
+ static char32_t fmt_spec() { return is_signed ? 'd' : 'u'; }
+ };
+ template<>
+ struct format_traits<short> : format_traits_base<short>
+ {
+ static char32_t fmt_spec() { return 'd'; }
+ };
- template<typename X, typename Y, typename... Args>
- pfmt &operator()(X&& x, Y && y, Args&&... args)
+ template<>
+ struct format_traits<int> : format_traits_base<int>
{
- return ((*this)(std::forward<X>(x)))(std::forward<Y>(y), std::forward<Args>(args)...);
- }
+ static char32_t fmt_spec() { return 'd'; }
+ };
- template<typename T>
- typename std::enable_if<std::is_integral<T>::value, pfmt &>::type
- x(const T &x)
+ template<>
+ struct format_traits<long> : format_traits_base<long>
{
- return format_element('x', x);
- }
+ static char32_t fmt_spec() { return 'd'; }
+ };
- template<typename T>
- typename std::enable_if<std::is_integral<T>::value, pfmt &>::type
- o(const T &x)
+ template<>
+ struct format_traits<long long> : format_traits_base<long long>
{
- return format_element('o', x);
- }
+ static char32_t fmt_spec() { return 'd'; }
+ };
- friend std::ostream& operator<<(std::ostream &ostrm, const pfmt &fmt)
+ template<>
+ struct format_traits<signed char> : format_traits_base<signed char>
{
- ostrm << fmt.m_str;
- return ostrm;
- }
+ static char32_t fmt_spec() { return 'd'; }
+ };
-protected:
+ template<>
+ struct format_traits<unsigned char> : format_traits_base<unsigned char>
+ {
+ static char32_t fmt_spec() { return 'u'; }
+ };
- struct rtype
+ template<>
+ struct format_traits<unsigned short> : format_traits_base<unsigned short>
{
- rtype() : ret(0), p(0), sl(0), pend(0), width(0) {}
- int ret;
- pstring::size_type p;
- pstring::size_type sl;
- char32_t pend;
- pstring::size_type width;
+ static char32_t fmt_spec() { return 'u'; }
+ };
+ template<>
+ struct format_traits<unsigned int> : format_traits_base<unsigned int>
+ {
+ static char32_t fmt_spec() { return 'u'; }
};
- rtype setfmt(std::stringstream &strm, char32_t cfmt_spec);
- template <typename T>
- pfmt &format_element(const char32_t cfmt_spec, T &&val)
+ template<>
+ struct format_traits<unsigned long> : format_traits_base<unsigned long>
{
- rtype ret;
+ static char32_t fmt_spec() { return 'u'; }
+ };
- m_arg++;
+ template<>
+ struct format_traits<unsigned long long> : format_traits_base<unsigned long long>
+ {
+ static char32_t fmt_spec() { return 'u'; }
+ };
- do {
- std::stringstream strm;
- strm.imbue(m_locale);
- ret = setfmt(strm, cfmt_spec);
- if (ret.ret>=0)
- {
- strm << std::forward<T>(val);
- const pstring ps(strm.str());
- pstring pad("");
- if (ret.width > ps.length())
- pad = pstring(ret.width - ps.length(), ' ');
+ template<>
+ struct format_traits<float> : format_traits_base<float>
+ {
+ static char32_t fmt_spec() { return 'f'; }
+ };
- m_str = m_str.substr(0, ret.p) + pad + ps + m_str.substr(ret.p + ret.sl);
- }
- } while (ret.ret == 1);
+ template<>
+ struct format_traits<double> : format_traits_base<double>
+ {
+ static char32_t fmt_spec() { return 'f'; }
+ };
- return *this;
- }
+ template<>
+ struct format_traits<long double> : format_traits_base<long double>
+ {
+ static char32_t fmt_spec() { return 'f'; }
+ };
+ #if (PUSE_FLOAT128)
+ template<>
+ struct format_traits<FLOAT128> : format_traits_base<FLOAT128>
+ {
+ static char32_t fmt_spec() { return 'f'; }
+ };
+ #endif
-private:
- pstring m_str;
- std::locale m_locale;
- unsigned m_arg;
-};
+ template<>
+ struct format_traits<char *> : format_traits_base<char *>
+ {
+ static char32_t fmt_spec() { return 's'; }
+ };
-template <class T, bool build_enabled = true>
-class pfmt_writer_t
-{
-public:
- explicit pfmt_writer_t() : m_enabled(true) { }
+ template<>
+ struct format_traits<const char *> : format_traits_base<const char *>
+ {
+ static char32_t fmt_spec() { return 's'; }
+ };
- COPYASSIGNMOVE(pfmt_writer_t, delete)
+ template<>
+ struct format_traits<const char16_t *> : format_traits_base<const char16_t *>
+ {
+ static char32_t fmt_spec() { return 's'; }
+ static void streamify(std::ostream &s, const char16_t *v)
+ {
+ const putf16string su16(v);
+ s << putf8string(su16).c_str();
+ }
+ };
- /* runtime enable */
- template<bool enabled, typename... Args>
- void log(const pstring & fmt, Args&&... args) const
+ template<>
+ struct format_traits<const char32_t *> : format_traits_base<const char32_t *>
{
- if (build_enabled && enabled && m_enabled)
+ static char32_t fmt_spec() { return 's'; }
+ static void streamify(std::ostream &s, const char32_t *v)
{
- pfmt pf(fmt);
- static_cast<T *>(this)->vdowrite(xlog(pf, std::forward<Args>(args)...));
+ const putf32string su32(v);
+ s << putf8string(su32).c_str();
}
- }
+ };
+
+ template<>
+ struct format_traits<std::string> : format_traits_base<std::string>
+ {
+ static char32_t fmt_spec() { return 's'; }
+ };
+
+ template<>
+ struct format_traits<putf8string> : format_traits_base<putf8string>
+ {
+ static char32_t fmt_spec() { return 's'; }
+ };
- template<typename... Args>
- void operator ()(const pstring &fmt, Args&&... args) const
+ template<>
+ struct format_traits<putf16string> : format_traits_base<putf16string>
{
- if (build_enabled && m_enabled)
+ static char32_t fmt_spec() { return 's'; }
+ static void streamify(std::ostream &s, const putf16string &v)
{
- pfmt pf(fmt);
- static_cast<const T *>(this)->vdowrite(xlog(pf, std::forward<Args>(args)...));
+ s << putf8string(v).c_str();
}
- }
+ };
+
+ template<>
+ struct format_traits<const void *> : format_traits_base<const void *>
+ {
+ static char32_t fmt_spec() { return 'p'; }
+ };
- void set_enabled(const bool v)
+ class pfmt
{
- m_enabled = v;
- }
+ public:
+ explicit pfmt(const pstring &fmt)
+ : m_str(fmt), m_locale(std::locale::classic()), m_arg(0)
+ {
+ }
+ explicit pfmt(const std::locale &loc, const pstring &fmt)
+ : m_str(fmt), m_locale(loc), m_arg(0)
+ {
+ }
- bool is_enabled() const { return m_enabled; }
+ PCOPYASSIGNMOVE(pfmt, default)
-protected:
- ~pfmt_writer_t() noexcept = default;
+ ~pfmt() noexcept = default;
-private:
- pfmt &xlog(pfmt &fmt) const { return fmt; }
+ operator pstring() const { return m_str; }
- template<typename X, typename... Args>
- pfmt &xlog(pfmt &fmt, X&& x, Args&&... args) const
+ template <typename T>
+ std::enable_if_t<plib::is_floating_point<T>::value, pfmt &>
+ f(const T &x) {return format_element('f', x); }
+
+ template <typename T>
+ std::enable_if_t<plib::is_floating_point<T>::value, pfmt &>
+ e(const T &x) {return format_element('e', x); }
+
+ template <typename T>
+ std::enable_if_t<plib::is_floating_point<T>::value, pfmt &>
+ g(const T &x) {return format_element('g', x); }
+
+ pfmt &operator ()(const void *x) {return format_element('p', x); }
+ pfmt &operator ()(const pstring &x) {return format_element('s', x.c_str() ); }
+
+ template<typename T>
+ pfmt &operator ()(const T &x)
+ {
+ return format_element(x);
+ }
+
+ template<typename T>
+ pfmt &operator ()(const T *x)
+ {
+ return format_element(x);
+ }
+
+ pfmt &operator ()()
+ {
+ return *this;
+ }
+
+ template<typename X, typename Y, typename... Args>
+ pfmt &operator()(X&& x, Y && y, Args&&... args)
+ {
+ return ((*this)(std::forward<X>(x)))(std::forward<Y>(y), std::forward<Args>(args)...);
+ }
+
+ template<typename T>
+ std::enable_if_t<plib::is_integral<T>::value, pfmt &>
+ x(const T &x)
+ {
+ return format_element('x', x);
+ }
+
+ template<typename T>
+ std::enable_if_t<plib::is_integral<T>::value, pfmt &>
+ o(const T &x)
+ {
+ return format_element('o', x);
+ }
+
+ friend std::ostream& operator<<(std::ostream &out_stream, const pfmt &fmt)
+ {
+ out_stream << putf8string(fmt.m_str);
+ return out_stream;
+ }
+
+ protected:
+
+ struct rtype
+ {
+ rtype() : ret(0), p(0), sl(0) {}
+ int ret;
+ pstring::size_type p;
+ pstring::size_type sl;
+ };
+ rtype set_format(std::stringstream &strm, char32_t char_format);
+
+ template <typename T>
+ pfmt &format_element(T &&v)
+ {
+ return format_element(format_traits<typename std::decay<T>::type>::fmt_spec(), std::forward<T>(v));
+ }
+
+ template <typename T>
+ pfmt &format_element(const char32_t char_format, T &&v)
+ {
+ rtype ret;
+
+ m_arg++;
+
+ do {
+ std::stringstream strm;
+ strm.imbue(m_locale);
+ ret = set_format(strm, char_format);
+ if (ret.ret>=0)
+ {
+ format_traits<typename std::decay<T>::type>::streamify(strm, std::forward<T>(v));
+ const pstring ps(putf8string(strm.str()));
+ m_str = m_str.substr(0, ret.p) + ps + m_str.substr(ret.p + ret.sl);
+ }
+ } while (ret.ret == 1);
+
+ return *this;
+ }
+
+
+ private:
+
+ pstring m_str;
+ std::locale m_locale;
+ unsigned m_arg;
+ };
+
+ template <class T, bool build_enabled = true>
+ class pfmt_writer_t
{
- return xlog(fmt(std::forward<X>(x)), std::forward<Args>(args)...);
- }
+ public:
+ explicit pfmt_writer_t() : m_enabled(true) { }
+
+ PCOPYASSIGNMOVE(pfmt_writer_t, delete)
+
+ // runtime enable
+ template<bool enabled, typename... Args>
+ void log(const pstring & fmt, Args&&... args) const noexcept
+ {
+ if (build_enabled && enabled && m_enabled)
+ {
+ pfmt pf(fmt);
+ plib::dynamic_downcast<T &>(*this).upstream_write(log_translate(pf, std::forward<Args>(args)...));
+ }
+ }
+
+ template<typename... Args>
+ void operator ()(const pstring &fmt, Args&&... args) const noexcept
+ {
+ if (build_enabled && m_enabled)
+ {
+ pfmt pf(fmt);
+ static_cast<const T &>(*this).upstream_write(log_translate(pf, std::forward<Args>(args)...));
+ }
+ }
+
+ void set_enabled(const bool v)
+ {
+ m_enabled = v;
+ }
+
+ bool is_enabled() const { return m_enabled; }
+
+ protected:
+ ~pfmt_writer_t() noexcept = default;
- bool m_enabled;
+ private:
+ pfmt &log_translate(pfmt &fmt) const { return fmt; }
-};
+ template<typename X, typename... Args>
+ pfmt &log_translate(pfmt &fmt, X&& x, Args&&... args) const
+ {
+ return log_translate(fmt(std::forward<X>(x)), std::forward<Args>(args)...);
+ }
-template <class T, plog_level::E L, bool build_enabled = true>
-class plog_channel : public pfmt_writer_t<plog_channel<T, L, build_enabled>, build_enabled>
-{
- friend class pfmt_writer_t<plog_channel<T, L, build_enabled>, build_enabled>;
-public:
- explicit plog_channel(T &b) : pfmt_writer_t<plog_channel, build_enabled>(), m_base(b) { }
+ bool m_enabled;
- COPYASSIGNMOVE(plog_channel, delete)
+ };
- ~plog_channel() noexcept = default;
+ using plog_delegate = plib::pmfp<void (plog_level, const pstring &)>;
-protected:
- void vdowrite(const pstring &ls) const
+ template <plog_level::E L, bool build_enabled = true>
+ class plog_channel : public pfmt_writer_t<plog_channel<L, build_enabled>, build_enabled>
{
- m_base.vlog(L, ls);
- }
+ friend class pfmt_writer_t<plog_channel<L, build_enabled>, build_enabled>;
+ public:
+ explicit plog_channel(plog_delegate logger)
+ : pfmt_writer_t<plog_channel, build_enabled>()
+ , m_logger(logger) { }
+
+ PCOPYASSIGNMOVE(plog_channel, delete)
-private:
- T &m_base;
-};
+ ~plog_channel() noexcept = default;
-template<class T, bool debug_enabled>
-class plog_base
-{
-public:
+ protected:
+ void upstream_write(const pstring &ls) const noexcept
+ {
+ m_logger(L, ls);
+ }
+
+ private:
+ plog_delegate m_logger;
+ };
- explicit plog_base(T &proxy)
- : debug(proxy),
- info(proxy),
- verbose(proxy),
- warning(proxy),
- error(proxy),
- fatal(proxy)
- {}
+ template<bool debug_enabled>
+ class plog_base
+ {
+ public:
+
+ explicit plog_base(plog_delegate logger)
+ : debug(logger),
+ info(logger),
+ verbose(logger),
+ warning(logger),
+ error(logger),
+ fatal(logger)
+ {}
+
+ PCOPYASSIGNMOVE(plog_base, delete)
+ virtual ~plog_base() noexcept = default;
+
+ plog_channel<plog_level::DEBUG, debug_enabled> debug;
+ plog_channel<plog_level::INFO> info;
+ plog_channel<plog_level::VERBOSE> verbose;
+ plog_channel<plog_level::WARNING> warning;
+ plog_channel<plog_level::ERROR> error;
+ plog_channel<plog_level::FATAL> fatal;
+ };
- COPYASSIGNMOVE(plog_base, default)
- virtual ~plog_base() noexcept = default;
+ struct perrmsg
+ {
+ template<std::size_t N, typename... Args>
+ explicit perrmsg(const char (&fmt)[N], Args&&... args) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
+ : m_msg(plib::pfmt(fmt)(std::forward<Args>(args)...))
+ { }
+ operator pstring const & () const noexcept { return m_msg; }
+ const pstring & operator ()() const noexcept { return m_msg; }
+ private:
+ pstring m_msg;
+ };
- plog_channel<T, plog_level::DEBUG, debug_enabled> debug;
- plog_channel<T, plog_level::INFO> info;
- plog_channel<T, plog_level::VERBOSE> verbose;
- plog_channel<T, plog_level::WARNING> warning;
- plog_channel<T, plog_level::ERROR> error;
- plog_channel<T, plog_level::FATAL> fatal;
-};
} // namespace plib
template<typename T>
plib::pfmt& operator<<(plib::pfmt &p, T&& val) { return p(std::forward<T>(val)); }
-#endif /* PSTRING_H_ */
+#endif // PFMT_LOG_H_
diff --git a/src/lib/netlist/plib/pfunction.cpp b/src/lib/netlist/plib/pfunction.cpp
index efbdcee6b6b..ed906a40385 100644
--- a/src/lib/netlist/plib/pfunction.cpp
+++ b/src/lib/netlist/plib/pfunction.cpp
@@ -1,221 +1,487 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * palloc.c
- *
- */
#include "pfunction.h"
#include "pexception.h"
#include "pfmtlog.h"
+#include "pmath.h"
+#include "pstonum.h"
+#include "pstrutil.h"
#include "putil.h"
-#include <cmath>
+#include <array>
+#include <map>
#include <stack>
+#include <type_traits>
+#include <utility>
namespace plib {
-void pfunction::compile(const std::vector<pstring> &inputs, const pstring &expr)
-{
- if (plib::startsWith(expr, "rpn:"))
- compile_postfix(inputs, expr.substr(4));
- else
- compile_infix(inputs, expr);
-}
-
-void pfunction::compile_postfix(const std::vector<pstring> &inputs, const pstring &expr)
-{
- std::vector<pstring> cmds(plib::psplit(expr, " "));
- compile_postfix(inputs, cmds, expr);
-}
-
-void pfunction::compile_postfix(const std::vector<pstring> &inputs,
- const std::vector<pstring> &cmds, const pstring &expr)
-{
- m_precompiled.clear();
- int stk = 0;
-
- for (const pstring &cmd : cmds)
+ PERRMSGV(MF_FUNCTION_UNKNOWN_TOKEN, 2, "pfunction: unknown/misformatted token <{1}> in <{2}>")
+ PERRMSGV(MF_FUNCTION_STACK_UNDERFLOW, 2, "pfunction: stack underflow on token <{1}> in <{2}>")
+ PERRMSGV(MF_FUNCTION_STACK_OVERFLOW, 2, "pfunction: stack overflow on token <{1}> in <{2}>")
+ PERRMSGV(MF_FUNCTION_PARENTHESIS_INEQUALITY, 2, "pfunction: parenthesis inequality on token <{1}> in <{2}>")
+ PERRMSGV(MF_FUNCTION_STACK_UNEQUAL_ONE, 2, "pfunction: stack count {1} different to one on <{2}>")
+ PERRMSGV(MF_FUNCTION_STACK_UNDERFLOW_INFIX, 1, "pfunction: stack underflow during infix parsing of: <{1}>")
+
+ static constexpr const std::size_t MAX_STACK = 32;
+
+ struct pcmd_t
+ {
+ rpn_cmd cmd;
+ int adj;
+ int prio;
+ };
+
+ static const std::map<pstring, pcmd_t> &pcmds()
+ {
+ static const std::map<pstring, pcmd_t> lpcmds =
+ {
+ { "^", { POW, 1, 30 } },
+ { "neg", { NEG, 0, 25 } },
+ { "+", { ADD, 1, 10 } },
+ { "-", { SUB, 1, 10 } },
+ { "*", { MULT, 1, 20 } },
+ { "/", { DIV, 1, 20 } },
+ { "<", { LT, 1, 9 } },
+ { ">", { GT, 1, 9 } },
+ { "<=", { LE, 1, 9 } },
+ { ">=", { GE, 1, 9 } },
+ { "==", { EQ, 1, 8 } },
+ { "!=", { NE, 1, 8 } },
+ { "if", { IF, 2, 0 } },
+ { "pow", { POW, 1, 0 } },
+ { "log", { LOG, 0, 0 } },
+ { "sin", { SIN, 0, 0 } },
+ { "cos", { COS, 0, 0 } },
+ { "max", { MAX, 1, 0 } },
+ { "min", { MIN, 1, 0 } },
+ { "trunc", { TRUNC, 0, 0 } },
+ { "rand", { RAND, -1, 0 } },
+
+ { "(", { LP, 0, 1 } },
+ { ")", { RP, 0, 1 } },
+ };
+ return lpcmds;
+ }
+ // FIXME: Exa parsing conflicts with e,E parsing
+ template<typename F>
+ static const std::map<pstring, F> &units_si()
+ {
+ static std::map<pstring, F> units_si_stat =
+ {
+ //{ "Y", narrow_cast<F>(1e24) }, // NOLINT: Yotta
+ //{ "Z", narrow_cast<F>(1e21) }, // NOLINT: Zetta
+ //{ "E", narrow_cast<F>(1e18) }, // NOLINT: Exa
+ { "P", narrow_cast<F>(1e15) }, // NOLINT: Peta
+ { "T", narrow_cast<F>(1e12) }, // NOLINT: Tera
+ { "G", narrow_cast<F>( 1e9) }, // NOLINT: Giga
+ { "M", narrow_cast<F>( 1e6) }, // NOLINT: Mega
+ { "k", narrow_cast<F>( 1e3) }, // NOLINT: Kilo
+ { "h", narrow_cast<F>( 1e2) }, // NOLINT: Hecto
+ //{ "da", narrow_cast<F>(1e1) }, // NOLINT: Deca
+ { "d", narrow_cast<F>(1e-1) }, // NOLINT: Deci
+ { "c", narrow_cast<F>(1e-2) }, // NOLINT: Centi
+ { "m", narrow_cast<F>(1e-3) }, // NOLINT: Milli
+ { "μ", narrow_cast<F>(1e-6) }, // NOLINT: Micro
+ { "n", narrow_cast<F>(1e-9) }, // NOLINT: Nano
+ { "p", narrow_cast<F>(1e-12) }, // NOLINT: Pico
+ { "f", narrow_cast<F>(1e-15) }, // NOLINT: Femto
+ { "a", narrow_cast<F>(1e-18) }, // NOLINT: Atto
+ { "z", narrow_cast<F>(1e-21) }, // NOLINT: Zepto
+ { "y", narrow_cast<F>(1e-24) }, // NOLINT: Yocto
+ };
+ return units_si_stat;
+ }
+
+
+ template <typename NT>
+ void pfunction<NT>::compile(const pstring &expr, const inputs_container &inputs) noexcept(false)
{
- rpn_inst rc;
- if (cmd == "+")
- { rc.m_cmd = ADD; stk -= 1; }
- else if (cmd == "-")
- { rc.m_cmd = SUB; stk -= 1; }
- else if (cmd == "*")
- { rc.m_cmd = MULT; stk -= 1; }
- else if (cmd == "/")
- { rc.m_cmd = DIV; stk -= 1; }
- else if (cmd == "pow")
- { rc.m_cmd = POW; stk -= 1; }
- else if (cmd == "sin")
- { rc.m_cmd = SIN; stk -= 0; }
- else if (cmd == "cos")
- { rc.m_cmd = COS; stk -= 0; }
- else if (cmd == "trunc")
- { rc.m_cmd = TRUNC; stk -= 0; }
- else if (cmd == "rand")
- { rc.m_cmd = RAND; stk += 1; }
+ if (plib::startsWith(expr, "rpn:"))
+ compile_postfix(expr.substr(4), inputs);
else
+ compile_infix(expr, inputs);
+ }
+
+ template <typename NT>
+ void pfunction<NT>::compile_postfix(const pstring &expr, const inputs_container &inputs) noexcept(false)
+ {
+ std::vector<pstring> cmds(plib::psplit(expr, ' '));
+ compile_postfix(inputs, cmds, expr);
+ }
+
+ template <typename NT>
+ void pfunction<NT>::compile_postfix(const inputs_container &inputs,
+ const std::vector<pstring> &cmds, const pstring &expr) noexcept(false)
+ {
+ m_precompiled.clear();
+ int stk = 0;
+
+ for (const pstring &cmd : cmds)
{
- for (std::size_t i = 0; i < inputs.size(); i++)
+ rpn_inst rc;
+ auto p = pcmds().find(cmd);
+ if (p != pcmds().end())
+ {
+ rc = rpn_inst(p->second.cmd);
+ stk -= p->second.adj;
+ }
+ else
{
- if (inputs[i] == cmd)
+ for (std::size_t i = 0; i < inputs.size(); i++)
+ {
+ if (inputs[i] == cmd)
+ {
+ rc = rpn_inst(PUSH_INPUT, i);
+ stk += 1;
+ break;
+ }
+ }
+ if (rc.cmd() != PUSH_INPUT)
{
- rc.m_cmd = PUSH_INPUT;
- rc.m_param = static_cast<double>(i);
+ bool err(false);
+ auto rs(plib::right(cmd,1));
+ auto r=units_si<NT>().find(rs);
+ if (r == units_si<NT>().end())
+ rc = rpn_inst(plib::pstonum_ne<NT>(cmd, err));
+ else
+ rc = rpn_inst(plib::pstonum_ne<NT>(plib::left(cmd, cmd.length()-1), err) * r->second);
+ if (err)
+ throw pexception(MF_FUNCTION_UNKNOWN_TOKEN(cmd, expr));
stk += 1;
- break;
}
}
- if (rc.m_cmd != PUSH_INPUT)
- {
- rc.m_cmd = PUSH_CONST;
- bool err;
- rc.m_param = plib::pstonum_ne<decltype(rc.m_param), true>(cmd, err);
- if (err)
- throw plib::pexception(plib::pfmt("pfunction: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr));
- stk += 1;
- }
+ if (stk < 1)
+ throw pexception(MF_FUNCTION_STACK_UNDERFLOW(cmd, expr));
+ if (stk >= narrow_cast<int>(MAX_STACK))
+ throw pexception(MF_FUNCTION_STACK_OVERFLOW(cmd, expr));
+ if (rc.cmd() == LP || rc.cmd() == RP)
+ throw pexception(MF_FUNCTION_PARENTHESIS_INEQUALITY(cmd, expr));
+ m_precompiled.push_back(rc);
}
- if (stk < 1)
- throw plib::pexception(plib::pfmt("pfunction: stack underflow on token <{1}> in <{2}>")(cmd)(expr));
- m_precompiled.push_back(rc);
+ if (stk != 1)
+ throw pexception(MF_FUNCTION_STACK_UNEQUAL_ONE(stk, expr));
+ compress();
+ }
+
+ static bool is_number(const pstring &n)
+ {
+ if (n.empty())
+ return false;
+ const auto l = n.substr(0,1);
+ return (l >= "0" && l <= "9");
+ }
+
+ static bool is_id(const pstring &n)
+ {
+ if (n.empty())
+ return false;
+ const auto l = n.substr(0,1);
+ return ((l >= "a" && l <= "z") || (l >= "A" && l <= "Z"));
}
- if (stk != 1)
- throw plib::pexception(plib::pfmt("pfunction: stack count different to one on <{2}>")(expr));
-}
-
-static int get_prio(const pstring &v)
-{
- if (v == "(" || v == ")")
- return 1;
- else if (plib::left(v, 1) >= "a" && plib::left(v, 1) <= "z")
- return 0;
- else if (v == "*" || v == "/")
- return 20;
- else if (v == "+" || v == "-")
- return 10;
- else if (v == "^")
- return 30;
- else
+
+ static int get_prio(const pstring &v)
+ {
+ auto p = pcmds().find(v);
+ if (p != pcmds().end())
+ return p->second.prio;
+ if (plib::left(v, 1) >= "a" && plib::left(v, 1) <= "z")
+ return 0;
return -1;
-}
-
-static pstring pop_check(std::stack<pstring> &stk, const pstring &expr)
-{
- if (stk.size() == 0)
- throw plib::pexception(plib::pfmt("pfunction: stack underflow during infix parsing of: <{1}>")(expr));
- pstring res = stk.top();
- stk.pop();
- return res;
-}
-
-void pfunction::compile_infix(const std::vector<pstring> &inputs, const pstring &expr)
-{
- // Shunting-yard infix parsing
- std::vector<pstring> sep = {"(", ")", ",", "*", "/", "+", "-", "^"};
- std::vector<pstring> sexpr(plib::psplit(plib::replace_all(expr, pstring(" "), pstring("")), sep));
- std::stack<pstring> opstk;
- std::vector<pstring> postfix;
-
- for (std::size_t i = 0; i < sexpr.size(); i++)
+ }
+
+ static pstring pop_check(std::stack<pstring> &stk, const pstring &expr) noexcept(false)
{
- pstring &s = sexpr[i];
- if (s=="(")
- opstk.push(s);
- else if (s==")")
+ if (stk.empty())
+ throw pexception(MF_FUNCTION_STACK_UNDERFLOW_INFIX(expr));
+ pstring res = stk.top();
+ stk.pop();
+ return res;
+ }
+
+ template <typename NT>
+ void pfunction<NT>::compile_infix(const pstring &expr, const inputs_container &inputs)
+ {
+ // Shunting-yard infix parsing
+ std::vector<pstring> sep = {"(", ")", ",", "*", "/", "+", "-", "^", "<=", ">=", "==", "!=", "<", ">"};
+ std::vector<pstring> sexpr2(plib::psplit(plib::replace_all(expr, " ", ""), sep));
+ std::stack<pstring> opstk;
+ std::vector<pstring> postfix;
+ std::vector<pstring> sexpr1;
+ std::vector<pstring> sexpr;
+
+ // FIXME: We really need to switch to ptokenizer and fix negative number
+ // handling in ptokenizer.
+
+ // Fix numbers exponential numbers
+ for (std::size_t i = 0; i < sexpr2.size(); )
{
- pstring x = pop_check(opstk, expr);
- while (x != "(")
+ if (i + 2 < sexpr2.size() && sexpr2[i].length() > 1)
{
- postfix.push_back(x);
- x = pop_check(opstk, expr);
+ auto r(plib::right(sexpr2[i], 1));
+ auto ne(sexpr2[i+1]);
+ if ((is_number(sexpr2[i]))
+ && (r == "e" || r == "E")
+ && (ne == "-" || ne == "+"))
+ {
+ sexpr1.push_back(sexpr2[i] + ne + sexpr2[i+2]);
+ i+=3;
+ }
+ else
+ sexpr1.push_back(sexpr2[i++]);
}
- if (opstk.size() > 0 && get_prio(opstk.top()) == 0)
- postfix.push_back(pop_check(opstk, expr));
+ else
+ sexpr1.push_back(sexpr2[i++]);
}
- else if (s==",")
+ // Fix numbers with unary minus/plus
+ for (std::size_t i = 0; i < sexpr1.size(); )
{
- pstring x = pop_check(opstk, expr);
- while (x != "(")
+ if (sexpr1[i]=="-" && (i+1 < sexpr1.size()) && is_number(sexpr1[i+1]))
{
- postfix.push_back(x);
- x = pop_check(opstk, expr);
+ if (i==0 || !(is_number(sexpr1[i-1]) || sexpr1[i-1] == ")" || is_id(sexpr1[i-1])))
+ {
+ sexpr.push_back("-" + sexpr1[i+1]);
+ i+=2;
+ }
+ else
+ sexpr.push_back(sexpr1[i++]);
}
- opstk.push(x);
- }
- else {
- int p = get_prio(s);
- if (p>0)
+ else if (sexpr1[i]=="-" && (i+1 < sexpr1.size()) && (is_id(sexpr1[i+1]) || sexpr1[i+1] == "("))
{
- if (opstk.size() == 0)
- opstk.push(s);
+ if (i==0 || !(is_number(sexpr1[i-1]) || sexpr1[i-1] == ")" || is_id(sexpr1[i-1])))
+ {
+ sexpr.emplace_back("neg");
+ sexpr.push_back(sexpr1[i+1]);
+ i+=2;
+ }
else
+ sexpr.push_back(sexpr1[i++]);
+ }
+ else
+ sexpr.push_back(sexpr1[i++]);
+ }
+
+ for (std::size_t i = 0; i < sexpr.size(); i++)
+ {
+ pstring &s = sexpr[i];
+ if (s=="(")
+ opstk.push(s);
+ else if (s==")")
+ {
+ pstring x = pop_check(opstk, expr);
+ while (x != "(")
{
- if (get_prio(opstk.top()) >= get_prio(s))
- postfix.push_back(pop_check(opstk, expr));
- opstk.push(s);
+ postfix.push_back(x);
+ x = pop_check(opstk, expr);
}
+ if (!opstk.empty() && get_prio(opstk.top()) == 0)
+ postfix.push_back(pop_check(opstk, expr));
}
- else if (p == 0) // Function or variable
+ else if (s==",")
{
- if (sexpr[i+1] == "(")
- opstk.push(s);
+ pstring x = pop_check(opstk, expr);
+ while (x != "(")
+ {
+ postfix.push_back(x);
+ x = pop_check(opstk, expr);
+ }
+ opstk.push(x);
+ }
+ else {
+ int prio = get_prio(s);
+ if (prio>0)
+ {
+ if (opstk.empty())
+ opstk.push(s);
+ else
+ {
+ if (get_prio(opstk.top()) >= prio)
+ postfix.push_back(pop_check(opstk, expr));
+ opstk.push(s);
+ }
+ }
+ else if (prio == 0) // Function or variable
+ {
+ if ((i+1<sexpr.size()) && sexpr[i+1] == "(")
+ opstk.push(s);
+ else
+ postfix.push_back(s);
+ }
else
postfix.push_back(s);
}
- else
- postfix.push_back(s);
}
+ while (!opstk.empty())
+ {
+ postfix.push_back(opstk.top());
+ opstk.pop();
+ }
+ //for (auto &e : postfix)
+ // printf("\t%s\n", e.c_str());
+ compile_postfix(inputs, postfix, expr);
+ }
+
+ template <typename NT>
+ static inline std::enable_if_t<plib::is_floating_point<NT>::value, NT>
+ lfsr_random(std::uint16_t &lfsr) noexcept
+ {
+ std::uint16_t lsb = lfsr & 1;
+ lfsr >>= 1;
+ if (lsb)
+ lfsr ^= 0xB400U; // NOLINT: taps 15, 13, 12, 10
+ return narrow_cast<NT>(lfsr) / narrow_cast<NT>(0xffffU); // NOLINT
}
- while (opstk.size() > 0)
+#if 0
+ // Currently unused since no integral type pfunction defined
+ template <typename NT>
+ static inline std::enable_if_t<plib::is_integral<NT>::value, NT>
+ lfsr_random(std::uint16_t &lfsr) noexcept
+ {
+ std::uint16_t lsb = lfsr & 1;
+ lfsr >>= 1;
+ if (lsb)
+ lfsr ^= 0xB400U; // NOLINT: taps 15, 13, 12, 10
+ return narrow_cast<NT>(lfsr);
+ }
+#endif
+ #define ST0 *(ptr+1)
+ #define ST1 *ptr
+ #define ST2 *(ptr-1)
+
+ #define OP(OP, ADJ, EXPR) \
+ case OP: \
+ ptr-= (ADJ); \
+ *(ptr-1) = (EXPR); \
+ break;
+
+ #define OP0(OP, EXPR) \
+ case OP: \
+ *(ptr++) = (EXPR); \
+ break;
+
+ template <typename NT>
+ NT pfunction<NT>::evaluate(const values_container &values) noexcept
{
- postfix.push_back(opstk.top());
- opstk.pop();
+ std::array<value_type, MAX_STACK> stack; // NOLINT
+ value_type *ptr = stack.data();
+ constexpr const auto zero = plib::constants<value_type>::zero();
+ constexpr const auto one = plib::constants<value_type>::one();
+ for (const auto &rc : m_precompiled)
+ {
+ switch (rc.cmd())
+ {
+ OP(ADD, 1, ST2 + ST1)
+ OP(MULT, 1, ST2 * ST1)
+ OP(SUB, 1, ST2 - ST1)
+ OP(DIV, 1, ST2 / ST1)
+ OP(EQ, 1, ST2 == ST1 ? one : zero)
+ OP(NE, 1, ST2 != ST1 ? one : zero)
+ OP(GT, 1, ST2 > ST1 ? one : zero)
+ OP(LT, 1, ST2 < ST1 ? one : zero)
+ OP(LE, 1, ST2 <= ST1 ? one : zero)
+ OP(GE, 1, ST2 >= ST1 ? one : zero)
+ OP(IF, 2, (ST2 != zero) ? ST1 : ST0)
+ OP(NEG, 0, -ST2)
+ OP(POW, 1, plib::pow(ST2, ST1))
+ OP(LOG, 0, plib::log(ST2))
+ OP(SIN, 0, plib::sin(ST2))
+ OP(COS, 0, plib::cos(ST2))
+ OP(MAX, 1, std::max(ST2, ST1))
+ OP(MIN, 1, std::min(ST2, ST1))
+ OP(TRUNC, 0, plib::trunc(ST2))
+ OP0(RAND, lfsr_random<value_type>(m_lfsr))
+ OP0(PUSH_INPUT, values[rc.index()])
+ OP0(PUSH_CONST, rc.value())
+ // please compiler
+ case LP:
+ case RP:
+ break;
+ }
+ }
+ return *(ptr-1);
}
- compile_postfix(inputs, postfix, expr);
-}
+#undef ST0
+#undef ST1
+#undef ST2
+#undef OP
+#undef OP0
-#define ST1 stack[ptr]
-#define ST2 stack[ptr-1]
+#define ST0 m_precompiled[ptr+0].value()
+#define ST1 m_precompiled[ptr-1].value()
+#define ST2 m_precompiled[ptr-2].value()
#define OP(OP, ADJ, EXPR) \
-case OP: \
- ptr-= (ADJ); \
- stack[ptr-1] = (EXPR); \
- break;
-
-double pfunction::evaluate(const std::vector<double> &values)
-{
- std::array<double, 20> stack = { 0 };
- unsigned ptr = 0;
- stack[0] = 0.0;
- for (auto &rc : m_precompiled)
+ case OP: \
+ if (ADJ == 2) {\
+ if (m_precompiled[ptr-3].cmd() == PUSH_CONST && m_precompiled[ptr-2].cmd() == PUSH_CONST && m_precompiled[ptr-1].cmd() == PUSH_CONST) \
+ { ptr--; m_precompiled[ptr-2] = rpn_inst(EXPR); n -= 3; ptr++; std::copy(m_precompiled.begin()+(ptr+1), m_precompiled.end(), m_precompiled.begin()+(ptr-2)); ptr-=2;} \
+ else { ptr++; } \
+ } else if (ADJ == 1) {\
+ if (m_precompiled[ptr-2].cmd() == PUSH_CONST && m_precompiled[ptr-1].cmd() == PUSH_CONST) \
+ { m_precompiled[ptr-2] = rpn_inst(EXPR); n -= 2; std::copy(m_precompiled.begin()+(ptr+1), m_precompiled.end(), m_precompiled.begin()+(ptr-1)); ptr--;} \
+ else { ptr++; } \
+ } else if (ADJ == 0) {\
+ if (m_precompiled[ptr-1].cmd() == PUSH_CONST) \
+ { ptr++; m_precompiled[ptr-2] = rpn_inst(EXPR); n -= 1; ptr--; std::copy(m_precompiled.begin()+(ptr+1), m_precompiled.end(), m_precompiled.begin()+(ptr)); } \
+ else { ptr++; } \
+ } else ptr++; \
+ break;
+
+#define OP0(OP, EXPR) \
+ case OP: \
+ ptr++; \
+ break;
+
+ template <typename NT>
+ void pfunction<NT>::compress()
{
- switch (rc.m_cmd)
+ constexpr const auto zero = plib::constants<value_type>::zero();
+ constexpr const auto one = plib::constants<value_type>::one();
+ unsigned ptr = 0;
+ auto n = m_precompiled.size();
+ for (; ptr < n; )
{
- OP(ADD, 1, ST2 + ST1)
- OP(MULT, 1, ST2 * ST1)
- OP(SUB, 1, ST2 - ST1)
- OP(DIV, 1, ST2 / ST1)
- OP(POW, 1, std::pow(ST2, ST1))
- OP(SIN, 0, std::sin(ST2))
- OP(COS, 0, std::cos(ST2))
- OP(TRUNC, 0, std::trunc(ST2))
- case RAND:
- stack[ptr++] = lfsr_random();
- break;
- case PUSH_INPUT:
- stack[ptr++] = values[static_cast<unsigned>(rc.m_param)];
- break;
- case PUSH_CONST:
- stack[ptr++] = rc.m_param;
- break;
+ switch (m_precompiled[ptr].cmd())
+ {
+ OP(ADD, 1, ST2 + ST1)
+ OP(MULT, 1, ST2 * ST1)
+ OP(SUB, 1, ST2 - ST1)
+ OP(DIV, 1, ST2 / ST1)
+ OP(EQ, 1, ST2 == ST1 ? one : zero)
+ OP(NE, 1, ST2 != ST1 ? one : zero)
+ OP(GT, 1, ST2 > ST1 ? one : zero)
+ OP(LT, 1, ST2 < ST1 ? one : zero)
+ OP(LE, 1, ST2 <= ST1 ? one : zero)
+ OP(GE, 1, ST2 >= ST1 ? one : zero)
+ OP(IF, 2, (ST2 != zero) ? ST1 : ST0)
+ OP(NEG, 0, -ST2)
+ OP(POW, 1, plib::pow(ST2, ST1))
+ OP(LOG, 0, plib::log(ST2))
+ OP(SIN, 0, plib::sin(ST2))
+ OP(COS, 0, plib::cos(ST2))
+ OP(MAX, 1, std::max(ST2, ST1))
+ OP(MIN, 1, std::min(ST2, ST1))
+ OP(TRUNC, 0, plib::trunc(ST2))
+ OP0(RAND, lfsr_random<value_type>(m_lfsr))
+ OP0(PUSH_INPUT, values[rc.index()])
+ OP0(PUSH_CONST, rc.value())
+ // please compiler
+ case LP:
+ case RP:
+ break;
+ }
}
+ //printf("func %lld %lld\n", m_precompiled.size(), n);
+ m_precompiled.resize(n);
}
- return stack[ptr-1];
-}
+
+ template class pfunction<float>;
+ template class pfunction<double>;
+ template class pfunction<long double>;
+#if (PUSE_FLOAT128)
+ template class pfunction<FLOAT128>;
+#endif
} // namespace plib
diff --git a/src/lib/netlist/plib/pfunction.h b/src/lib/netlist/plib/pfunction.h
index bc81ef0c5a6..836c6f9c126 100644
--- a/src/lib/netlist/plib/pfunction.h
+++ b/src/lib/netlist/plib/pfunction.h
@@ -1,14 +1,14 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pfunction.h
- *
- */
#ifndef PFUNCTION_H_
#define PFUNCTION_H_
-#include "pstate.h"
+///
+/// \file pfunction.h
+///
+
+#include "pmath.h"
#include "pstring.h"
#include <vector>
@@ -19,100 +19,163 @@ namespace plib {
// function evaluation
//============================================================
- /*! Class providing support for evaluating expressions
- *
- */
+ enum rpn_cmd
+ {
+ ADD,
+ MULT,
+ SUB,
+ DIV,
+ EQ,
+ NE,
+ LT,
+ GT,
+ LE,
+ GE,
+ IF,
+ NEG, // unary minus
+ POW,
+ LOG,
+ SIN,
+ COS,
+ MIN,
+ MAX,
+ RAND, /// random number between 0 and 1
+ TRUNC,
+
+ PUSH_CONST,
+ PUSH_INPUT,
+
+ LP, // Left parenthesis - for infix parsing
+ RP // right parenthesis - for infix parsing
+ };
+
+ /// \brief Class providing support for evaluating expressions
+ ///
+ /// \tparam NT Number type, should be float or double
+ ///
+ template <typename NT>
class pfunction
{
- enum rpn_cmd
- {
- ADD,
- MULT,
- SUB,
- DIV,
- POW,
- SIN,
- COS,
- RAND, /* random number between 0 and 1 */
- TRUNC,
- PUSH_CONST,
- PUSH_INPUT
- };
struct rpn_inst
{
- rpn_inst() : m_cmd(ADD), m_param(0.0) { }
+ constexpr rpn_inst() : m_cmd(ADD)
+ {
+ m_param.val = plib::constants<NT>::zero();
+ }
+ constexpr rpn_inst(rpn_cmd cmd, std::size_t index = 0)
+ : m_cmd(cmd)
+ {
+ m_param.index = index;
+ }
+ constexpr rpn_inst(NT v) : m_cmd(PUSH_CONST)
+ {
+ m_param.val = v;
+ }
+ constexpr const rpn_cmd &cmd() const noexcept
+ {
+ return m_cmd;
+ }
+ constexpr const NT &value() const noexcept
+ {
+ return m_param.val; // NOLINT
+ }
+ constexpr const std::size_t &index() const noexcept
+ {
+ return m_param.index; // NOLINT
+ }
+ private:
rpn_cmd m_cmd;
- double m_param;
+ union
+ {
+ NT val;
+ std::size_t index;
+ } m_param;
};
public:
- /*! Constructor with state saving support
- *
- * @param name Name of this object
- * @param owner Owner of this object
- * @param state_manager State manager to handle saving object state
- *
- */
- pfunction(const pstring &name, const void *owner, state_manager_t &state_manager)
- : m_lfsr(0xACE1u)
+
+ using value_type = NT;
+
+ using inputs_container = std::vector<pstring>;
+ using values_container = std::vector<value_type>;
+
+ /// \brief Constructor
+ ///
+ pfunction()
+ : m_lfsr(0xace1U) // NOLINT
{
- state_manager.save_item(owner, m_lfsr, name + ".lfsr");
}
- /*! Constructor without state saving support
- *
- */
- pfunction()
- : m_lfsr(0xACE1u)
+ /// \brief Constructor with compile
+ ///
+ pfunction(const pstring &expr, const inputs_container &inputs = inputs_container())
+ : m_lfsr(0xace1U) // NOLINT
{
+ compile(expr, inputs);
}
- /*! Compile an expression
- *
- * @param inputs Vector of input variables, e.g. {"A","B"}
- * @param expr infix or postfix expression. default is infix, postrix
- * to be prefixed with rpn, e.g. "rpn:A B + 1.3 /"
- */
- void compile(const std::vector<pstring> &inputs, const pstring &expr);
-
- /*! Compile a rpn expression
- *
- * @param inputs Vector of input variables, e.g. {"A","B"}
- * @param expr Reverse polish notation expression, e.g. "A B + 1.3 /"
- */
- void compile_postfix(const std::vector<pstring> &inputs, const pstring &expr);
- /*! Compile an infix expression
- *
- * @param inputs Vector of input variables, e.g. {"A","B"}
- * @param expr Infix expression, e.g. "(A+B)/1.3"
- */
- void compile_infix(const std::vector<pstring> &inputs, const pstring &expr);
- /*! Evaluate the expression
- *
- * @param values for input variables, e.g. {1.1, 2.2}
- * @return value of expression
- */
- double evaluate(const std::vector<double> &values);
+ /// \brief Evaluate the expression
+ ///
+ /// \param values for input variables, e.g. {1.1, 2.2}
+ /// \return value of expression
+ ///
+ value_type operator()(const values_container &values = values_container()) noexcept
+ {
+ return evaluate(values);
+ }
+
+ /// \brief Compile an expression
+ ///
+ /// \param expr infix or postfix expression. default is infix, postfix
+ /// to be prefixed with rpn, e.g. "rpn:A B + 1.3 /"
+ /// \param inputs Vector of input variables, e.g. {"A","B"}
+ ///
+ void compile(const pstring &expr, const inputs_container &inputs = inputs_container()) noexcept(false);
+
+ /// \brief Compile a rpn expression
+ ///
+ /// \param expr Reverse polish notation expression, e.g. "A B + 1.3 /"
+ /// \param inputs Vector of input variables, e.g. {"A","B"}
+ ///
+ void compile_postfix(const pstring &expr, const inputs_container &inputs = inputs_container()) noexcept(false);
+
+ /// \brief Compile an infix expression
+ ///
+ /// \param expr Infix expression, e.g. "(A+B)/1.3"
+ /// \param inputs Vector of input variables, e.g. {"A","B"}
+ ///
+ void compile_infix(const pstring &expr, const inputs_container &inputs = inputs_container()) noexcept(false);
+
+ /// \brief Evaluate the expression
+ ///
+ /// \param values for input variables, e.g. {1.1, 2.2}
+ /// \return value of expression
+ ///
+ value_type evaluate(const values_container &values = values_container()) noexcept;
+
+ template <typename ST>
+ void save_state(ST &st)
+ {
+ st.save_item(m_lfsr, "m_lfsr");
+ }
private:
- void compile_postfix(const std::vector<pstring> &inputs,
+ void compress();
+ void compile_postfix(const inputs_container &inputs,
const std::vector<pstring> &cmds, const pstring &expr);
- double lfsr_random()
- {
- std::uint16_t lsb = m_lfsr & 1;
- m_lfsr >>= 1;
- if (lsb)
- m_lfsr ^= 0xB400u; // taps 15, 13, 12, 10
- return static_cast<double>(m_lfsr) / static_cast<double>(0xffffu);
- }
-
std::vector<rpn_inst> m_precompiled; //!< precompiled expression
std::uint16_t m_lfsr; //!< lfsr used for generating random numbers
};
+ extern template class pfunction<float>;
+ extern template class pfunction<double>;
+ extern template class pfunction<long double>;
+#if (PUSE_FLOAT128)
+ extern template class pfunction<FLOAT128>;
+#endif
} // namespace plib
-#endif /* PEXCEPTION_H_ */
+#endif // PEXCEPTION_H_
diff --git a/src/lib/netlist/plib/pgsl.h b/src/lib/netlist/plib/pgsl.h
new file mode 100644
index 00000000000..b585e3ed9e3
--- /dev/null
+++ b/src/lib/netlist/plib/pgsl.h
@@ -0,0 +1,181 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PGSL_H_
+#define PGSL_H_
+
+///
+/// \file pgsl.h
+///
+/// This core guidelines gsl implementation currently provides only enough
+/// functionality to syntactically support a very limited number of the
+/// gsl specification.
+///
+/// Going forward this approach may be extended.
+///
+
+#include "pconfig.h"
+#include "pexception.h"
+#include "ptypes.h"
+
+#include <exception>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+#if defined(__has_builtin) // clang and gcc 10
+ #if __has_builtin(__builtin_unreachable)
+ #define gsl_Expects(e) ((e) ? static_cast<void>(0) : __builtin_unreachable())
+ #else
+ #define gsl_Expects(e) ((e) ? static_cast<void>(0) : static_cast<void>(0))
+ #endif
+#elif defined(__GNUC__) && !(defined( __CUDACC__ ) && defined( __CUDA_ARCH__ ))
+ //#define gsl_Expects(e) ((e) ? static_cast<void>(0) : __builtin_unreachable())
+ #define gsl_Expects(e) (__builtin_expect(!!(e), 1) ? static_cast<void>(0) : __builtin_unreachable())
+#elif defined(_MSC_VER)
+ #define gsl_Expects(e) __assume(e)
+#else
+ #define gsl_Expects(e) ((e) ? static_cast<void>(0) : static_cast<void>(0))
+#endif
+
+#if 1
+// FIXME: __builtin_unreachable contrary to google sources does not seem to be of
+// any use and decreases performance slightly. Convert gsl_Expects to a noop
+// and revisit in the future.
+#undef gsl_Expects
+#define gsl_Expects(e) do {} while (0)
+
+#if 0
+// Alternative and c++ style implementation. Suffers from the same drawbacks
+// like __builtin_unreachable
+static constexpr inline void gsl_Expects(const bool &e)
+{
+ if (!e)
+ __builtin_unreachable();
+}
+#endif
+#endif
+
+#define gsl_Ensures(e) gsl_Expects(e)
+
+namespace plib {
+ namespace pgsl {
+
+ struct narrowing_error : public std::exception {};
+
+ template <typename T>
+ using owner = T;
+
+ template <typename T>
+ using not_null = T;
+
+ /// \brief perform a narrowing cast without checks
+ ///
+ template <typename T, typename O>
+ constexpr T narrow_cast(O && v) noexcept
+ {
+ static_assert(plib::is_arithmetic<T>::value && std::is_convertible<std::remove_reference_t<O>, T>::value, "narrow cast expects conversion between arithmetic types");
+ return static_cast<T>(std::forward<O>(v));
+ }
+
+ /// \brief perform a narrowing cast terminating if loss of precision
+ ///
+ /// The c++ core guidelines require the narrow function to raise an error
+ /// This will make narrow noexcept(false). This has shown to have a
+ /// measurable impact on performance and thus we deviate from
+ /// the standard here.
+ ///
+ template <typename T, typename O>
+ inline T narrow(O && v) noexcept
+ {
+ static_assert(plib::is_arithmetic<T>::value && std::is_convertible<std::remove_reference_t<O>, T>::value, "narrow cast expects conversion between arithmetic types");
+
+ const auto val = static_cast<T>(std::forward<O>(v));
+ if( v == static_cast<std::remove_reference_t<O>>(val))
+ {
+ return val;
+ }
+
+ plib::terminate("narrowing_error");
+ // throw narrowing_error();
+ }
+
+ } // namespace pgsl
+
+ /// \brief dynamic downcast from base type pointer to derived type pointer
+ ///
+ /// This is a `noexcept` dynamic_cast implementation. It will return the cast
+ /// wrapped into a std::optional type forcing the caller to
+ /// examine if the conversion was successful.
+ ///
+ /// \tparam D Derived type
+ /// \tparam B Base type
+ /// \param base pointer to type B
+ /// \returns return_success_t
+ ///
+ template <typename D, typename B>
+ std::enable_if_t<std::is_pointer_v<D> && std::is_pointer_v<std::remove_reference_t<B>>,
+ std::optional<D>>
+ dynamic_downcast(B base) noexcept
+ {
+ D ret = dynamic_cast<D>(base);
+ if (ret != nullptr)
+ return ret;
+ return std::nullopt;
+ }
+
+ /// \brief dynamic downcast from base type reference to derived type pointer
+ ///
+ /// This is a `noexcept` dynamic_cast implementation. It will return the
+ /// pointer cast wrapped into a std::optional type forcing the caller to
+ /// examine if the conversion was successful.
+ ///
+ /// The return value is a pointer cast since there is no std::optional for
+ /// references. Such a construct is considered ill-formed according to
+ /// the std::optional specification (Section 5.2, p1).
+ ///
+ /// \tparam D Derived type
+ /// \tparam B Base type
+ /// \param base reference of type B
+ /// \returns return_success_t
+ ///
+ template <typename D, typename B>
+ std::enable_if_t<std::is_pointer_v<D> &&!std::is_pointer_v<std::remove_reference_t<B>> ,
+ std::optional<D>>
+ dynamic_downcast(B &base) noexcept
+ {
+ D ret = dynamic_cast<D>(&base);
+ if (ret != nullptr)
+ return ret;
+ return std::nullopt;
+ }
+
+ /// \brief downcast from base type to derived type
+ ///
+ /// The cpp core guidelines require a very careful use of static cast
+ /// for downcast operations. This template is used to identify these uses
+ /// and later for debug builds use dynamic_cast.
+ ///
+ template <typename D, typename B>
+ constexpr D downcast(B && b) noexcept
+ {
+ static_assert((std::is_pointer<D>::value && std::is_pointer<B>::value)
+ || (std::is_reference<D>::value && std::is_reference<B>::value), "downcast only supports pointers or reference for derived");
+ return static_cast<D>(std::forward<B>(b));
+ }
+
+ using pgsl::narrow_cast;
+
+ /// \brief cast to void *
+ ///
+ /// The purpose of void_ptr_cast is to help identify casts to void in the code.
+ ///
+ template <typename T>
+ constexpr void * void_ptr_cast(T *ptr) noexcept { return static_cast<void *>(ptr); }
+
+} // namespace plib
+
+//FIXME: This is the place to use more complete implementations
+namespace gsl = plib::pgsl;
+
+#endif // PGSL_H_
diff --git a/src/lib/netlist/plib/plists.h b/src/lib/netlist/plib/plists.h
index 525a65a97f2..2e389f34b54 100644
--- a/src/lib/netlist/plib/plists.h
+++ b/src/lib/netlist/plib/plists.h
@@ -1,281 +1,323 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * plists.h
- *
- */
-
-#pragma once
#ifndef PLISTS_H_
#define PLISTS_H_
-#include "pstring.h"
+///
+/// \file plists.h
+///
+
+#include "ptypes.h"
+#include <algorithm>
#include <array>
#include <type_traits>
-#include <vector>
+#include <utility>
namespace plib {
-/* ----------------------------------------------------------------------------------------
- * uninitialised_array_t:
- * fixed size array allowing to override constructor and initialize
- * members by placement new.
- *
- * Use with care. This template is provided to improve locality of storage
- * in high frequency applications. It should not be used for anything else.
- * ---------------------------------------------------------------------------------------- */
-
-template <class C, std::size_t N>
-class uninitialised_array_t
-{
-public:
-
- using iterator = C *;
- using const_iterator = const C *;
-
- //uninitialised_array_t() noexcept = default;
- uninitialised_array_t() noexcept
- : m_initialized(0)
- {
- }
- COPYASSIGNMOVE(uninitialised_array_t, delete)
- ~uninitialised_array_t() noexcept
+ /// \brief Array holding uninitialized elements
+ ///
+ /// Use with care. This template is provided to improve locality of storage
+ /// in high frequency applications. It should not be used for anything else.
+ ///
+ template <class C, std::size_t N>
+ class uninitialised_array
{
- if (m_initialized>=N)
- for (std::size_t i=0; i<N; i++)
- (*this)[i].~C();
- }
+ public:
- size_t size() const { return N; }
+ using value_type = C;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using iterator = value_type *;
+ using const_iterator = const value_type *;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
- C& operator[](const std::size_t &index) noexcept
- {
- return *reinterpret_cast<C *>(&m_buf[index]);
- }
+ constexpr uninitialised_array() noexcept = default;
- const C& operator[](const std::size_t &index) const noexcept
- {
- return *reinterpret_cast<const C *>(&m_buf[index]);
- }
+ constexpr uninitialised_array(const uninitialised_array &) = default;
+ constexpr uninitialised_array &operator=(const uninitialised_array &) = default;
+ constexpr uninitialised_array(uninitialised_array &&) noexcept = default;
+ constexpr uninitialised_array &operator=(uninitialised_array &&) noexcept = default;
- template<typename... Args>
- void emplace(const std::size_t index, Args&&... args)
- {
- m_initialized++;
- // allocate on buffer
- new (&m_buf[index]) C(std::forward<Args>(args)...);
- }
+ ~uninitialised_array() noexcept = default;
- iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
- iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
+ constexpr size_t size() const noexcept { return N; }
- iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
- iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[N]); }
+ constexpr bool empty() const noexcept { return size() == 0; }
- const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); }
- const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[N]); }
+ constexpr reference operator[](size_type index) noexcept
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast<reference>(m_buf[index]);
+ }
-protected:
+ constexpr const_reference operator[](size_type index) const noexcept
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast<const_reference>(m_buf[index]);
+ }
-private:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); }
- /* ensure proper alignment */
- PALIGNAS_VECTOROPT()
- std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
- unsigned m_initialized;
-};
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[0] + N); }
-// ----------------------------------------------------------------------------------------
-// plinkedlist_t: a simple linked list
-// the list allows insertions / deletions if used properly
-// ----------------------------------------------------------------------------------------
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0] + N); }
-#if 0
-template <class LC>
-class linkedlist_t
-{
-public:
+ private:
+ std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
+ };
- struct element_t
+ /// \brief fixed allocation vector
+ ///
+ /// Currently only emplace_back and clear are supported.
+ ///
+ /// Use with care. This template is provided to improve locality of storage
+ /// in high frequency applications. It should not be used for anything else.
+ ///
+ template <class C, std::size_t N>
+ class static_vector
{
public:
- friend class linkedlist_t<LC>;
-
- constexpr element_t() : m_next(nullptr) {}
- constexpr element_t(const element_t &rhs) = delete;
- constexpr element_t(element_t &&rhs) = delete;
+ using value_type = C;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using iterator = value_type *;
+ using const_iterator = const value_type *;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ constexpr static_vector() noexcept
+ : m_pos(0)
+ {
+ }
- constexpr LC *next() const noexcept { return m_next; }
+ constexpr static_vector(const static_vector &) = default;
+ constexpr static_vector &operator=(const static_vector &) = default;
+ constexpr static_vector(static_vector &&) noexcept = default;
+ constexpr static_vector &operator=(static_vector &&) noexcept = default;
- protected:
- ~element_t() = default;
- private:
- LC * m_next;
- };
+ ~static_vector() noexcept
+ {
+ clear();
+ }
- struct iter_t final : public std::iterator<std::forward_iterator_tag, LC>
- {
- private:
- LC* p;
- public:
- explicit constexpr iter_t(LC* x) noexcept : p(x) { }
- explicit constexpr iter_t(const iter_t &rhs) noexcept : p(rhs.p) { }
- iter_t(iter_t &&rhs) noexcept { std::swap(*this, rhs); }
- iter_t& operator=(const iter_t &rhs) { iter_t t(rhs); std::swap(*this, t); return *this; }
- iter_t& operator=(iter_t &&rhs) { std::swap(*this, rhs); return *this; }
- iter_t& operator++() noexcept {p = p->next();return *this;}
- iter_t operator++(int) noexcept {iter_t tmp(*this); operator++(); return tmp;}
- constexpr bool operator==(const iter_t& rhs) const noexcept {return p == rhs.p;}
- constexpr bool operator!=(const iter_t& rhs) const noexcept {return p != rhs.p;}
- /* constexpr */ LC& operator*() noexcept {return *p;}
- /* constexpr */ LC* operator->() noexcept {return p;}
-
- constexpr LC& operator*() const noexcept {return *p;}
- constexpr LC* operator->() const noexcept {return p;}
- };
+ constexpr size_t size() const noexcept { return m_pos; }
- constexpr linkedlist_t() : m_head(nullptr) {}
+ constexpr bool empty() const noexcept { return size() == 0; }
- constexpr iter_t begin() const noexcept { return iter_t(m_head); }
- constexpr iter_t end() const noexcept { return iter_t(nullptr); }
+ constexpr void clear()
+ {
+ for (size_type i=0; i<m_pos; ++i)
+ (*this)[i].~C();
+ m_pos = 0;
+ }
- void push_front(LC *elem) noexcept
- {
- elem->m_next = m_head;
- m_head = elem;
- }
+ template<typename... Args>
+ constexpr void emplace_back(Args&&... args)
+ {
+ // placement new on buffer
+ new (&m_buf[m_pos]) C(std::forward<Args>(args)...);
+ m_pos++;
+ }
- void push_back(LC *elem) noexcept
- {
- LC **p = &m_head;
- while (*p != nullptr)
+ constexpr reference operator[](size_type index) noexcept
{
- p = &((*p)->m_next);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast<reference>(m_buf[index]);
}
- *p = elem;
- elem->m_next = nullptr;
- }
- void remove(const LC *elem) noexcept
- {
- auto p = &m_head;
- while(*p != elem)
+ constexpr const_reference operator[](size_type index) const noexcept
{
- //nl_assert(*p != nullptr);
- p = &((*p)->m_next);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast<const_reference>(m_buf[index]);
}
- (*p) = elem->m_next;
- }
- LC *front() const noexcept { return m_head; }
- void clear() noexcept { m_head = nullptr; }
- constexpr bool empty() const noexcept { return (m_head == nullptr); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator begin() const noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator end() const noexcept { return reinterpret_cast<iterator>(&m_buf[0] + m_pos); }
-private:
- LC *m_head;
-};
-#else
-template <class LC>
-class linkedlist_t
-{
-public:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator begin() noexcept { return reinterpret_cast<iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr iterator end() noexcept { return reinterpret_cast<iterator>(&m_buf[0] + m_pos); }
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr const_iterator cbegin() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0]); }
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ constexpr const_iterator cend() const noexcept { return reinterpret_cast<const_iterator>(&m_buf[0] + m_pos); }
+
+ private:
+ std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
+ size_type m_pos;
+ };
- struct element_t
+ /// \brief a simple linked list.
+ ///
+ /// The list allows insertions and deletions whilst being processed.
+ ///
+ template <class LC, int TAG>
+ class linked_list_t
{
public:
+ using ttag = std::integral_constant<int, TAG>;
- friend class linkedlist_t<LC>;
+ struct element_t
+ {
+ public:
+ using tag = std::integral_constant<int, TAG>;
- constexpr element_t() : m_next(nullptr), m_prev(nullptr) {}
- ~element_t() noexcept = default;
+ friend class linked_list_t<LC, TAG>;
- COPYASSIGNMOVE(element_t, delete)
+ constexpr element_t() noexcept : m_next(nullptr), m_prev(nullptr) {}
+ ~element_t() noexcept = default;
- constexpr LC *next() const noexcept { return m_next; }
- constexpr LC *prev() const noexcept { return m_prev; }
- private:
- LC * m_next;
- LC * m_prev;
- };
+ constexpr element_t(const element_t &) noexcept = default;
+ constexpr element_t &operator=(const element_t &) noexcept = default;
+ constexpr element_t(element_t &&) noexcept = default;
+ constexpr element_t &operator=(element_t &&) noexcept = default;
- struct iter_t final : public std::iterator<std::forward_iterator_tag, LC>
- {
- private:
- LC* p;
- public:
- explicit constexpr iter_t(LC* x) noexcept : p(x) { }
- constexpr iter_t(iter_t &rhs) noexcept : p(rhs.p) { }
- iter_t(iter_t &&rhs) noexcept { std::swap(*this, rhs); }
- iter_t& operator=(const iter_t &rhs) noexcept { iter_t t(rhs); std::swap(*this, t); return *this; }
- iter_t& operator=(iter_t &&rhs) noexcept { std::swap(*this, rhs); return *this; }
- iter_t& operator++() noexcept {p = p->next();return *this;}
- // NOLINTNEXTLINE(cert-dcl21-cpp)
- iter_t operator++(int) & noexcept {const iter_t tmp(*this); operator++(); return tmp;}
-
- ~iter_t() = default;
-
- constexpr bool operator==(const iter_t& rhs) const noexcept {return p == rhs.p;}
- constexpr bool operator!=(const iter_t& rhs) const noexcept {return p != rhs.p;}
- /* constexpr */ LC& operator*() noexcept {return *p;}
- /* constexpr */ LC* operator->() noexcept {return p;}
-
- constexpr LC& operator*() const noexcept {return *p;}
- constexpr LC* operator->() const noexcept {return p;}
- };
+ private:
+ element_t * m_next;
+ element_t * m_prev;
+ };
- constexpr linkedlist_t() : m_head(nullptr) {}
+ struct iter_t final
+ {
+ private:
+ element_t * p;
+ public:
+ using tag = std::integral_constant<int, TAG>;
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = LC;
+ using pointer = value_type *;
+ using reference = value_type &;
+ using difference_type = std::ptrdiff_t;
+
+ explicit constexpr iter_t(element_t * x) noexcept : p(x) { }
+
+ constexpr iter_t(const iter_t &rhs) noexcept = default;
+ constexpr iter_t(iter_t &&rhs) noexcept = default;
+ constexpr iter_t& operator=(const iter_t &rhs) noexcept = default;
+ constexpr iter_t& operator=(iter_t &&rhs) noexcept = default;
+
+ ~iter_t() noexcept = default;
+
+ iter_t& operator++() noexcept { p = p->m_next; return *this; }
+ // NOLINTNEXTLINE(cert-dcl21-cpp)
+ iter_t operator++(int) & noexcept { const iter_t tmp(*this); operator++(); return tmp; }
+
+ constexpr bool operator==(const iter_t& rhs) const noexcept { return p == rhs.p; }
+ constexpr bool operator!=(const iter_t& rhs) const noexcept { return p != rhs.p; }
+#if 0
+ constexpr LC& operator*() noexcept { return *static_cast<LC *>(p); }
+ constexpr LC* operator->() noexcept { return static_cast<LC *>(p); }
- constexpr iter_t begin() const noexcept { return iter_t(m_head); }
- constexpr iter_t end() const noexcept { return iter_t(nullptr); }
+ constexpr LC& operator*() const noexcept { return *static_cast<LC *>(p); }
+ constexpr LC* operator->() const noexcept { return static_cast<LC *>(p); }
+#else
+ constexpr LC* operator*() noexcept { return static_cast<LC *>(p); }
+ constexpr LC* operator->() noexcept { return static_cast<LC *>(p); }
- void push_front(LC *elem) noexcept
- {
- if (m_head)
- m_head->m_prev = elem;
- elem->m_next = m_head;
- elem->m_prev = nullptr;
- m_head = elem;
- }
-
- void push_back(LC *elem) noexcept
- {
- LC ** p(&m_head);
- LC * prev(nullptr);
- while (*p != nullptr)
+ constexpr LC* operator*() const noexcept { return static_cast<LC *>(p); }
+ constexpr LC* operator->() const noexcept { return static_cast<LC *>(p); }
+#endif
+ };
+
+ constexpr linked_list_t() noexcept : m_head(nullptr) {}
+
+ constexpr iter_t begin() const noexcept { return iter_t(m_head); }
+ constexpr iter_t end() const noexcept { return iter_t(nullptr); }
+
+ constexpr void push_front(element_t *elem) noexcept
{
- prev = *p;
- p = &((*p)->m_next);
+ elem->m_next = m_head;
+ elem->m_prev = nullptr;
+ if (m_head)
+ m_head->m_prev = elem;
+ m_head = elem;
}
- *p = elem;
- elem->m_prev = prev;
- elem->m_next = nullptr;
- }
- void remove(const LC *elem) noexcept
- {
- if (elem->m_prev)
- elem->m_prev->m_next = elem->m_next;
- else
- m_head = elem->m_next;
- if (elem->m_next)
- elem->m_next->m_prev = elem->m_prev;
- else
+ constexpr void push_back(element_t *elem) noexcept
{
- /* update tail */
+ element_t ** p(&m_head);
+ element_t * prev(nullptr);
+ while (*p != nullptr)
+ {
+ prev = *p;
+ p = &((*p)->m_next);
+ }
+ *p = elem;
+ elem->m_prev = prev;
+ elem->m_next = nullptr;
}
- }
- LC *front() const noexcept { return m_head; }
- void clear() noexcept { m_head = nullptr; }
- constexpr bool empty() const noexcept { return (m_head == nullptr); }
+ constexpr void remove(const element_t *elem) noexcept
+ {
+ if (elem->m_prev)
+ elem->m_prev->m_next = elem->m_next;
+ else
+ m_head = elem->m_next;
+ if (elem->m_next)
+ elem->m_next->m_prev = elem->m_prev;
+ else
+ {
+ // update tail
+ }
+ }
+
+ constexpr LC *front() const noexcept { return m_head; }
+ constexpr bool empty() const noexcept { return (m_head == nullptr); }
+ constexpr std::size_t size() const noexcept
+ {
+ std::size_t ret = 0;
+ element_t *p = m_head;
+ while (p != nullptr)
+ {
+ ret++;
+ p = p->m_next;
+ }
+ return ret;
+ }
+
+ constexpr void clear() noexcept
+ {
+ element_t *p(m_head);
+ while (p != nullptr)
+ {
+ element_t *n(p->m_next);
+ p->m_next = nullptr;
+ p->m_prev = nullptr;
+ p = n;
+ }
+ m_head = nullptr;
+ }
+
+ private:
+ element_t *m_head;
+ };
-private:
- LC *m_head;
-};
-#endif
} // namespace plib
-#endif /* PLISTS_H_ */
+#endif // PLISTS_H_
diff --git a/src/lib/netlist/plib/pmain.cpp b/src/lib/netlist/plib/pmain.cpp
index 1ecf0a4d032..ef1f47077e5 100644
--- a/src/lib/netlist/plib/pmain.cpp
+++ b/src/lib/netlist/plib/pmain.cpp
@@ -1,80 +1,47 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pmain.cpp
- *
- */
#include "pmain.h"
-#ifdef _WIN32
-#include <windows.h>
-#include <string.h>
-#include <tchar.h>
-#endif
-
namespace plib {
- #ifdef _WIN32
- static pstring toutf8(const wchar_t *w)
- {
- auto wlen = wcslen(w);
- int dst_char_count = WideCharToMultiByte(CP_UTF8, 0, w, wlen, nullptr, 0, nullptr, nullptr);
- char *buf = new char[dst_char_count + 1];
- WideCharToMultiByte(CP_UTF8, 0, w, wlen, buf, dst_char_count, nullptr, nullptr);
- buf[dst_char_count] = 0;
- auto ret = pstring(buf);
- delete [] buf;
- return ret;
- }
- #endif
-
-/***************************************************************************
- Application
-***************************************************************************/
-
app::app()
- : options()
- , pout(&std::cout)
- , perr(&std::cerr)
+ : std_out(&std::cout)
+ , std_err(&std::cerr)
{
}
- int app::main_utfX(int argc, char **argv)
+ int app::main_utfX(const std::vector<putf8string> &argv)
{
- auto r = this->parse(argc, argv);
- int ret = 0;
+ auto r = this->parse(argv);
- if (r != argc)
+ if (r != argv.size())
{
- this->perr("Error parsing {}\n", argv[r]);
- //FIXME: usage_short
- this->perr(this->usage());
- ret = 1;
+ this->std_err("Error parsing {}\n", argv[r]);
+ this->std_err(this->usage_short());
+ return 1;
}
- else
- ret = this->execute();
- return ret;
+ return this->execute();
}
-#ifdef _WIN32
- int app::main_utfX(int argc, wchar_t *argv[])
+ int app::main_utfX(int argc, char *argv[])
{
- std::vector<pstring> argv_vectors(argc);
- std::vector<char *> utf8_argv(argc);
+ std::vector<putf8string> arg;
+ for (std::size_t i = 0; i < narrow_cast<std::size_t>(argc); i++)
+ arg.emplace_back(putf8string(argv[i]));
- // convert arguments to UTF-8
- for (int i = 0; i < argc; i++)
- {
- argv_vectors[i] = toutf8(argv[i]);
- utf8_argv[i] = const_cast<char *>(argv_vectors[i].c_str());
- }
+ return main_utfX(arg);
+ }
+
+ int app::main_utfX(int argc, wchar_t *argv[])
+ {
+ std::vector<putf8string> arg;
+ for (std::size_t i = 0; i < narrow_cast<std::size_t>(argc); i++)
+ arg.emplace_back(putf8string(pwstring(argv[i])));
- // run utf8_main
- return main_utfX(argc, utf8_argv.data());
+ return main_utfX(arg);
}
-#endif
} // namespace plib
diff --git a/src/lib/netlist/plib/pmain.h b/src/lib/netlist/plib/pmain.h
index cfa73844f57..3213719d0d2 100644
--- a/src/lib/netlist/plib/pmain.h
+++ b/src/lib/netlist/plib/pmain.h
@@ -1,15 +1,13 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * poptions.h
- *
- */
-
-#pragma once
#ifndef PMAIN_H_
#define PMAIN_H_
+///
+/// \file poptions.h
+///
+
#include "palloc.h"
#include "poptions.h"
#include "pstream.h"
@@ -17,49 +15,52 @@
#include "putil.h"
#include <memory>
+#include <vector>
#ifdef _WIN32
#include <cwchar>
-#define PMAIN(appclass) \
-extern "C" int wmain(int argc, wchar_t *argv[]) { return plib::app::mainrun<appclass, wchar_t>(argc, argv); }
+#define PMAIN(app_class) \
+extern "C" int wmain(int argc, wchar_t *argv[]) { return plib::app::run_main<app_class, wchar_t>(argc, argv); }
#else
-#define PMAIN(appclass) \
-int main(int argc, char **argv) { return plib::app::mainrun<appclass, char>(argc, argv); }
+#define PMAIN(app_class) \
+int main(int argc, char **argv) { return plib::app::run_main<app_class, char>(argc, argv); }
#endif
namespace plib {
-/***************************************************************************
- Application
-***************************************************************************/
+ /// \brief Application class.
+ ///
class app : public options
{
public:
app();
- COPYASSIGNMOVE(app, delete)
+ PCOPYASSIGNMOVE(app, delete)
virtual ~app() = default;
virtual pstring usage() = 0;
+
+ // short version of usage, defaults to usage
+ virtual pstring usage_short() { return usage(); }
+
virtual int execute() = 0;
- plib::putf8_fmt_writer pout;
- plib::putf8_fmt_writer perr;
+ plib::putf8_fmt_writer std_out;
+ plib::putf8_fmt_writer std_err;
template <class C, typename T>
- static int mainrun(int argc, T **argv)
+ static int run_main(int argc, T **argv)
{
- auto a = plib::make_unique<C>();
- return a->main_utfX(argc, argv);
+ C application;
+ return application.main_utfX(argc, argv);
}
private:
- int main_utfX(int argc, char **argv);
-#ifdef _WIN32
+ int main_utfX(const std::vector<putf8string> &argv);
+ int main_utfX(int argc, char *argv[]);
int main_utfX(int argc, wchar_t *argv[]);
-#endif
};
@@ -67,4 +68,4 @@ namespace plib {
-#endif /* PMAIN_H_ */
+#endif // PMAIN_H_
diff --git a/src/lib/netlist/plib/pmath.h b/src/lib/netlist/plib/pmath.h
new file mode 100644
index 00000000000..96f815bcca7
--- /dev/null
+++ b/src/lib/netlist/plib/pmath.h
@@ -0,0 +1,422 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PMATH_H_
+#define PMATH_H_
+
+///
+/// \file pmath.h
+///
+
+#include "pconfig.h"
+#include "pgsl.h"
+#include "ptypes.h"
+
+#include <algorithm>
+#include <cmath>
+#include <type_traits>
+
+// `quadmath.h` included by `ptypes.h`
+
+namespace plib
+{
+
+ /// \brief Holds constants used repeatedly.
+ ///
+ /// \tparam T floating point type
+ ///
+ /// Using the structure members we can avoid magic numbers in the code.
+ /// In addition, this is a typesafe approach.
+ ///
+ template <typename T>
+ struct constants
+ {
+ static constexpr T zero() noexcept { return static_cast<T>(0); } // NOLINT
+ static constexpr T half() noexcept { return static_cast<T>(0.5); } // NOLINT
+ static constexpr T one() noexcept { return static_cast<T>(1); } // NOLINT
+ static constexpr T two() noexcept { return static_cast<T>(2); } // NOLINT
+ static constexpr T three() noexcept { return static_cast<T>(3); } // NOLINT
+ static constexpr T four() noexcept { return static_cast<T>(4); } // NOLINT
+ static constexpr T hundred()noexcept { return static_cast<T>(100); } // NOLINT
+
+ static constexpr T one_thirds() noexcept { return fraction(one(), three()); }
+ static constexpr T two_thirds() noexcept { return fraction(two(), three()); }
+
+ static constexpr T ln2() noexcept { return static_cast<T>(0.6931471805599453094172321214581766L); } // NOLINT
+ static constexpr T sqrt2() noexcept { return static_cast<T>(1.4142135623730950488016887242096982L); } // NOLINT
+ static constexpr T sqrt3() noexcept { return static_cast<T>(1.7320508075688772935274463415058723L); } // NOLINT
+ static constexpr T sqrt3_2() noexcept { return static_cast<T>(0.8660254037844386467637231707529362L); } // NOLINT
+ static constexpr T pi() noexcept { return static_cast<T>(3.1415926535897932384626433832795029L); } // NOLINT
+
+ /// \brief Electric constant of vacuum
+ ///
+ static constexpr T eps_0() noexcept { return static_cast<T>(8.854187817e-12); } // NOLINT
+
+ // \brief Relative permittivity of Silicon dioxide
+ ///
+ static constexpr T eps_SiO2() noexcept { return static_cast<T>(3.9); } // NOLINT
+
+ /// \brief Relative permittivity of Silicon
+ ///
+ static constexpr T eps_Si() noexcept { return static_cast<T>(11.7); } // NOLINT
+
+ /// \brief Boltzmann constant
+ ///
+ static constexpr T k_b() noexcept { return static_cast<T>(1.38064852e-23); } // NOLINT
+
+ /// \brief room temperature (gives VT = 0.02585 at T=300)
+ ///
+ static constexpr T T0() noexcept { return static_cast<T>(300); } // NOLINT
+
+ /// \brief Elementary charge
+ ///
+ static constexpr T Q_e() noexcept { return static_cast<T>(1.6021765314e-19); } // NOLINT
+
+ /// \brief Intrinsic carrier concentration in 1/m^3 of Silicon
+ ///
+ static constexpr T NiSi() noexcept { return static_cast<T>(1.45e16); } // NOLINT
+
+ /// \brief clearly identify magic numbers in code
+ ///
+ /// Magic numbers should be avoided. The magic member at least clearly
+ /// identifies them and makes it easier to convert them to named constants
+ /// later.
+ ///
+ template <typename V>
+ static constexpr T magic(V &&v) noexcept { return static_cast<T>(v); }
+
+ template <typename V>
+ static constexpr T fraction(V &&v1, V &&v2) noexcept { return static_cast<T>(v1 / v2); }
+ };
+
+ /// \brief typesafe reciprocal function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return reciprocal of argument
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ reciprocal(T v) noexcept
+ {
+ return constants<T>::one() / v;
+ }
+
+ /// \brief abs function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return absolute value of argument
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ abs(T v) noexcept
+ {
+ return std::abs(v);
+ }
+
+ /// \brief sqrt function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return absolute value of argument
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ sqrt(T v) noexcept
+ {
+ return std::sqrt(v);
+ }
+
+ /// \brief hypot function
+ ///
+ /// \tparam T type of the arguments
+ /// \param v1 first argument
+ /// \param v2 second argument
+ /// \return sqrt(v1*v1+v2*v2)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ hypot(T v1, T v2) noexcept
+ {
+ return std::hypot(v1, v2);
+ }
+
+ /// \brief exp function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return exp(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ exp(T v) noexcept
+ {
+ return std::exp(v);
+ }
+
+ /// \brief log function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return log(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ log(T v) noexcept
+ {
+ return std::log(v);
+ }
+
+ /// \brief tanh function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return tanh(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ tanh(T v) noexcept
+ {
+ return std::tanh(v);
+ }
+
+ /// \brief floor function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return floor(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ floor(T v) noexcept
+ {
+ return std::floor(v);
+ }
+
+ /// \brief log1p function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return log(1 + v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ log1p(T v) noexcept
+ {
+ return std::log1p(v);
+ }
+
+ /// \brief sin function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return sin(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ sin(T v) noexcept
+ {
+ return std::sin(v);
+ }
+
+ /// \brief cos function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return cos(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ cos(T v) noexcept
+ {
+ return std::cos(v);
+ }
+
+ /// \brief trunc function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return trunc(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ trunc(T v) noexcept
+ {
+ return std::trunc(v);
+ }
+
+ /// \brief signum function
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \param r optional argument, if given will return r and -r instead of 1 and -1
+ /// \return signum(v)
+ ///
+ template <typename T>
+ static constexpr std::enable_if_t<std::is_floating_point<T>::value, T>
+ signum(T v, T r = static_cast<T>(1))
+ {
+ constexpr const T z(static_cast<T>(0));
+ return (v > z) ? r : ((v < z) ? -r : v);
+ }
+
+ /// \brief pow function
+ ///
+ /// \tparam T1 type of the first argument
+ /// \tparam T2 type of the second argument
+ /// \param v argument
+ /// \param p power
+ /// \return v^p
+ ///
+ /// FIXME: limited implementation
+ ///
+ template <typename T1, typename T2>
+ static inline auto pow(T1 v, T2 p) noexcept -> decltype(std::pow(v, p))
+ {
+ return std::pow(v, p);
+ }
+
+#if (PUSE_FLOAT128)
+ static constexpr FLOAT128 reciprocal(FLOAT128 v) noexcept
+ {
+ return constants<FLOAT128>::one() / v;
+ }
+
+ static FLOAT128 abs(FLOAT128 v) noexcept { return fabsq(v); }
+
+ static FLOAT128 sqrt(FLOAT128 v) noexcept { return sqrtq(v); }
+
+ static FLOAT128 hypot(FLOAT128 v1, FLOAT128 v2) noexcept
+ {
+ return hypotq(v1, v2);
+ }
+
+ static FLOAT128 exp(FLOAT128 v) noexcept { return expq(v); }
+
+ static FLOAT128 log(FLOAT128 v) noexcept { return logq(v); }
+
+ static FLOAT128 tanh(FLOAT128 v) noexcept { return tanhq(v); }
+
+ static FLOAT128 floor(FLOAT128 v) noexcept { return floorq(v); }
+
+ static FLOAT128 log1p(FLOAT128 v) noexcept { return log1pq(v); }
+
+ static FLOAT128 sin(FLOAT128 v) noexcept { return sinq(v); }
+
+ static FLOAT128 cos(FLOAT128 v) noexcept { return cosq(v); }
+
+ static FLOAT128 trunc(FLOAT128 v) noexcept { return truncq(v); }
+
+ template <typename T>
+ static FLOAT128 pow(FLOAT128 v, T p) noexcept
+ {
+ return powq(v, static_cast<FLOAT128>(p));
+ }
+
+ static FLOAT128 pow(FLOAT128 v, int p) noexcept
+ {
+ if (p == 2)
+ return v * v;
+ else
+ return powq(v, static_cast<FLOAT128>(p));
+ }
+
+#endif
+
+ /// \brief is argument a power of two?
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument to be checked
+ /// \return true if argument is a power of two
+ ///
+ template <typename T>
+ constexpr bool is_pow2(T v) noexcept
+ {
+ static_assert(is_integral<T>::value, "is_pow2 needs integer arguments");
+ return !(v & (v - 1));
+ }
+
+ /// \brief return absolute value of signed argument
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return absolute value of argument
+ ///
+ template <typename T>
+ constexpr std::enable_if_t<
+ plib::is_integral<T>::value && plib::is_signed<T>::value, T>
+ abs(T v) noexcept
+ {
+ return v < 0 ? -v : v;
+ }
+
+ /// \brief return absolute value of unsigned argument
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return argument since it has no sign
+ ///
+ template <typename T>
+ constexpr std::enable_if_t<
+ plib::is_integral<T>::value && plib::is_unsigned<T>::value, T>
+ abs(T v) noexcept
+ {
+ return v;
+ }
+
+ /// \brief return greatest common denominator
+ ///
+ /// Function returns the greatest common denominator of m and n. For known
+ /// arguments, this function also works at compile time.
+ ///
+ /// \tparam M type of the first argument
+ /// \tparam N type of the second argument
+ /// \param m first argument
+ /// \param n first argument
+ /// \return greatest common denominator of m and n
+ ///
+ template <typename M, typename N>
+ constexpr typename std::common_type<M, N>::type
+ gcd(M m, N n) noexcept // NOLINT(misc-no-recursion)
+ {
+ static_assert(plib::is_integral<M>::value, "gcd: M must be an integer");
+ static_assert(plib::is_integral<N>::value, "gcd: N must be an integer");
+
+ return m == 0 ? plib::abs(n) : n == 0 ? plib::abs(m) : gcd(n, m % n);
+ }
+
+ /// \brief return least common multiple
+ ///
+ /// Function returns the least common multiple of m and n. For known
+ /// arguments, this function also works at compile time.
+ ///
+ /// \tparam M type of the first argument
+ /// \tparam N type of the second argument
+ /// \param m first argument
+ /// \param n first argument
+ /// \return least common multiple of m and n
+ ///
+ template <typename M, typename N>
+ constexpr typename std::common_type<M, N>::type lcm(M m, N n) noexcept
+ {
+ static_assert(plib::is_integral<M>::value, "lcm: M must be an integer");
+ static_assert(plib::is_integral<N>::value, "lcm: N must be an integer");
+
+ return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n)
+ : 0;
+ }
+
+ template <class T>
+ constexpr const T &clamp(const T &v, const T &low, const T &high)
+ {
+ gsl_Expects(high >= low);
+ return (v < low) ? low : (high < v) ? high : v;
+ }
+
+ static_assert(noexcept(constants<double>::one()),
+ "Not evaluated as constexpr");
+
+} // namespace plib
+
+#endif // PMATH_H_
diff --git a/src/lib/netlist/plib/pmatrix2d.h b/src/lib/netlist/plib/pmatrix2d.h
index 7d6072fc358..4c167e9baf8 100644
--- a/src/lib/netlist/plib/pmatrix2d.h
+++ b/src/lib/netlist/plib/pmatrix2d.h
@@ -1,85 +1,248 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pmatrix2d.h
- *
- * NxM regular matrix
- *
- */
#ifndef PMATRIX2D_H_
#define PMATRIX2D_H_
+///
+/// \file pmatrix2d.h
+///
+
#include "palloc.h"
#include <algorithm>
-#include <cmath>
-//#include <cstdlib>
#include <type_traits>
#include <vector>
namespace plib
{
-
- template<typename T, typename A = aligned_allocator<T>>
+ template<typename A, typename T>
class pmatrix2d
{
public:
+ using size_type = std::size_t;
+ using arena_type = A;
+ using allocator_type = typename A::template allocator_type<T, PALIGN_VECTOROPT>;
+
+ static constexpr const size_type align_size = align_traits<allocator_type>::align_size;
+ static constexpr const size_type stride_size = align_traits<allocator_type>::stride_size;
+
using value_type = T;
- using allocator_type = A;
+ using reference = T &;
+ using const_reference = const T &;
+
+ using pointer = T *;
+ using const_pointer = const T *;
- static constexpr const std::size_t align_size = align_traits<A>::align_size;
- static constexpr const std::size_t stride_size = align_traits<A>::stride_size;
- pmatrix2d()
- : m_N(0), m_M(0), m_stride(8), m_v()
+ pmatrix2d(A &arena) noexcept
+ : m_N(0), m_M(0), m_stride(8), m_v(nullptr), m_a(arena)
{
}
- pmatrix2d(std::size_t N, std::size_t M)
- : m_N(N), m_M(M), m_v()
+ pmatrix2d(A &arena, size_type N, size_type M)
+ : m_N(N), m_M(M), m_v(), m_a(arena)
{
+ gsl_Expects(N>0);
+ gsl_Expects(M>0);
m_stride = ((M + stride_size-1) / stride_size) * stride_size;
- m_v.resize(N * m_stride);
+ m_v = m_a.allocate(N * m_stride);
+ for (std::size_t i = 0; i < N * m_stride; i++)
+ ::new(&m_v[i]) T();
}
- void resize(std::size_t N, std::size_t M)
+ pmatrix2d(const pmatrix2d &) = delete;
+ pmatrix2d &operator=(const pmatrix2d &) = delete;
+ pmatrix2d(pmatrix2d &&) = delete;
+ pmatrix2d &operator=(pmatrix2d &&) = delete;
+
+ ~pmatrix2d()
{
+ if (m_v != nullptr)
+ {
+ for (std::size_t i = 0; i < m_N * m_stride; i++)
+ (&m_v[i])->~T();
+ m_a.deallocate(m_v, m_N * m_stride);
+ }
+ }
+
+ void resize(size_type N, size_type M)
+ {
+ gsl_Expects(N>0);
+ gsl_Expects(M>0);
+ if (m_v != nullptr)
+ {
+ for (std::size_t i = 0; i < N * m_stride; i++)
+ (&m_v[i])->~T();
+ m_a.deallocate(m_v, N * m_stride);
+ }
m_N = N;
m_M = M;
m_stride = ((M + stride_size-1) / stride_size) * stride_size;
- m_v.resize(N * m_stride);
+ m_v = m_a.allocate(N * m_stride);
+ for (std::size_t i = 0; i < N * m_stride; i++)
+ ::new(&m_v[i]) T();
}
- C14CONSTEXPR T * operator[] (std::size_t row) noexcept
+ constexpr pointer operator[] (size_type row) noexcept
{
- return assume_aligned_ptr<T, align_size>(&m_v[m_stride * row]);
+ return &m_v[m_stride * row];
}
- constexpr const T * operator[] (std::size_t row) const noexcept
+ constexpr const_pointer operator[] (size_type row) const noexcept
{
- return assume_aligned_ptr<T, align_size>(&m_v[m_stride * row]);
+ return &m_v[m_stride * row];
}
- T & operator()(std::size_t r, std::size_t c) noexcept
+ reference operator()(size_type r, size_type c) noexcept
{
return (*this)[r][c];
}
- const T & operator()(std::size_t r, std::size_t c) const noexcept
+ const_reference operator()(size_type r, size_type c) const noexcept
{
return (*this)[r][c];
}
+ // for compatibility with vrl variant
+ void set(size_type r, size_type c, const value_type &v) noexcept
+ {
+ (*this)[r][c] = v;
+ }
+
+ pointer data() noexcept
+ {
+ return m_v;
+ }
+
+ const_pointer data() const noexcept
+ {
+ return m_v;
+ }
+
+ size_type didx(size_type r, size_type c) const noexcept
+ {
+ return m_stride * r + c;
+ }
private:
- std::size_t m_N;
- std::size_t m_M;
- std::size_t m_stride;
+ size_type m_N;
+ size_type m_M;
+ size_type m_stride;
+
+ T * __restrict m_v;
+
+ allocator_type m_a;
+ };
+
+ // variable row length matrix
+ template<typename A, typename T>
+ class pmatrix2d_vrl
+ {
+ public:
+ using size_type = std::size_t;
+ using value_type = T;
+ using arena_type = A;
+
+ using allocator_type = typename A::template allocator_type<T, PALIGN_VECTOROPT>;
+
+ static constexpr const size_type align_size = align_traits<allocator_type>::align_size;
+ static constexpr const size_type stride_size = align_traits<allocator_type>::stride_size;
+
+ pmatrix2d_vrl(A &arena) noexcept
+ : m_N(0), m_M(0), m_row(arena), m_v(arena)
+ {
+ }
+
+ pmatrix2d_vrl(A &arena, size_type N, size_type M)
+ : m_N(N), m_M(M), m_row(arena), m_v(arena)
+ {
+ m_row.resize(N + 1, 0);
+ m_v.resize(N); //FIXME
+ }
+
+ pmatrix2d_vrl(const pmatrix2d_vrl &) = default;
+ pmatrix2d_vrl &operator=(const pmatrix2d_vrl &) = default;
+ pmatrix2d_vrl(pmatrix2d_vrl &&) noexcept = default;
+ pmatrix2d_vrl &operator=(pmatrix2d_vrl &&) noexcept(!compile_info::clang_noexcept_issue::value) = default;
+
+ ~pmatrix2d_vrl() = default;
- std::vector<T, A> m_v;
+ void resize(size_type N, size_type M)
+ {
+ m_N = N;
+ m_M = M;
+ m_row.resize(N + 1);
+ for (std::size_t i = 0; i < m_N; i++)
+ m_row[i] = 0;
+ m_v.resize(N); //FIXME
+ }
+
+ constexpr T * operator[] (size_type row) noexcept
+ {
+ return &(m_v[m_row[row]]);
+ }
+
+ constexpr const T * operator[] (size_type row) const noexcept
+ {
+ return &(m_v[m_row[row]]);
+ }
+
+ //FIXME: no check!
+ T & operator()(size_type r, size_type c) noexcept
+ {
+ return (*this)[r][c];
+ }
+
+ void set(size_type r, size_type c, const T &v) noexcept
+ {
+ if (c + m_row[r] >= m_row[r + 1])
+ {
+ m_v.insert(m_v.begin() + narrow_cast<std::ptrdiff_t>(m_row[r+1]), v);
+ for (size_type i = r + 1; i <= m_N; i++)
+ m_row[i] = m_row[i] + 1;
+ }
+ else
+ (*this)[r][c] = v;
+ }
+
+ //FIXME: no check!
+ const T & operator()(size_type r, size_type c) const noexcept
+ {
+ return (*this)[r][c];
+ }
+
+ T * data() noexcept
+ {
+ return m_v.data();
+ }
+
+ const T * data() const noexcept
+ {
+ return m_v.data();
+ }
+
+ //FIXME: no check!
+ constexpr size_type didx(size_type r, size_type c) const noexcept
+ {
+ return m_row[r] + c;
+ }
+
+ constexpr size_type col_count(size_type row) const noexcept
+ {
+ return m_row[row + 1] - m_row[row];
+ }
+
+ size_type tx() const { return m_v.size(); }
+ private:
+
+ size_type m_N;
+ size_type m_M;
+ plib::arena_vector<A, size_type, PALIGN_VECTOROPT> m_row;
+ plib::arena_vector<A, T, PALIGN_VECTOROPT> m_v;
};
+
} // namespace plib
-#endif /* MAT_CR_H_ */
+#endif // PMATRIX2D_H_
diff --git a/src/lib/netlist/plib/pmatrix_cr.h b/src/lib/netlist/plib/pmatrix_cr.h
new file mode 100644
index 00000000000..74175e07101
--- /dev/null
+++ b/src/lib/netlist/plib/pmatrix_cr.h
@@ -0,0 +1,648 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PMATRIX_CR_H_
+#define PMATRIX_CR_H_
+
+///
+/// \file pmatrix_cr.h
+///
+/// Compressed row format matrices
+///
+
+#include "palloc.h"
+#include "parray.h"
+#include "pconfig.h"
+#include "pexception.h"
+#include "pfmtlog.h"
+#include "pmatrix2d.h"
+#include "pomp.h"
+#include "ptypes.h"
+#include "putil.h" // <- container::contains
+
+#include <algorithm>
+#include <array>
+#include <type_traits>
+#include <vector>
+
+namespace plib
+{
+
+ template<typename ARENA, typename T, int N, typename C = uint16_t>
+ struct pmatrix_cr
+ {
+ using index_type = C;
+ using value_type = T;
+
+ static constexpr const int NSQ = (N < 0 ? -N * N : N * N);
+ static constexpr const int Np1 = (N == 0) ? 0 : (N < 0 ? N - 1 : N + 1);
+
+ pmatrix_cr(const pmatrix_cr &) = default;
+ pmatrix_cr &operator=(const pmatrix_cr &) = default;
+ pmatrix_cr(pmatrix_cr &&) noexcept(std::is_nothrow_move_constructible<parray<value_type, NSQ>>::value) = default;
+ pmatrix_cr &operator=(pmatrix_cr &&) noexcept(std::is_nothrow_move_assignable<parray<value_type, NSQ>>::value && std::is_nothrow_move_assignable<pmatrix2d_vrl<ARENA, index_type>>::value) = default;
+
+ enum constants_e
+ {
+ FILL_INFINITY = 9999999
+ };
+
+ // FIXME: these should be private
+ // NOLINTNEXTLINE
+ parray<index_type, N> diagonal; // diagonal index pointer n
+ // NOLINTNEXTLINE
+ parray<index_type, Np1> row_idx; // row index pointer n + 1
+ // NOLINTNEXTLINE
+ parray<index_type, NSQ> col_idx; // column index array nz_num, initially (n * n)
+ // NOLINTNEXTLINE
+ parray<value_type, NSQ> A; // Matrix elements nz_num, initially (n * n)
+ // NOLINTNEXTLINE
+ std::size_t nz_num;
+
+ explicit pmatrix_cr(ARENA &arena, std::size_t n)
+ : diagonal(n)
+ , row_idx(n+1)
+ , col_idx(n*n)
+ , A(n*n)
+ , nz_num(0)
+ //, nzbd(n * (n+1) / 2)
+ , m_nzbd(arena, n, n)
+ , m_size(n)
+ {
+ for (std::size_t i=0; i<n+1; i++)
+ {
+ row_idx[i] = 0;
+ }
+ }
+
+ ~pmatrix_cr() = default;
+
+ constexpr std::size_t size() const noexcept { return (N>0) ? narrow_cast<std::size_t>(N) : m_size; }
+
+ constexpr void clear() noexcept
+ {
+ nz_num = 0;
+ for (std::size_t i=0; i < size() + 1; i++)
+ row_idx[i] = 0;
+ }
+
+ constexpr void set_scalar(T scalar) noexcept
+ {
+ for (std::size_t i=0, e=nz_num; i<e; i++)
+ A[i] = scalar;
+ }
+
+ constexpr void set_row_scalar(C r, T val) noexcept
+ {
+ C ri = row_idx[r];
+ while (ri < row_idx[r+1])
+ A[ri++] = val;
+ }
+
+ constexpr void set(C r, C c, T val) noexcept
+ {
+ C ri = row_idx[r];
+ while (ri < row_idx[r+1] && col_idx[ri] < c)
+ ri++;
+ // we have the position now;
+ if (ri < row_idx[r+1] && col_idx[ri] == c)
+ A[ri] = val;
+ else
+ {
+ for (C i = nz_num; i>ri; i--)
+ {
+ A[i] = A[i-1];
+ col_idx[i] = col_idx[i-1];
+ }
+ A[ri] = val;
+ col_idx[ri] = c;
+ for (C i = r + 1; i < size() + 1; i++)
+ row_idx[i]++;
+ nz_num++;
+ if (c==r)
+ diagonal[r] = ri;
+ }
+ }
+
+ template <typename M>
+ void build_from_fill_mat(const M &f, std::size_t max_fill = FILL_INFINITY - 1,
+ std::size_t band_width = FILL_INFINITY) noexcept(false)
+ {
+ C nz = 0;
+ if (nz_num != 0)
+ throw pexception("build_from_mat only allowed on empty CR matrix");
+ for (std::size_t k=0; k < size(); k++)
+ {
+ row_idx[k] = nz;
+
+ for (std::size_t j=0; j < size(); j++)
+ if (f[k][j] <= max_fill && plib::abs(narrow_cast<int>(k)-narrow_cast<int>(j)) <= narrow_cast<int>(band_width))
+ {
+ col_idx[nz] = narrow_cast<C>(j);
+ if (j == k)
+ diagonal[k] = nz;
+ nz++;
+ }
+ }
+
+ row_idx[size()] = nz;
+ nz_num = nz;
+
+ // build nzbd
+
+ for (std::size_t k=0; k < size(); k++)
+ {
+ for (std::size_t j=k + 1; j < size(); j++)
+ if (f[j][k] < FILL_INFINITY)
+ m_nzbd.set(k, m_nzbd.col_count(k), narrow_cast<C>(j));
+ m_nzbd.set(k, m_nzbd.col_count(k), 0); // end of sequence
+ }
+
+ }
+
+ template <typename VTV, typename VTR>
+ void mult_vec(VTR & res, const VTV & x) const noexcept
+ {
+
+ // res = A * x
+ // this is a bit faster than the version above
+ std::size_t row = 0;
+ std::size_t k = 0;
+ const std::size_t oe = nz_num;
+ while (k < oe)
+ {
+ T tmp = plib::constants<T>::zero();
+ const std::size_t e = row_idx[row+1];
+ for (; k < e; k++)
+ tmp += A[k] * x[col_idx[k]];
+ res[row++] = tmp;
+ }
+ }
+
+ // throws error if P(source)>P(destination)
+ template <typename LUMAT>
+ void slim_copy_from(LUMAT & src) noexcept(false)
+ {
+ for (std::size_t r=0; r<src.size(); r++)
+ {
+ C dp = row_idx[r];
+ for (C sp = src.row_idx[r]; sp < src.row_idx[r+1]; sp++)
+ {
+ // advance dp to source column and fill 0s if necessary
+ while (col_idx[dp] < src.col_idx[sp])
+ A[dp++] = 0;
+ if (row_idx[r+1] <= dp || col_idx[dp] != src.col_idx[sp])
+ throw pexception("slim_copy_from error");
+ A[dp++] = src.A[sp];
+ }
+ // fill remaining elements in row
+ while (dp < row_idx[r+1])
+ A[dp++] = 0;
+ }
+ }
+
+ // only copies common elements
+ template <typename LUMAT>
+ void reduction_copy_from(LUMAT & src) noexcept
+ {
+ C sp(0);
+ for (std::size_t r=0; r<src.size(); r++)
+ {
+ C dp(row_idx[r]);
+ while(sp < src.row_idx[r+1])
+ {
+ // advance dp to source column and fill 0s if necessary
+ if (col_idx[dp] < src.col_idx[sp])
+ A[dp++] = 0;
+ else if (col_idx[dp] == src.col_idx[sp])
+ A[dp++] = src.A[sp++];
+ else
+ sp++;
+ }
+ // fill remaining elements in row
+ while (dp < row_idx[r+1])
+ A[dp++] = 0;
+ }
+ }
+
+ // no checks at all - may crash
+ template <typename LUMAT>
+ void raw_copy_from(LUMAT & src) noexcept
+ {
+ for (std::size_t k = 0; k < nz_num; k++)
+ A[k] = src.A[k];
+ }
+
+ constexpr index_type * nzbd(std::size_t row) noexcept { return m_nzbd[row]; }
+ constexpr std::size_t nzbd_count(std::size_t row) noexcept { return m_nzbd.col_count(row) - 1; }
+ protected:
+ // FIXME: this should be private
+ // NOLINTNEXTLINE
+ //parray<std::vector<index_type>, N > m_nzbd; // Support for gaussian elimination
+ pmatrix2d_vrl<ARENA, index_type> m_nzbd; // Support for gaussian elimination
+ private:
+ //parray<C, N < 0 ? -N * (N-1) / 2 : N * (N+1) / 2 > nzbd; // Support for gaussian elimination
+ std::size_t m_size;
+ };
+
+ template<typename B>
+ struct pGEmatrix_cr : public B
+ {
+ using base_type = B;
+ using index_type = typename base_type::index_type;
+
+ pGEmatrix_cr(const pGEmatrix_cr &) = default;
+ pGEmatrix_cr &operator=(const pGEmatrix_cr &) = default;
+ pGEmatrix_cr(pGEmatrix_cr &&) noexcept(std::is_nothrow_move_constructible<base_type>::value) = default;
+ pGEmatrix_cr &operator=(pGEmatrix_cr &&) noexcept(std::is_nothrow_move_assignable<base_type>::value) = default;
+
+ template<typename ARENA>
+ explicit pGEmatrix_cr(ARENA &arena, std::size_t n)
+ : B(arena, n)
+ {
+ }
+
+ ~pGEmatrix_cr() = default;
+
+ template <typename M>
+ std::pair<std::size_t, std::size_t> gaussian_extend_fill_mat(M &fill) noexcept
+ {
+ std::size_t ops = 0;
+ std::size_t fill_max = 0;
+
+ for (std::size_t k = 0; k < fill.size(); k++)
+ {
+ ops++; // 1/A(k,k)
+ for (std::size_t row = k + 1; row < fill.size(); row++)
+ {
+ if (fill[row][k] < base_type::FILL_INFINITY)
+ {
+ ops++;
+ for (std::size_t col = k + 1; col < fill[row].size(); col++)
+ //if (fill[k][col] < FILL_INFINITY)
+ {
+ auto f = std::min(fill[row][col], 1 + fill[row][k] + fill[k][col]);
+ if (f < base_type::FILL_INFINITY)
+ {
+ if (f > fill_max)
+ fill_max = f;
+ ops += 2;
+ }
+ fill[row][col] = f;
+ }
+ }
+ }
+ }
+ build_parallel_gaussian_execution_scheme(fill);
+ return { fill_max, ops };
+ }
+
+ template <typename V>
+ void gaussian_elimination(V & RHS) noexcept
+ {
+ const std::size_t iN = base_type::size();
+
+ for (std::size_t i = 0; i < iN - 1; i++)
+ {
+ std::size_t nzbdp = 0;
+ std::size_t pi = base_type::diagonal[i];
+ auto f = reciprocal(base_type::A[pi++]);
+ const std::size_t piie = base_type::row_idx[i+1];
+
+ const auto *nz = base_type::m_nzbd[i];
+ while (auto j = nz[nzbdp++]) // NOLINT(bugprone-infinite-loop)
+ {
+ // proceed to column i
+
+ std::size_t pj = base_type::row_idx[j];
+ std::size_t pje = base_type::row_idx[j+1];
+
+ while (base_type::col_idx[pj] < i)
+ pj++;
+
+ const typename base_type::value_type f1 = - base_type::A[pj++] * f;
+
+ // subtract row i from j
+ // fill-in available assumed, i.e. matrix was prepared
+
+ for (std::size_t pii = pi; pii<piie && pj < pje; pii++)
+ {
+ while (base_type::col_idx[pj] < base_type::col_idx[pii])
+ pj++;
+ if (base_type::col_idx[pj] == base_type::col_idx[pii])
+ base_type::A[pj++] += base_type::A[pii] * f1;
+ }
+
+ RHS[j] += f1 * RHS[i];
+ }
+ }
+ }
+
+ int get_parallel_level(std::size_t k) const noexcept
+ {
+ for (std::size_t i = 0; i < m_ge_par.size(); i++)
+ if (plib::container::contains( m_ge_par[i], k))
+ return narrow_cast<int>(i);
+ return -1;
+ }
+
+ template <typename V>
+ void gaussian_elimination_parallel(V & RHS) noexcept
+ {
+ //printf("omp: %ld %d %d\n", m_ge_par.size(), nz_num, (int)m_ge_par[m_ge_par.size()-2].size());
+ for (auto l = 0UL; l < m_ge_par.size(); l++)
+ plib::omp::for_static(base_type::nz_num, 0UL, m_ge_par[l].size(), [this, &RHS, &l] (unsigned ll)
+ {
+ auto &i = m_ge_par[l][ll];
+ {
+ std::size_t nzbdp = 0;
+ std::size_t pi = base_type::diagonal[i];
+ const auto f = reciprocal(base_type::A[pi++]);
+ const std::size_t piie = base_type::row_idx[i+1];
+ const auto &nz = base_type::nzbd[i];
+
+ while (auto j = nz[nzbdp++])
+ {
+ // proceed to column i
+
+ std::size_t pj = base_type::row_idx[j];
+
+ while (base_type::col_idx[pj] < i)
+ pj++;
+
+ auto f1 = - base_type::A[pj++] * f;
+
+ // subtract row i from j
+ // fill-in available assumed, i.e. matrix was prepared
+ for (std::size_t pii = pi; pii<piie; pii++)
+ {
+ while (base_type::col_idx[pj] < base_type::col_idx[pii])
+ pj++;
+ if (base_type::col_idx[pj] == base_type::col_idx[pii])
+ base_type::A[pj++] += base_type::A[pii] * f1;
+ }
+ RHS[j] += f1 * RHS[i];
+ }
+ }
+ });
+ }
+
+ template <typename V1, typename V2>
+ void gaussian_back_substitution(V1 &V, const V2 &RHS) noexcept
+ {
+ const std::size_t iN = base_type::size();
+ // row n-1
+ V[iN - 1] = RHS[iN - 1] / base_type::A[base_type::diagonal[iN - 1]];
+
+ for (std::size_t j = iN - 1; j-- > 0;)
+ {
+ typename base_type::value_type tmp = 0;
+ const auto diagonal_j = base_type::diagonal[j];
+ const std::size_t e = base_type::row_idx[j+1];
+ for (std::size_t pk = diagonal_j + 1; pk < e; pk++)
+ tmp += base_type::A[pk] * V[base_type::col_idx[pk]];
+ V[j] = (RHS[j] - tmp) / base_type::A[diagonal_j];
+ }
+ }
+
+ template <typename V1>
+ void gaussian_back_substitution(V1 &V) noexcept
+ {
+ const std::size_t iN = base_type::size();
+ // row n-1
+ V[iN - 1] = V[iN - 1] / base_type::A[base_type::diagonal[iN - 1]];
+
+ for (std::size_t j = iN - 1; j-- > 0;)
+ {
+ typename base_type::value_type tmp = 0;
+ const auto diagonal_j = base_type::diagonal[j];
+ const std::size_t e = base_type::row_idx[j+1];
+ for (std::size_t pk = diagonal_j + 1; pk < e; pk++)
+ tmp += base_type::A[pk] * V[base_type::col_idx[pk]];
+ V[j] = (V[j] - tmp) / base_type::A[diagonal_j];
+ }
+ }
+
+ private:
+ template <typename M>
+ void build_parallel_gaussian_execution_scheme(const M &fill) noexcept
+ {
+ // calculate parallel scheme for gaussian elimination
+ std::vector<std::vector<std::size_t>> rt(base_type::size());
+ for (std::size_t k = 0; k < base_type::size(); k++)
+ {
+ for (std::size_t j = k+1; j < base_type::size(); j++)
+ {
+ if (fill[j][k] < base_type::FILL_INFINITY)
+ {
+ rt[k].push_back(j);
+ }
+ }
+ }
+
+ std::vector<std::size_t> levGE(base_type::size(), 0);
+ std::size_t cl = 0;
+
+ for (std::size_t k = 0; k < base_type::size(); k++ )
+ {
+ if (levGE[k] >= cl)
+ {
+ std::vector<std::size_t> t = rt[k];
+ for (std::size_t j = k+1; j < base_type::size(); j++ )
+ {
+ bool overlap = false;
+ // is there overlap
+ if (plib::container::contains(t, j))
+ overlap = true;
+ for (auto &x : rt[j])
+ if (plib::container::contains(t, x))
+ {
+ overlap = true;
+ break;
+ }
+ if (overlap)
+ levGE[j] = cl + 1;
+ else
+ {
+ t.push_back(j);
+ for (auto &x : rt[j])
+ t.push_back(x);
+ }
+ }
+ cl++;
+ }
+ }
+
+ m_ge_par.clear();
+ m_ge_par.resize(cl+1);
+ for (std::size_t k = 0; k < base_type::size(); k++)
+ m_ge_par[levGE[k]].push_back(k);
+ //for (std::size_t k = 0; k < m_ge_par.size(); k++)
+ // printf("%d %d\n", (int) k, (int) m_ge_par[k].size());
+ }
+ std::vector<std::vector<std::size_t>> m_ge_par; // parallel execution support for Gauss
+ };
+
+ template<typename B>
+ struct pLUmatrix_cr : public B
+ {
+ using base_type = B;
+ using index_type = typename base_type::index_type;
+
+ pLUmatrix_cr(const pLUmatrix_cr &) = default;
+ pLUmatrix_cr &operator=(const pLUmatrix_cr &) = default;
+ pLUmatrix_cr(pLUmatrix_cr &&) noexcept(std::is_nothrow_move_constructible<base_type>::value) = default;
+ pLUmatrix_cr &operator=(pLUmatrix_cr &&) noexcept(std::is_nothrow_move_assignable<base_type>::value) = default;
+
+ template<typename ARENA>
+ explicit pLUmatrix_cr(ARENA &arena, std::size_t n)
+ : B(arena, n)
+ , ilu_rows(n+1)
+ , m_ILUp(0)
+ {
+ }
+
+ ~pLUmatrix_cr() = default;
+
+ template <typename M>
+ void build(M &fill, std::size_t ilup) noexcept(false)
+ {
+ std::size_t p(0);
+ // build ilu_rows
+ for (decltype(fill.size()) i=1; i < fill.size(); i++)
+ {
+ bool found(false);
+ for (decltype(fill.size()) k = 0; k < i; k++)
+ {
+ // if (fill[i][k] < base::FILL_INFINITY)
+ if (fill[i][k] <= ilup)
+ {
+ // assume A[k][k]!=0
+ for (decltype(fill.size()) j=k+1; j < fill.size(); j++)
+ {
+ auto f = std::min(fill[i][j], 1 + fill[i][k] + fill[k][j]);
+ if (f <= ilup)
+ fill[i][j] = f;
+ }
+ found = true;
+ }
+ }
+ if (found)
+ ilu_rows[p++] = narrow_cast<index_type>(i);
+ }
+ ilu_rows[p] = 0; // end of array
+ this->build_from_fill_mat(fill, ilup); //, m_band_width); // ILU(2)
+ m_ILUp = ilup;
+ }
+
+ /// \brief incomplete LU Factorization.
+ ///
+ /// We are following http://de.wikipedia.org/wiki/ILU-Zerlegung here.
+ ///
+ /// The result is stored in matrix LU
+ ///
+ /// For i = 1,...,N-1
+ /// For k = 0, ... , i - 1
+ /// If a[i,k] != 0
+ /// a[i,k] = a[i,k] / a[k,k]
+ /// For j = k + 1, ... , N - 1
+ /// If a[i,j] != 0
+ /// a[i,j] = a[i,j] - a[i,k] * a[k,j]
+ /// j=j+1
+ /// k=k+1
+ /// i=i+1
+ ///
+ void incomplete_LU_factorization(const base_type &mat) noexcept
+ {
+ if (m_ILUp < 1)
+ this->raw_copy_from(mat);
+ else
+ this->reduction_copy_from(mat);
+
+ std::size_t p(0);
+ while (auto i = ilu_rows[p++]) // NOLINT(bugprone-infinite-loop)
+ {
+ const auto p_i_end = base_type::row_idx[i + 1];
+ // loop over all columns k left of diagonal in row i
+ //if (row_idx[i] < diagonal[i])
+ // printf("occ %d\n", (int)i);
+ for (auto i_k = base_type::row_idx[i]; i_k < base_type::diagonal[i]; i_k++)
+ {
+ const index_type k(base_type::col_idx[i_k]);
+ const index_type p_k_end(base_type::row_idx[k + 1]);
+ const typename base_type::value_type LUp_i_k = base_type::A[i_k] = base_type::A[i_k] / base_type::A[base_type::diagonal[k]];
+
+ std::size_t k_j(base_type::diagonal[k] + 1);
+ std::size_t i_j(i_k + 1);
+
+ while (i_j < p_i_end && k_j < p_k_end ) // pj = (i, j)
+ {
+ // we can assume that within a row ja increases continuously
+ const index_type c_i_j(base_type::col_idx[i_j]); // row i, column j
+ const index_type c_k_j(base_type::col_idx[k_j]); // row k, column j
+
+ if (c_k_j == c_i_j)
+ base_type::A[i_j] -= LUp_i_k * base_type::A[k_j];
+ k_j += (c_k_j <= c_i_j ? 1 : 0);
+ i_j += (c_k_j >= c_i_j ? 1 : 0);
+
+ }
+ }
+ }
+ }
+
+
+ /// \brief Solve a linear equation
+ ///
+ /// Solve a linear equation Ax = r
+ ///
+ /// where
+ /// A = L*U
+ ///
+ /// L unit lower triangular
+ /// U upper triangular
+ ///
+ /// ==> LUx = r
+ ///
+ /// ==> Ux = L⁻¹ r = w
+ ///
+ /// ==> r = Lw
+ ///
+ /// This can be solved for w using backwards elimination in L.
+ ///
+ /// Now Ux = w
+ ///
+ /// This can be solved for x using backwards elimination in U.
+ ///
+ template <typename R>
+ void solveLU (R &r) noexcept
+ {
+ for (std::size_t i = 1; i < base_type::size(); ++i )
+ {
+ typename base_type::value_type tmp(0);
+ const index_type j1(base_type::row_idx[i]);
+ const index_type j2(base_type::diagonal[i]);
+
+ for (auto j = j1; j < j2; ++j )
+ tmp += base_type::A[j] * r[base_type::col_idx[j]];
+ r[i] -= tmp;
+ }
+ // i now is equal to n;
+ for (std::size_t i = base_type::size(); i-- > 0; )
+ {
+ typename base_type::value_type tmp(0);
+ const index_type di(base_type::diagonal[i]);
+ const index_type j2(base_type::row_idx[i+1]);
+ for (std::size_t j = di + 1; j < j2; j++ )
+ tmp += base_type::A[j] * r[base_type::col_idx[j]];
+ r[i] = (r[i] - tmp) / base_type::A[di];
+ }
+ }
+ private:
+ parray<index_type, base_type::Np1> ilu_rows;
+ std::size_t m_ILUp;
+ };
+
+} // namespace plib
+
+#endif // PMATRIX_CR_H_
diff --git a/src/lib/netlist/plib/pmempool.h b/src/lib/netlist/plib/pmempool.h
index 769d2f653b5..29712601237 100644
--- a/src/lib/netlist/plib/pmempool.h
+++ b/src/lib/netlist/plib/pmempool.h
@@ -1,21 +1,21 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pmempool.h
- *
- */
#ifndef PMEMPOOL_H_
#define PMEMPOOL_H_
+///
+/// \file pmempool.h
+///
+
#include "palloc.h"
-#include "pstream.h"
-#include "pstring.h"
+#include "pconfig.h"
+#include "pstream.h" // perrlogger
+//#include "pstring.h"
#include "ptypes.h"
-#include "putil.h"
+//#include "putil.h"
#include <algorithm>
-//#include <cstddef>
#include <memory>
#include <unordered_map>
#include <utility>
@@ -27,30 +27,39 @@ namespace plib {
// Memory pool
//============================================================
- class mempool
+ template <typename BASEARENA, std::size_t MINALIGN = PALIGN_MIN_SIZE>
+ class mempool_arena : public arena_base<mempool_arena<BASEARENA, MINALIGN>, MINALIGN, false, false>
{
+ public:
+
+ using size_type = typename BASEARENA::size_type;
+ using base_type = arena_base<mempool_arena<BASEARENA, MINALIGN>, MINALIGN, false, false>;
+
+ template <class T>
+ using base_allocator_type = typename BASEARENA::template allocator_type<T>;
+ using block_align = std::integral_constant<size_t, 1024>;
+
+ mempool_arena(size_t min_alloc = (1<<21));
+
+ PCOPYASSIGNMOVE(mempool_arena, delete)
+
+ ~mempool_arena();
+
+ void *allocate(size_t align, size_t size);
+
+ void deallocate(void *ptr, std::size_t alignment, size_t size) noexcept;
+
+ bool operator ==(const mempool_arena &rhs) const noexcept { return this == &rhs; }
+
private:
struct block
{
- block(mempool *mp, std::size_t min_bytes)
- : m_num_alloc(0)
- , m_cur(0)
- , m_data(nullptr)
- , m_mempool(mp)
- {
- min_bytes = std::max(mp->m_min_alloc, min_bytes);
- m_free = min_bytes;
- std::size_t alloc_bytes = (min_bytes + mp->m_min_align); // - 1); // & ~(mp->m_min_align - 1);
- //m_data_allocated = ::operator new(alloc_bytes);
- m_data_allocated = new char[alloc_bytes];
- void *r = m_data_allocated;
- std::align(mp->m_min_align, min_bytes, r, alloc_bytes);
- m_data = reinterpret_cast<char *>(r);
- }
+ block(mempool_arena &mp, size_type min_bytes);
~block()
{
//::operator delete(m_data_allocated);
- delete [] m_data_allocated;
+ //delete [] m_data_allocated;
+ m_mempool.base_arena().deallocate(m_data_allocated, mempool_arena::block_align(), m_bytes_allocated);
}
block(const block &) = delete;
@@ -58,151 +67,157 @@ namespace plib {
block &operator =(const block &) = delete;
block &operator =(block &&) = delete;
- std::size_t m_num_alloc;
- std::size_t m_free;
- std::size_t m_cur;
- char *m_data;
- char *m_data_allocated;
- mempool *m_mempool;
+ size_type m_num_alloc;
+ size_type m_free;
+ size_type m_cur;
+ size_type m_bytes_allocated;
+ std::uint8_t *m_data;
+ std::uint8_t *m_data_allocated;
+ mempool_arena &m_mempool;
};
struct info
{
- info(block *b, std::size_t p) : m_block(b), m_pos(p) { }
+ info(block *b, size_type p, size_type orig_size) : m_block(b), m_pos(p), m_orig_size(orig_size) { }
+ info(const info &) = default;
+ info &operator=(const info &) = default;
+ info(info &&) noexcept = default;
+ info &operator=(info &&) noexcept = default;
~info() = default;
- COPYASSIGNMOVE(info, default)
block * m_block;
- std::size_t m_pos;
+ size_type m_pos;
+ size_type m_orig_size;
};
-
- block * new_block(std::size_t min_bytes)
+ block * new_block(size_type min_bytes)
{
- auto *b = plib::pnew<block>(this, min_bytes);
+ auto *b = detail::alloc<block, 0>(base_arena(), *this, min_bytes);
m_blocks.push_back(b);
return b;
}
-
- static std::unordered_map<void *, info> &sinfo()
+ size_t m_min_alloc;
+ static BASEARENA &base_arena()
{
- static std::unordered_map<void *, info> spinfo;
- return spinfo;
+ static BASEARENA s_arena;
+ return s_arena;
}
- size_t m_min_alloc;
- size_t m_min_align;
+ using info_type = std::pair<void * const, info>;
- std::vector<block *> m_blocks;
+ using info_allocator_type = typename BASEARENA::template allocator_type<info_type>;
+ std::unordered_map<void *, info, std::hash<void *>, std::equal_to<>,
+ info_allocator_type> m_info;
- public:
- static constexpr const bool is_stateless = false;
- template <class T, std::size_t ALIGN = alignof(T)>
- using allocator_type = arena_allocator<mempool, T, ALIGN>;
+ plib::arena_vector<BASEARENA, block *> m_blocks;
- mempool(size_t min_alloc = (1<<21), size_t min_align = 16)
- : m_min_alloc(min_alloc), m_min_align(min_align)
- {
- }
+ };
- COPYASSIGNMOVE(mempool, delete)
+ template <typename BASEARENA, std::size_t MINALIGN>
+ mempool_arena<BASEARENA, MINALIGN>::mempool_arena(size_t min_alloc)
+ : m_min_alloc(min_alloc)
+ //, m_blocks(base_allocator_type<block *>(base_arena()))
+ , m_blocks(base_arena())
+ {
+ }
- ~mempool()
+ template <typename BASEARENA, std::size_t MINALIGN>
+ mempool_arena<BASEARENA, MINALIGN>::~mempool_arena()
+ {
+ for (auto & b : m_blocks)
{
-
- for (auto & b : m_blocks)
+ if (b->m_num_alloc != 0)
{
- if (b->m_num_alloc != 0)
- {
- plib::perrlogger("Found {} info blocks\n", sinfo().size());
- plib::perrlogger("Found block with {} dangling allocations\n", b->m_num_alloc);
- }
- plib::pdelete(b);
- //::operator delete(b->m_data);
+ plib::perrlogger("Found {} info blocks\n", m_info.size());
+ plib::perrlogger("Found block with {} dangling allocations\n", b->m_num_alloc);
}
+ detail::free<0>(base_arena(), b);
}
+ if (!m_info.empty())
+ plib::perrlogger("Still found {} info blocks after mempool deleted\n", m_info.size());
+ }
- void *allocate(size_t align, size_t size)
- {
- block *b = nullptr;
+ template <typename BASEARENA, std::size_t MINALIGN>
+ void * mempool_arena<BASEARENA, MINALIGN>::allocate(size_t align, size_t size)
+ {
+ block *b = nullptr;
- if (align < m_min_align)
- align = m_min_align;
+ if (align < MINALIGN)
+ align = MINALIGN;
- size_t rs = size + align;
- for (auto &bs : m_blocks)
- {
- if (bs->m_free > rs)
- {
- b = bs;
- break;
- }
- }
- if (b == nullptr)
- {
- b = new_block(rs);
- }
- b->m_free -= rs;
- b->m_num_alloc++;
- void *ret = reinterpret_cast<void *>(b->m_data + b->m_cur);
- auto capacity(rs);
- ret = std::align(align, size, ret, capacity);
- sinfo().insert({ ret, info(b, b->m_cur)});
- rs -= (capacity - size);
- b->m_cur += rs;
-
- return ret;
- }
+ size_t rs = size + align;
- static void deallocate(void *ptr)
+ for (auto &bs : m_blocks)
{
-
- auto it = sinfo().find(ptr);
- if (it == sinfo().end())
- plib::terminate("mempool::free - pointer not found\n");
- block *b = it->second.m_block;
- if (b->m_num_alloc == 0)
- plib::terminate("mempool::free - double free was called\n");
- else
+ if (bs->m_free > rs)
{
- b->m_num_alloc--;
- //printf("Freeing in block %p %lu\n", b, b->m_num_alloc);
- if (b->m_num_alloc == 0)
- {
- mempool *mp = b->m_mempool;
- auto itb = std::find(mp->m_blocks.begin(), mp->m_blocks.end(), b);
- if (itb == mp->m_blocks.end())
- plib::terminate("mempool::free - block not found\n");
-
- mp->m_blocks.erase(itb);
- plib::pdelete(b);
- }
- sinfo().erase(it);
+ b = bs;
+ break;
}
}
-
- template <typename T>
- using owned_pool_ptr = plib::owned_ptr<T, arena_deleter<mempool, T>>;
-
- template<typename T, typename... Args>
- owned_pool_ptr<T> make_poolptr(Args&&... args)
+ if (b == nullptr)
{
- auto *mem = this->allocate(alignof(T), sizeof(T));
- try
- {
- auto *mema = new (mem) T(std::forward<Args>(args)...);
- return owned_pool_ptr<T>(mema, true, arena_deleter<mempool, T>(this));
- }
- catch (...)
- {
- this->deallocate(mem);
- throw;
- }
+ b = new_block(rs);
}
+ b->m_free -= rs;
+ b->m_num_alloc++;
+ void *ret = reinterpret_cast<void *>(b->m_data + b->m_cur); // NOLINT(cppcoreguidelines-pro-type-reinterpret
+ auto capacity(rs);
+ ret = std::align(align, size, ret, capacity);
+ m_info.insert({ ret, info(b, b->m_cur, size)});
+ rs -= (capacity - size);
+ b->m_cur += rs;
+ this->inc_alloc_stat(size);
+
+ return ret;
+ }
+
+ template <typename BASEARENA, std::size_t MINALIGN>
+ void mempool_arena<BASEARENA, MINALIGN>::deallocate(void *ptr, [[maybe_unused]] std::size_t alignment, size_t size) noexcept
+ {
+ auto it = m_info.find(ptr);
+ if (it == m_info.end())
+ plib::terminate("mempool::free - pointer not found");
+ block *b = it->second.m_block;
+ if (b->m_num_alloc == 0)
+ plib::terminate("mempool::free - double free was called");
+ if (it->second.m_orig_size != size)
+ plib::perrlogger("Size mismatch original {} vs {}\n", it->second.m_orig_size, size);
+
+ mempool_arena &mp = b->m_mempool;
+ b->m_num_alloc--;
+ mp.dec_alloc_stat(size);
+ if (b->m_num_alloc == 0)
+ {
+ auto itb = std::find(mp.m_blocks.begin(), mp.m_blocks.end(), b);
+ if (itb == mp.m_blocks.end())
+ plib::terminate("mempool::free - block not found");
- };
+ mp.m_blocks.erase(itb);
+ detail::free<0>(mp.base_arena(), b);
+ }
+ m_info.erase(it);
+ }
+
+ template <typename BASEARENA, std::size_t MINALIGN>
+ mempool_arena<BASEARENA, MINALIGN>::block::block(mempool_arena &mp, size_type min_bytes)
+ : m_num_alloc(0)
+ , m_cur(0)
+ , m_data(nullptr)
+ , m_mempool(mp)
+ {
+ min_bytes = std::max(mp.m_min_alloc, min_bytes);
+ m_free = min_bytes;
+ m_bytes_allocated = (min_bytes + mempool_arena::block_align()); // - 1); // & ~(mp.m_min_align - 1);
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
+ //m_data_allocated = new std::uint8_t[alloc_bytes];
+ m_data_allocated = static_cast<std::uint8_t *>(mp.base_arena().allocate(mempool_arena::block_align(), m_bytes_allocated));
+ void *r = m_data_allocated;
+ std::align(mempool_arena::block_align(), min_bytes, r, m_bytes_allocated);
+ m_data = reinterpret_cast<std::uint8_t *>(r); // NOLINT(cppcoreguidelines-pro-type-reinterpret
+ }
} // namespace plib
-#endif /* PMEMPOOL_H_ */
+#endif // PMEMPOOL_H_
diff --git a/src/lib/netlist/plib/pmulti_threading.h b/src/lib/netlist/plib/pmulti_threading.h
new file mode 100644
index 00000000000..f7b89079bc0
--- /dev/null
+++ b/src/lib/netlist/plib/pmulti_threading.h
@@ -0,0 +1,89 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PMULTI_THREADING_H_
+#define PMULTI_THREADING_H_
+
+///
+/// \file pmulti_threading.h
+///
+
+#include "pconfig.h"
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <type_traits>
+#include <utility>
+
+namespace plib {
+
+ template<bool enabled_ = true>
+ struct pspin_mutex
+ {
+ public:
+ inline pspin_mutex() noexcept = default;
+ inline void lock() noexcept{ while (m_lock.test_and_set(std::memory_order_acquire)) { } }
+ inline void unlock() noexcept { m_lock.clear(std::memory_order_release); }
+ private:
+ PALIGNAS_CACHELINE()
+ std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
+ };
+
+ template<>
+ struct pspin_mutex<false>
+ {
+ public:
+ inline pspin_mutex() noexcept = default;
+ static inline void lock() noexcept { }
+ static inline void unlock() noexcept { }
+ };
+
+ class psemaphore
+ {
+ public:
+
+ psemaphore(long count = 0) noexcept: m_count(count) { }
+
+ psemaphore(const psemaphore& other) = delete;
+ psemaphore& operator=(const psemaphore& other) = delete;
+ psemaphore(psemaphore&& other) = delete;
+ psemaphore& operator=(psemaphore&& other) = delete;
+ ~psemaphore() = default;
+
+ void release(long update = 1)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_count += update;
+ m_cv.notify_one();
+ }
+
+ void acquire()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ while (m_count == 0)
+ m_cv.wait(lock);
+ --m_count;
+ }
+
+ bool try_acquire()
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (m_count)
+ {
+ --m_count;
+ return true;
+ }
+ return false;
+ }
+ private:
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ long m_count;
+ };
+
+
+} // namespace plib
+
+#endif // PMULTI_THREADING_H_
diff --git a/src/lib/netlist/plib/pomp.h b/src/lib/netlist/plib/pomp.h
index ecd9eacc41b..b77d3e5ee56 100644
--- a/src/lib/netlist/plib/pomp.h
+++ b/src/lib/netlist/plib/pomp.h
@@ -1,73 +1,76 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pomp.h
- *
- * Wrap all OPENMP stuff here in a hopefully c++ compliant way.
- */
#ifndef POMP_H_
#define POMP_H_
+///
+/// \file pomp.h
+///
+/// Wrap all OPENMP stuff here in a hopefully c++ compliant way.
+///
+
#include "pconfig.h"
#include "ptypes.h"
#include <cstdint>
-#if HAS_OPENMP
+#if PHAS_OPENMP
#include "omp.h"
#endif
-namespace plib {
-namespace omp {
+namespace plib::omp {
template <typename I, class T>
-void for_static(const I start, const I end, const T &what)
+void for_static(std::size_t numops, const I start, const I end, const T &what) noexcept(noexcept(what))
{
-#if HAS_OPENMP && USE_OPENMP
- #pragma omp parallel
-#endif
+ if (numops>1000)
{
-#if HAS_OPENMP && USE_OPENMP
- #pragma omp for //schedule(static)
-#endif
+ #if PHAS_OPENMP && PUSE_OPENMP
+ #pragma omp parallel for schedule(static)
+ #endif
for (I i = start; i < end; i++)
what(i);
}
+ else
+ for (I i = start; i < end; i++)
+ what(i);
}
template <typename I, class T>
-void for_static_np(const I start, const I end, const T &what)
+void for_static(const I start, const I end, const T &what) noexcept(noexcept(what))
+{
+#if PHAS_OPENMP && PUSE_OPENMP
+ #pragma omp parallel for schedule(static)
+#endif
+ for (I i = start; i < end; i++)
+ what(i);
+}
+
+template <typename I, class T>
+void for_static_np(const I start, const I end, const T &what) noexcept(noexcept(what))
{
for (I i = start; i < end; i++)
what(i);
}
-inline void set_num_threads(const std::size_t threads)
+inline void set_num_threads([[maybe_unused]] const std::size_t threads) noexcept
{
-#if HAS_OPENMP && USE_OPENMP
+#if PHAS_OPENMP && PUSE_OPENMP
omp_set_num_threads(threads);
-#else
- plib::unused_var(threads);
#endif
}
-inline std::size_t get_max_threads()
+inline std::size_t get_max_threads() noexcept
{
-#if HAS_OPENMP && USE_OPENMP
+#if PHAS_OPENMP && PUSE_OPENMP
return omp_get_max_threads();
#else
return 1;
#endif
}
+} // namespace plib::omp
-// ----------------------------------------------------------------------------------------
-// pdynlib: dynamic loading of libraries ...
-// ----------------------------------------------------------------------------------------
-
-} // namespace omp
-} // namespace plib
-
-#endif /* PSTRING_H_ */
+#endif // PSTRING_H_
diff --git a/src/lib/netlist/plib/poptions.cpp b/src/lib/netlist/plib/poptions.cpp
index fa9a343815e..1ddda613e85 100644
--- a/src/lib/netlist/plib/poptions.cpp
+++ b/src/lib/netlist/plib/poptions.cpp
@@ -1,18 +1,12 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * poptions.cpp
- *
- */
#include "poptions.h"
#include "pexception.h"
+#include "pstrutil.h"
#include "ptypes.h"
namespace plib {
-/***************************************************************************
- Options
-***************************************************************************/
option_base::option_base(options &parent, const pstring &help)
: m_help(help)
@@ -32,9 +26,8 @@ namespace plib {
return 0;
}
- int option_bool::parse(const pstring &argument)
+ int option_bool::parse([[maybe_unused]] const pstring &argument)
{
- unused_var(argument);
m_val = true;
return 0;
}
@@ -64,17 +57,17 @@ namespace plib {
void options::register_option(option_base *opt)
{
- m_opts.push_back(opt);
+ m_options.push_back(opt);
}
void options::check_consistency()
{
- for (auto &opt : m_opts)
+ for (auto &opt : m_options)
{
auto *o = dynamic_cast<option *>(opt);
if (o != nullptr)
{
- if (o->short_opt() == "" && o->long_opt() == "")
+ if (o->short_opt().empty() && o->long_opt().empty())
{
auto *ov = dynamic_cast<option_args *>(o);
if (ov != nullptr)
@@ -83,10 +76,8 @@ namespace plib {
{
throw pexception("other args can only be specified once!");
}
- else
- {
- m_other_args = ov;
- }
+
+ m_other_args = ov;
}
else
throw pexception("found option with neither short or long tag!" );
@@ -95,13 +86,13 @@ namespace plib {
}
}
- int options::parse(int argc, char **argv)
+ std::size_t options::parse(const std::vector<putf8string> &argv)
{
check_consistency();
- m_app = pstring(argv[0]);
+ m_app = argv[0];
bool seen_other_args = false;
- for (int i=1; i<argc; )
+ for (std::size_t i=1; i < argv.size(); )
{
pstring arg(argv[i]);
option *opt = nullptr;
@@ -110,8 +101,8 @@ namespace plib {
if (!seen_other_args && plib::startsWith(arg, "--"))
{
- auto v = psplit(arg.substr(2),"=");
- if (v.size() && v[0] != pstring(""))
+ auto v = psplit(arg.substr(2),'=');
+ if (!v.empty() && !v[0].empty())
{
opt = getopt_long(v[0]);
has_equal_arg = (v.size() > 1);
@@ -159,7 +150,7 @@ namespace plib {
else
{
i++;
- if (i >= argc)
+ if (i >= argv.size())
return i - 1;
if (opt->do_parse(pstring(argv[i])) != 0)
return i - 1;
@@ -173,28 +164,30 @@ namespace plib {
}
i++;
}
- return argc;
+ return argv.size();
}
pstring options::split_paragraphs(const pstring &text, unsigned width, unsigned indent,
- unsigned firstline_indent)
+ unsigned firstline_indent, const pstring &line_end)
{
- auto paragraphs = psplit(text,"\n");
+ auto paragraphs = psplit(text,'\n');
pstring ret("");
for (auto &p : paragraphs)
{
pstring line = plib::rpad(pstring(""), pstring(" "), firstline_indent);
- for (auto &s : psplit(p, " "))
+ for (auto &s : psplit(p, ' '))
{
if (line.length() + s.length() > width)
{
- ret += line + "\n";
+ ret += line + line_end;
line = plib::rpad(pstring(""), pstring(" "), indent);
}
line += s + " ";
}
- ret += line + "\n";
+ ret += line;
+ if (p != paragraphs.back())
+ ret += line_end;
}
return ret;
}
@@ -204,23 +197,23 @@ namespace plib {
{
pstring ret;
- ret = split_paragraphs(description, width, 0, 0) + "\n";
+ ret = split_paragraphs(description, width, 0, 0) + "\n\n";
ret += "Usage:\t" + usage + "\n\nOptions:\n\n";
- for (auto & optbase : m_opts )
+ for (const auto & optbase : m_options )
{
// Skip anonymous inputs which are collected in option_args
if (dynamic_cast<option_args *>(optbase) != nullptr)
continue;
- if (auto opt = dynamic_cast<option *>(optbase))
+ if (auto * const opt = dynamic_cast<option *>(optbase))
{
pstring line = "";
- if (opt->short_opt() != "")
+ if (!opt->short_opt().empty())
line += " -" + opt->short_opt();
- if (opt->long_opt() != "")
+ if (!opt->long_opt().empty())
{
- if (line != "")
+ if (!line.empty())
line += ", ";
else
line = " ";
@@ -229,9 +222,9 @@ namespace plib {
{
line += "=";
auto *ol = dynamic_cast<option_str_limit_base *>(opt);
- if (ol)
+ if (ol != nullptr)
{
- for (auto &v : ol->limit())
+ for (const auto &v : ol->limit())
{
line += v + "|";
}
@@ -246,28 +239,30 @@ namespace plib {
{
//ret += "TestGroup abc\n def gef\nxyz\n\n" ;
ret += line + "\n";
- ret += split_paragraphs(opt->help(), width, indent, indent);
+ ret += split_paragraphs(opt->help(), width, indent, indent) + "\n";
}
else
- ret += split_paragraphs(line + opt->help(), width, indent, 0);
+ ret += split_paragraphs(line + opt->help(), width, indent, 0) + "\n";
}
- else if (auto grp = dynamic_cast<option_group *>(optbase))
+ else if (auto *grp = dynamic_cast<option_group *>(optbase))
{
ret += "\n" + grp->group() + ":\n";
- if (grp->help() != "") ret += split_paragraphs(grp->help(), width, 4, 4) + "\n";
+ if (!grp->help().empty())
+ ret += split_paragraphs(grp->help(), width, 4, 4) + "\n\n";
}
}
// FIXME: other help ...
pstring ex("");
- for (auto & optbase : m_opts )
+ for (const auto & optbase : m_options )
{
- if (auto example = dynamic_cast<option_example *>(optbase))
+ if (auto *example = dynamic_cast<option_example *>(optbase))
{
- ex += "> " + example->example()+"\n\n";
- ex += split_paragraphs(example->help(), width, 4, 4) + "\n";
+ // help2man doesn't like \\ line continuation in output
+ ex += split_paragraphs(example->example(), width, 8, 0, "\n") + "\n\n";
+ ex += split_paragraphs(example->help(), width, 4, 4) + "\n\n";
}
}
- if (ex.length() > 0)
+ if (!ex.empty())
{
ret += "\n\nExamples:\n\n" + ex;
}
@@ -276,20 +271,20 @@ namespace plib {
option *options::getopt_short(const pstring &arg) const
{
- for (auto & optbase : m_opts)
+ for (const auto & optbase : m_options)
{
- auto opt = dynamic_cast<option *>(optbase);
- if (opt && arg != "" && opt->short_opt() == arg)
+ auto *opt = dynamic_cast<option *>(optbase);
+ if (opt != nullptr && !arg.empty() && opt->short_opt() == arg)
return opt;
}
return nullptr;
}
option *options::getopt_long(const pstring &arg) const
{
- for (auto & optbase : m_opts)
+ for (const auto & optbase : m_options)
{
- auto opt = dynamic_cast<option *>(optbase);
- if (opt && arg !="" && opt->long_opt() == arg)
+ auto *opt = dynamic_cast<option *>(optbase);
+ if (opt != nullptr && !arg.empty() && opt->long_opt() == arg)
return opt;
}
return nullptr;
diff --git a/src/lib/netlist/plib/poptions.h b/src/lib/netlist/plib/poptions.h
index aa2608c2c91..a758c446ff9 100644
--- a/src/lib/netlist/plib/poptions.h
+++ b/src/lib/netlist/plib/poptions.h
@@ -1,264 +1,274 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * poptions.h
- *
- */
-
-#pragma once
#ifndef POPTIONS_H_
#define POPTIONS_H_
-#include "plists.h"
+///
+/// \file poptions.h
+///
+
+#include "pfmtlog.h"
+#include "pstonum.h"
#include "pstring.h"
#include "putil.h"
namespace plib {
-/***************************************************************************
- Options
-***************************************************************************/
-class options;
+ /// \brief options base class
+ ///
+ class options;
-class option_base
-{
-public:
- option_base(options &parent, const pstring &help);
- virtual ~option_base() = default;
+ class option_base
+ {
+ public:
+ option_base(options &parent, const pstring &help);
+ virtual ~option_base() = default;
- COPYASSIGNMOVE(option_base, delete)
+ PCOPYASSIGNMOVE(option_base, delete)
- pstring help() const { return m_help; }
-private:
- pstring m_help;
-};
+ virtual pstring help() const { return m_help; }
+ private:
+ pstring m_help;
+ };
-class option_group : public option_base
-{
-public:
- option_group(options &parent, const pstring &group, const pstring &help)
- : option_base(parent, help), m_group(group) { }
+ class option_group : public option_base
+ {
+ public:
+ option_group(options &parent, const pstring &group, const pstring &help)
+ : option_base(parent, help), m_group(group) { }
- pstring group() const { return m_group; }
-private:
- pstring m_group;
-};
+ pstring group() const { return m_group; }
+ private:
+ pstring m_group;
+ };
-class option_example : public option_base
-{
-public:
- option_example(options &parent, const pstring &group, const pstring &help)
- : option_base(parent, help), m_example(group) { }
+ class option_example : public option_base
+ {
+ public:
+ option_example(options &parent, const pstring &group, const pstring &help)
+ : option_base(parent, help), m_example(group) { }
- pstring example() const { return m_example; }
-private:
- pstring m_example;
-};
+ pstring example() const { return m_example; }
+ private:
+ pstring m_example;
+ };
-class option : public option_base
-{
-public:
- option(options &parent, const pstring &ashort, const pstring &along, const pstring &help, bool has_argument);
+ class option : public option_base
+ {
+ public:
+ option(options &parent, const pstring &short_opt, const pstring &long_opt, const pstring &help, bool has_argument);
- /* no_argument options will be called with "" argument */
+ // no_argument options will be called with "" argument
- pstring short_opt() { return m_short; }
- pstring long_opt() { return m_long; }
- bool has_argument() { return m_has_argument ; }
- bool was_specified() { return m_specified; }
+ pstring short_opt() const { return m_short; }
+ pstring long_opt() const { return m_long; }
+ bool has_argument() const { return m_has_argument ; }
+ bool was_specified() const { return m_specified; }
- int do_parse(const pstring &argument)
- {
- m_specified = true;
- return parse(argument);
- }
+ int do_parse(const pstring &argument)
+ {
+ m_specified = true;
+ return parse(argument);
+ }
-protected:
- virtual int parse(const pstring &argument) = 0;
+ protected:
+ virtual int parse(const pstring &argument) = 0;
-private:
- pstring m_short;
- pstring m_long;
- bool m_has_argument;
- bool m_specified;
-};
+ private:
+ pstring m_short;
+ pstring m_long;
+ bool m_has_argument;
+ bool m_specified;
+ };
-class option_str : public option
-{
-public:
- option_str(options &parent, const pstring &ashort, const pstring &along, const pstring &defval, const pstring &help)
- : option(parent, ashort, along, help, true), m_val(defval)
- {}
+ class option_str : public option
+ {
+ public:
+ option_str(options &parent, const pstring &short_opt, const pstring &long_opt, const pstring &default_value, const pstring &help)
+ : option(parent, short_opt, long_opt, help, true), m_val(default_value)
+ {}
- pstring operator ()() const { return m_val; }
+ pstring operator ()() const { return m_val; }
-protected:
- int parse(const pstring &argument) override;
+ protected:
+ int parse(const pstring &argument) override;
-private:
- pstring m_val;
-};
+ private:
+ pstring m_val;
+ };
-class option_str_limit_base : public option
-{
-public:
- option_str_limit_base(options &parent, const pstring &ashort, const pstring &along, std::vector<pstring> &&limit, const pstring &help)
- : option(parent, ashort, along, help, true)
- , m_limit(limit)
+ class option_str_limit_base : public option
{
- }
- const std::vector<pstring> &limit() const { return m_limit; }
+ public:
+ option_str_limit_base(options &parent, const pstring &short_opt, const pstring &long_opt, std::vector<pstring> &&limit, const pstring &help)
+ : option(parent, short_opt, long_opt, help, true)
+ , m_limit(limit)
+ {
+ }
+ const std::vector<pstring> &limit() const { return m_limit; }
-protected:
+ protected:
-private:
- std::vector<pstring> m_limit;
-};
+ private:
+ std::vector<pstring> m_limit;
+ };
-template <typename T>
-class option_str_limit : public option_str_limit_base
-{
-public:
- option_str_limit(options &parent, const pstring &ashort, const pstring &along, const T &defval, std::vector<pstring> &&limit, const pstring &help)
- : option_str_limit_base(parent, ashort, along, std::move(limit), help), m_val(defval)
+ template <typename T>
+ class option_str_limit : public option_str_limit_base
{
- }
-
- T operator ()() const { return m_val; }
+ public:
+ option_str_limit(options &parent, const pstring &short_opt, const pstring &long_opt, const T &default_value, std::vector<pstring> &&limit, const pstring &help)
+ : option_str_limit_base(parent, short_opt, long_opt, std::move(limit), help), m_val(default_value)
+ {
+ }
- pstring as_string() const { return limit()[m_val]; }
+ T operator ()() const { return m_val; }
-protected:
- int parse(const pstring &argument) override
- {
- auto raw = plib::container::indexof(limit(), argument);
+ pstring as_string() const { return limit()[m_val]; }
- if (raw != plib::container::npos)
+ protected:
+ int parse(const pstring &argument) override
{
- m_val = static_cast<T>(raw);
- return 0;
- }
- else
+ auto raw = plib::container::index_of(limit(), argument);
+
+ if (raw != plib::container::npos)
+ {
+ m_val = narrow_cast<T>(raw);
+ return 0;
+ }
+
return 1;
- }
+ }
-private:
- T m_val;
-};
+ private:
+ T m_val;
+ };
-class option_bool : public option
-{
-public:
- option_bool(options &parent, const pstring &ashort, const pstring &along, const pstring &help)
- : option(parent, ashort, along, help, false), m_val(false)
- {}
+ class option_bool : public option
+ {
+ public:
+ option_bool(options &parent, const pstring &short_opt, const pstring &long_opt, const pstring &help)
+ : option(parent, short_opt, long_opt, help, false), m_val(false)
+ {}
- bool operator ()() const { return m_val; }
+ bool operator ()() const { return m_val; }
-protected:
- int parse(const pstring &argument) override;
+ protected:
+ int parse(const pstring &argument) override;
-private:
- bool m_val;
-};
+ private:
+ bool m_val;
+ };
-template <typename T>
-class option_num : public option
-{
-public:
- option_num(options &parent, const pstring &ashort, const pstring &along, T defval,
- const pstring &help,
- T minval = std::numeric_limits<T>::min(),
- T maxval = std::numeric_limits<T>::max() )
- : option(parent, ashort, along, help, true)
- , m_val(defval)
- , m_min(minval)
- , m_max(maxval)
- {}
-
- T operator ()() const { return m_val; }
-
-protected:
- int parse(const pstring &argument) override
+ template <typename T>
+ class option_num : public option
{
- bool err;
- m_val = pstonum_ne<T, true>(argument, err);
- return (err ? 1 : (m_val < m_min || m_val > m_max));
- }
+ public:
+ option_num(options &parent, const pstring &short_opt, const pstring &long_opt, T default_value,
+ const pstring &help,
+ T min_val = std::numeric_limits<T>::lowest(),
+ T max_val = std::numeric_limits<T>::max() )
+ : option(parent, short_opt, long_opt, help, true)
+ , m_val(default_value)
+ , m_min(min_val)
+ , m_max(max_val)
+ , m_def(default_value)
+ {}
+
+ T operator ()() const { return m_val; }
+
+ pstring help() const override
+ {
+ auto hs(option::help());
+ return plib::pfmt(hs)(m_def, m_min, m_max);
+ }
-private:
- T m_val;
- T m_min;
- T m_max;
-};
+ protected:
+ int parse(const pstring &argument) override
+ {
+ bool err(false);
+ m_val = pstonum_ne<T>(argument, err);
+ return (err ? 1 : (m_val < m_min || m_val > m_max));
+ }
-class option_vec : public option
-{
-public:
- option_vec(options &parent, const pstring &ashort, const pstring &along, const pstring &help)
- : option(parent, ashort, along, help, true)
- {}
+ private:
+ T m_val;
+ T m_min;
+ T m_max;
+ T m_def;
+ };
- const std::vector<pstring> &operator ()() const { return m_val; }
+ class option_vec : public option
+ {
+ public:
+ option_vec(options &parent, const pstring &short_opt, const pstring &long_opt, const pstring &help)
+ : option(parent, short_opt, long_opt, help, true)
+ {}
-protected:
- int parse(const pstring &argument) override;
+ const std::vector<pstring> &operator ()() const { return m_val; }
-private:
- std::vector<pstring> m_val;
-};
+ protected:
+ int parse(const pstring &argument) override;
-class option_args : public option_vec
-{
-public:
- option_args(options &parent, const pstring &help)
- : option_vec(parent, "", "", help)
- {}
-};
+ private:
+ std::vector<pstring> m_val;
+ };
-class options : public nocopyassignmove
-{
-public:
+ class option_args : public option_vec
+ {
+ public:
+ option_args(options &parent, const pstring &help)
+ : option_vec(parent, "", "", help)
+ {}
+ };
- options();
- explicit options(option **o);
+ class options
+ {
+ public:
- void register_option(option_base *opt);
- int parse(int argc, char **argv);
+ options();
+ explicit options(option **o);
- pstring help(const pstring &description, const pstring &usage,
- unsigned width = 72, unsigned indent = 20) const;
+ ~options() = default;
- pstring app() const { return m_app; }
+ PCOPYASSIGNMOVE(options, delete)
-private:
- static pstring split_paragraphs(const pstring &text, unsigned width, unsigned indent,
- unsigned firstline_indent);
+ void register_option(option_base *opt);
+ std::size_t parse(const std::vector<putf8string> &argv);
- void check_consistency();
+ pstring help(const pstring &description, const pstring &usage,
+ unsigned width = 72, unsigned indent = 20) const;
- template <typename T>
- T *getopt_type() const
- {
- for (auto & optbase : m_opts )
+ pstring app() const { return m_app; }
+
+ private:
+ static pstring split_paragraphs(const pstring &text, unsigned width, unsigned indent,
+ unsigned first_line_indent, const pstring &line_end = "\n");
+
+ void check_consistency() noexcept(false);
+
+ template <typename T>
+ T *getopt_type() const
{
- if (auto opt = dynamic_cast<T *>(optbase))
- return opt;
- }
+ for (const auto & base_class : m_options )
+ {
+ if (auto opt = dynamic_cast<T *>(base_class))
+ return opt;
+ }
return nullptr;
}
- option *getopt_short(const pstring &arg) const;
- option *getopt_long(const pstring &arg) const;
+ option *getopt_short(const pstring &arg) const;
+ option *getopt_long(const pstring &arg) const;
- std::vector<option_base *> m_opts;
- pstring m_app;
- option_args * m_other_args;
+ std::vector<option_base *> m_options;
+ pstring m_app;
+ option_args * m_other_args;
};
} // namespace plib
-#endif /* POPTIONS_H_ */
+#endif // POPTIONS_H_
diff --git a/src/lib/netlist/plib/pparser.cpp b/src/lib/netlist/plib/pparser.cpp
deleted file mode 100644
index 2b365f22811..00000000000
--- a/src/lib/netlist/plib/pparser.cpp
+++ /dev/null
@@ -1,531 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * pparser.c
- *
- */
-
-#include "pparser.h"
-#include "palloc.h"
-#include "putil.h"
-
-//#include <cstdarg>
-
-namespace plib {
-// ----------------------------------------------------------------------------------------
-// A simple tokenizer
-// ----------------------------------------------------------------------------------------
-
-pstring ptokenizer::currentline_str()
-{
- return m_cur_line;
-}
-
-
-void ptokenizer::skipeol()
-{
- pstring::value_type c = getc();
- while (c)
- {
- if (c == 10)
- {
- c = getc();
- if (c != 13)
- ungetc(c);
- return;
- }
- c = getc();
- }
-}
-
-
-pstring::value_type ptokenizer::getc()
-{
- if (m_unget != 0)
- {
- pstring::value_type c = m_unget;
- m_unget = 0;
- return c;
- }
- if (m_px == m_cur_line.end())
- {
- m_lineno++;
- if (m_strm.readline(m_cur_line))
- m_px = m_cur_line.begin();
- else
- return 0;
- return '\n';
- }
- pstring::value_type c = *(m_px++);
- return c;
-}
-
-void ptokenizer::ungetc(pstring::value_type c)
-{
- m_unget = c;
-}
-
-void ptokenizer::require_token(const token_id_t &token_num)
-{
- require_token(get_token(), token_num);
-}
-
-void ptokenizer::require_token(const token_t &tok, const token_id_t &token_num)
-{
- if (!tok.is(token_num))
- {
- pstring val("");
- for (auto &i : m_tokens)
- if (i.second.id() == token_num.id())
- val = i.first;
- error(pfmt("Expected token <{1}> got <{2}>")(val)(tok.str()) );
- }
-}
-
-pstring ptokenizer::get_string()
-{
- token_t tok = get_token();
- if (!tok.is_type(STRING))
- {
- error(pfmt("Expected a string, got <{1}>")(tok.str()) );
- }
- return tok.str();
-}
-
-pstring ptokenizer::get_identifier()
-{
- token_t tok = get_token();
- if (!tok.is_type(IDENTIFIER))
- {
- error(pfmt("Expected an identifier, got <{1}>")(tok.str()) );
- }
- return tok.str();
-}
-
-pstring ptokenizer::get_identifier_or_number()
-{
- token_t tok = get_token();
- if (!(tok.is_type(IDENTIFIER) || tok.is_type(NUMBER)))
- {
- error(pfmt("Expected an identifier or number, got <{1}>")(tok.str()) );
- }
- return tok.str();
-}
-
-// FIXME: combine into template
-double ptokenizer::get_number_double()
-{
- token_t tok = get_token();
- if (!tok.is_type(NUMBER))
- {
- error(pfmt("Expected a number, got <{1}>")(tok.str()) );
- }
- bool err;
- auto ret = plib::pstonum_ne<double, true>(tok.str(), err);
- if (err)
- error(pfmt("Expected a number, got <{1}>")(tok.str()) );
- return ret;
-}
-
-long ptokenizer::get_number_long()
-{
- token_t tok = get_token();
- if (!tok.is_type(NUMBER))
- {
- error(pfmt("Expected a long int, got <{1}>")(tok.str()) );
- }
- bool err;
- auto ret = plib::pstonum_ne<long, true>(tok.str(), err);
- if (err)
- error(pfmt("Expected a long int, got <{1}>")(tok.str()) );
- return ret;
-}
-
-ptokenizer::token_t ptokenizer::get_token()
-{
- while (true)
- {
- token_t ret = get_token_internal();
- if (ret.is_type(ENDOFFILE))
- return ret;
-
- if (ret.is(m_tok_comment_start))
- {
- do {
- ret = get_token_internal();
- } while (ret.is_not(m_tok_comment_end));
- }
- else if (ret.is(m_tok_line_comment))
- {
- skipeol();
- }
- else if (ret.str() == "#")
- {
- skipeol();
- }
- else
- {
- return ret;
- }
- }
-}
-
-ptokenizer::token_t ptokenizer::get_token_internal()
-{
- /* skip ws */
- pstring::value_type c = getc();
- while (m_whitespace.find(c) != pstring::npos)
- {
- c = getc();
- if (eof())
- {
- return token_t(ENDOFFILE);
- }
- }
- if (m_number_chars_start.find(c) != pstring::npos)
- {
- /* read number while we receive number or identifier chars
- * treat it as an identifier when there are identifier chars in it
- *
- */
- token_type ret = NUMBER;
- pstring tokstr = "";
- while (true) {
- if (m_identifier_chars.find(c) != pstring::npos && m_number_chars.find(c) == pstring::npos)
- ret = IDENTIFIER;
- else if (m_number_chars.find(c) == pstring::npos)
- break;
- tokstr += c;
- c = getc();
- }
- ungetc(c);
- return token_t(ret, tokstr);
- }
- else if (m_identifier_chars.find(c) != pstring::npos)
- {
- /* read identifier till non identifier char */
- pstring tokstr = "";
- while (m_identifier_chars.find(c) != pstring::npos)
- {
- tokstr += c;
- c = getc();
- }
- ungetc(c);
- auto id = m_tokens.find(tokstr);
- if (id != m_tokens.end())
- return token_t(id->second, tokstr);
- else
- return token_t(IDENTIFIER, tokstr);
- }
- else if (c == m_string)
- {
- pstring tokstr = "";
- c = getc();
- while (c != m_string)
- {
- tokstr += c;
- c = getc();
- }
- return token_t(STRING, tokstr);
- }
- else
- {
- /* read identifier till first identifier char or ws */
- pstring tokstr = "";
- while ((m_identifier_chars.find(c) == pstring::npos) && (m_whitespace.find(c) == pstring::npos))
- {
- tokstr += c;
- /* expensive, check for single char tokens */
- if (tokstr.length() == 1)
- {
- auto id = m_tokens.find(tokstr);
- if (id != m_tokens.end())
- return token_t(id->second, tokstr);
- }
- c = getc();
- }
- ungetc(c);
- auto id = m_tokens.find(tokstr);
- if (id != m_tokens.end())
- return token_t(id->second, tokstr);
- else
- return token_t(UNKNOWN, tokstr);
- }
-}
-
-void ptokenizer::error(const pstring &errs)
-{
- verror(errs, currentline_no(), currentline_str());
- //throw error;
-}
-
-// ----------------------------------------------------------------------------------------
-// A simple preprocessor
-// ----------------------------------------------------------------------------------------
-
-ppreprocessor::ppreprocessor(defines_map_type *defines)
-: std::istream(new st(this))
-, m_ifflag(0)
-, m_level(0)
-, m_lineno(0)
-, m_pos(0)
-, m_state(PROCESS)
-, m_comment(false)
-{
- m_expr_sep.emplace_back("!");
- m_expr_sep.emplace_back("(");
- m_expr_sep.emplace_back(")");
- m_expr_sep.emplace_back("+");
- m_expr_sep.emplace_back("-");
- m_expr_sep.emplace_back("*");
- m_expr_sep.emplace_back("/");
- m_expr_sep.emplace_back("&&");
- m_expr_sep.emplace_back("||");
- m_expr_sep.emplace_back("==");
- m_expr_sep.emplace_back(",");
- m_expr_sep.emplace_back(" ");
- m_expr_sep.emplace_back("\t");
-
- if (defines != nullptr)
- m_defines = *defines;
- m_defines.insert({"__PLIB_PREPROCESSOR__", define_t("__PLIB_PREPROCESSOR__", "1")});
-}
-
-void ppreprocessor::error(const pstring &err)
-{
- throw pexception("PREPRO ERROR: " + err);
-}
-
-#define CHECKTOK2(p_op, p_prio) \
- else if (tok == # p_op) \
- { \
- if (prio < (p_prio)) \
- return val; \
- start++; \
- const auto v2 = expr(sexpr, start, (p_prio)); \
- val = (val p_op v2); \
- } \
-
-// Operator precedence see https://en.cppreference.com/w/cpp/language/operator_precedence
-
-int ppreprocessor::expr(const std::vector<pstring> &sexpr, std::size_t &start, int prio)
-{
- int val = 0;
- pstring tok=sexpr[start];
- if (tok == "(")
- {
- start++;
- val = expr(sexpr, start, /*prio*/ 255);
- if (sexpr[start] != ")")
- error("parsing error!");
- start++;
- }
- while (start < sexpr.size())
- {
- tok=sexpr[start];
- if (tok == ")")
- {
- // FIXME: catch error
- return val;
- }
- else if (tok == "!")
- {
- if (prio < 3)
- return val;
- start++;
- val = !expr(sexpr, start, 3);
- }
- CHECKTOK2(*, 5)
- CHECKTOK2(/, 5)
- CHECKTOK2(+, 6)
- CHECKTOK2(-, 6)
- CHECKTOK2(==, 10)
- CHECKTOK2(&&, 14)
- CHECKTOK2(||, 15)
- else
- {
- // FIXME: error handling
- val = plib::pstonum<decltype(val)>(tok);
- start++;
- }
- }
- return val;
-}
-
-ppreprocessor::define_t *ppreprocessor::get_define(const pstring &name)
-{
- auto idx = m_defines.find(name);
- return (idx != m_defines.end()) ? &idx->second : nullptr;
-}
-
-pstring ppreprocessor::replace_macros(const pstring &line)
-{
- std::vector<pstring> elems(psplit(line, m_expr_sep));
- pstring ret("");
- for (auto & elem : elems)
- {
- define_t *def = get_define(elem);
- ret += (def != nullptr) ? def->m_replace : elem;
- }
- return ret;
-}
-
-static pstring catremainder(const std::vector<pstring> &elems, std::size_t start, const pstring &sep)
-{
- pstring ret("");
- for (std::size_t i = start; i < elems.size(); i++)
- {
- ret += elems[i];
- ret += sep;
- }
- return ret;
-}
-
-pstring ppreprocessor::process_comments(pstring line)
-{
- bool in_string = false;
-
- std::size_t e = line.size();
- pstring ret = "";
- for (std::size_t i=0; i < e; )
- {
- pstring c = plib::left(line, 1);
- line = line.substr(1);
- if (!m_comment)
- {
- if (c=="\"")
- {
- in_string = !in_string;
- ret += c;
- }
- else if (in_string && c=="\\")
- {
- i++;
- ret += (c + plib::left(line, 1));
- line = line.substr(1);
- }
- else if (!in_string && c=="/" && plib::left(line,1) == "*")
- m_comment = true;
- else if (!in_string && c=="/" && plib::left(line,1) == "/")
- break;
- else
- ret += c;
- }
- else
- if (c=="*" && plib::left(line,1) == "/")
- {
- i++;
- line = line.substr(1);
- m_comment = false;
- }
- i++;
- }
- return ret;
-}
-
-pstring ppreprocessor::process_line(pstring line)
-{
- bool line_cont = plib::right(line, 1) == "\\";
- if (line_cont)
- line = plib::left(line, line.size() - 1);
-
- if (m_state == LINE_CONTINUATION)
- m_line += line;
- else
- m_line = line;
-
- if (line_cont)
- {
- m_state = LINE_CONTINUATION;
- return "";
- }
- else
- m_state = PROCESS;
-
- line = process_comments(m_line);
-
- pstring lt = plib::trim(plib::replace_all(line, pstring("\t"), pstring(" ")));
- pstring ret;
- // FIXME ... revise and extend macro handling
- if (plib::startsWith(lt, "#"))
- {
- std::vector<pstring> lti(psplit(lt, " ", true));
- if (lti[0] == "#if")
- {
- m_level++;
- std::size_t start = 0;
- lt = replace_macros(lt);
- std::vector<pstring> t(psplit(replace_all(lt.substr(3), pstring(" "), pstring("")), m_expr_sep));
- auto val = static_cast<int>(expr(t, start, 255));
- if (val == 0)
- m_ifflag |= (1 << m_level);
- }
- else if (lti[0] == "#ifdef")
- {
- m_level++;
- if (get_define(lti[1]) == nullptr)
- m_ifflag |= (1 << m_level);
- }
- else if (lti[0] == "#ifndef")
- {
- m_level++;
- if (get_define(lti[1]) != nullptr)
- m_ifflag |= (1 << m_level);
- }
- else if (lti[0] == "#else")
- {
- m_ifflag ^= (1 << m_level);
- }
- else if (lti[0] == "#endif")
- {
- m_ifflag &= ~(1 << m_level);
- m_level--;
- }
- else if (lti[0] == "#include")
- {
- // ignore
- }
- else if (lti[0] == "#pragma")
- {
- if (m_ifflag == 0 && lti.size() > 3 && lti[1] == "NETLIST")
- {
- if (lti[2] == "warning")
- error("NETLIST: " + catremainder(lti, 3, " "));
- }
- }
- else if (lti[0] == "#define")
- {
- if (m_ifflag == 0)
- {
- if (lti.size() < 2)
- error("PREPRO: define needs at least one argument: " + line);
- else if (lti.size() == 2)
- m_defines.insert({lti[1], define_t(lti[1], "")});
- else
- {
- pstring arg("");
- for (std::size_t i=2; i<lti.size() - 1; i++)
- arg += lti[i] + " ";
- arg += lti[lti.size()-1];
- m_defines.insert({lti[1], define_t(lti[1], arg)});
- }
- }
- }
- else
- {
- if (m_ifflag == 0)
- error(pfmt("unknown directive on line {1}: {2}")(m_lineno)(replace_macros(line)));
- }
- }
- else
- {
- lt = replace_macros(lt);
- if (m_ifflag == 0)
- ret += lt;
- }
- return ret;
-}
-
-
-
-} // namespace plib
diff --git a/src/lib/netlist/plib/pparser.h b/src/lib/netlist/plib/pparser.h
deleted file mode 100644
index 123786602e0..00000000000
--- a/src/lib/netlist/plib/pparser.h
+++ /dev/null
@@ -1,279 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * pparser.h
- *
- */
-
-#ifndef PPARSER_H_
-#define PPARSER_H_
-
-#include "plists.h"
-#include "pstream.h"
-#include "pstring.h"
-
-//#include <cstdint>
-#include <unordered_map>
-
-
-namespace plib {
-class ptokenizer
-{
-public:
- template <typename T>
- ptokenizer(T &&strm) // NOLINT(misc-forwarding-reference-overload, bugprone-forwarding-reference-overload)
- : m_strm(std::forward<T>(strm))
- , m_lineno(0)
- , m_cur_line("")
- , m_px(m_cur_line.begin())
- , m_unget(0)
- , m_string('"')
- {
- }
-
- COPYASSIGNMOVE(ptokenizer, delete)
-
- virtual ~ptokenizer() = default;
-
- enum token_type
- {
- IDENTIFIER,
- NUMBER,
- TOKEN,
- STRING,
- COMMENT,
- UNKNOWN,
- ENDOFFILE
- };
-
- struct token_id_t
- {
- public:
-
- static constexpr std::size_t npos = static_cast<std::size_t>(-1);
-
- token_id_t() : m_id(npos) {}
- explicit token_id_t(const std::size_t id) : m_id(id) {}
- std::size_t id() const { return m_id; }
- private:
- std::size_t m_id;
- };
-
- struct token_t
- {
- explicit token_t(token_type type)
- : m_type(type), m_id(), m_token("")
- {
- }
- token_t(token_type type, const pstring &str)
- : m_type(type), m_id(), m_token(str)
- {
- }
- token_t(const token_id_t &id, const pstring &str)
- : m_type(TOKEN), m_id(id), m_token(str)
- {
- }
-
- bool is(const token_id_t &tok_id) const { return m_id.id() == tok_id.id(); }
- bool is_not(const token_id_t &tok_id) const { return !is(tok_id); }
-
- bool is_type(const token_type type) const { return m_type == type; }
-
- pstring str() const { return m_token; }
-
- private:
- token_type m_type;
- token_id_t m_id;
- pstring m_token;
- };
-
-
- int currentline_no() { return m_lineno; }
- pstring currentline_str();
-
- /* tokenizer stuff follows ... */
-
- token_t get_token();
- pstring get_string();
- pstring get_identifier();
- pstring get_identifier_or_number();
- double get_number_double();
- long get_number_long();
-
- void require_token(const token_id_t &token_num);
- void require_token(const token_t &tok, const token_id_t &token_num);
-
- token_id_t register_token(const pstring &token)
- {
- token_id_t ret(m_tokens.size());
- m_tokens.emplace(token, ret);
- return ret;
- }
-
- ptokenizer & identifier_chars(pstring s) { m_identifier_chars = std::move(s); return *this; }
- ptokenizer & number_chars(pstring st, pstring rem) { m_number_chars_start = std::move(st); m_number_chars = std::move(rem); return *this; }
- ptokenizer & string_char(pstring::value_type c) { m_string = c; return *this; }
- ptokenizer & whitespace(pstring s) { m_whitespace = std::move(s); return *this; }
- ptokenizer & comment(const pstring &start, const pstring &end, const pstring &line)
- {
- m_tok_comment_start = register_token(start);
- m_tok_comment_end = register_token(end);
- m_tok_line_comment = register_token(line);
- return *this;
- }
-
- token_t get_token_internal();
- void error(const pstring &errs);
-
- putf8_reader &stream() { return m_strm; }
-protected:
- virtual void verror(const pstring &msg, int line_num, const pstring &line) = 0;
-
-private:
- void skipeol();
-
- pstring::value_type getc();
- void ungetc(pstring::value_type c);
-
- bool eof() { return m_strm.eof(); }
-
- putf8_reader m_strm;
-
- int m_lineno;
- pstring m_cur_line;
- pstring::const_iterator m_px;
- pstring::value_type m_unget;
-
- /* tokenizer stuff follows ... */
-
- pstring m_identifier_chars;
- pstring m_number_chars;
- pstring m_number_chars_start;
- std::unordered_map<pstring, token_id_t> m_tokens;
- pstring m_whitespace;
- pstring::value_type m_string;
-
- token_id_t m_tok_comment_start;
- token_id_t m_tok_comment_end;
- token_id_t m_tok_line_comment;
-};
-
-
-class ppreprocessor : public std::istream
-{
-public:
-
- struct define_t
- {
- define_t(const pstring &name, const pstring &replace)
- : m_name(name), m_replace(replace)
- {}
- pstring m_name;
- pstring m_replace;
- };
-
- using defines_map_type = std::unordered_map<pstring, define_t>;
-
- explicit ppreprocessor(defines_map_type *defines = nullptr);
- ~ppreprocessor() override
- {
- delete rdbuf();
- }
-
- template <typename T>
- ppreprocessor & process(T &&istrm)
- {
- putf8_reader reader(std::forward<T>(istrm));
- pstring line;
- while (reader.readline(line))
- {
- m_lineno++;
- line = process_line(line);
- m_buf += decltype(m_buf)(line.c_str()) + static_cast<char>(10);
- }
- return *this;
- }
-
- COPYASSIGN(ppreprocessor, delete)
- ppreprocessor &operator=(ppreprocessor &&src) = delete;
-
-
- ppreprocessor(ppreprocessor &&s) noexcept
- : std::istream(new st(this))
- , m_defines(std::move(s.m_defines))
- , m_expr_sep(std::move(s.m_expr_sep))
- , m_ifflag(s.m_ifflag)
- , m_level(s.m_level)
- , m_lineno(s.m_lineno)
- , m_buf(std::move(s.m_buf))
- , m_pos(s.m_pos)
- , m_state(s.m_state)
- , m_comment(s.m_comment)
- {
- }
-
-protected:
-
- class st : public std::streambuf
- {
- public:
- st(ppreprocessor *strm) : m_strm(strm) { setg(nullptr, nullptr, nullptr); }
- st(st &&rhs) noexcept : m_strm(rhs.m_strm) {}
- int_type underflow() override
- {
- //printf("here\n");
- if (this->gptr() == this->egptr())
- {
- /* clang reports sign error - weird */
- std::size_t bytes = pstring_mem_t_size(m_strm->m_buf) - static_cast<std::size_t>(m_strm->m_pos);
-
- if (bytes > m_buf.size())
- bytes = m_buf.size();
- std::copy(m_strm->m_buf.c_str() + m_strm->m_pos, m_strm->m_buf.c_str() + m_strm->m_pos + bytes, m_buf.data());
- //printf("%ld\n", (long int)bytes);
- this->setg(m_buf.data(), m_buf.data(), m_buf.data() + bytes);
-
- m_strm->m_pos += static_cast</*pos_type*/long>(bytes);
- }
- return this->gptr() == this->egptr()
- ? std::char_traits<char>::eof()
- : std::char_traits<char>::to_int_type(*this->gptr());
- }
- private:
- ppreprocessor *m_strm;
- std::array<char_type, 1024> m_buf;
- };
- //friend class st;
-
- int expr(const std::vector<pstring> &sexpr, std::size_t &start, int prio);
- define_t *get_define(const pstring &name);
- pstring replace_macros(const pstring &line);
- virtual void error(const pstring &err);
-
-private:
-
- enum state_e
- {
- PROCESS,
- LINE_CONTINUATION
- };
-
- pstring process_line(pstring line);
- pstring process_comments(pstring line);
-
- defines_map_type m_defines;
- std::vector<pstring> m_expr_sep;
-
- std::uint_least64_t m_ifflag; // 31 if levels
- int m_level;
- int m_lineno;
- pstring_t<pu8_traits> m_buf;
- std::istream::pos_type m_pos;
- state_e m_state;
- pstring m_line;
- bool m_comment;
-};
-
-} // namespace plib
-
-#endif /* PPARSER_H_ */
diff --git a/src/lib/netlist/plib/ppmf.cpp b/src/lib/netlist/plib/ppmf.cpp
new file mode 100644
index 00000000000..85088b25638
--- /dev/null
+++ b/src/lib/netlist/plib/ppmf.cpp
@@ -0,0 +1,109 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#include "ppmf.h"
+
+#if PPMF_USE_MAME_DELEGATES
+#include "../../util/delegate.cpp"
+#else
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpmf-conversions"
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
+namespace plib {
+
+ void mfp_raw<ppmf_type::INTERNAL_ITANIUM>::convert_to_generic(generic_function &func, mfp_generic_class *&object) const
+ {
+ // apply the "this" delta to the object first
+ // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult,cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *o_p_delta = reinterpret_cast<mfp_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
+
+ // if the low bit of the vtable index is clear, then it is just a raw function pointer
+ if ((m_function & 1) == 0)
+ {
+ // NOLINTNEXTLINE(performance-no-int-to-ptr,cppcoreguidelines-pro-type-reinterpret-cast)
+ func = reinterpret_cast<generic_function>(m_function);
+ }
+ else
+ {
+ // otherwise, it is the byte index into the vtable where the actual function lives
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(o_p_delta);
+ if (compile_info::abi_vtable_function_descriptors::value)
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ func = reinterpret_cast<generic_function>(uintptr_t(vtable_base + m_function - 1));
+ else
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ func = *reinterpret_cast<generic_function *>(vtable_base + m_function - 1);
+ }
+ object = o_p_delta;
+ }
+
+ void mfp_raw<ppmf_type::INTERNAL_ARM>::convert_to_generic(generic_function &func, mfp_generic_class *&object) const
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ object = reinterpret_cast<mfp_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> 1));
+ if ((m_this_delta & 1) == 0)
+ {
+ // NOLINTNEXTLINE(performance-no-int-to-ptr,cppcoreguidelines-pro-type-reinterpret-cast)
+ func = reinterpret_cast<generic_function>(m_function);
+ }
+ else
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object) + m_function;
+ if (compile_info::abi_vtable_function_descriptors::value)
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ func = reinterpret_cast<generic_function>(uintptr_t(vtable_base));
+ else
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ func = *reinterpret_cast<generic_function *>(vtable_base);
+ }
+ }
+
+ struct unknown_base_equiv_novtdisp { mfp_raw<ppmf_type::INTERNAL_MSC>::generic_function fptr; int thisdisp, vptrdisp; };
+
+ void mfp_raw<ppmf_type::INTERNAL_MSC>::convert_to_generic(generic_function &func, mfp_generic_class *&object) const
+ {
+ //printf("%lx, %lx, %lx, %lx %lx\n", m_function, m_this_delta, m_vptr_offs, m_vt_index, m_size);
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *byteptr = reinterpret_cast<std::uint8_t *>(object);
+
+ // test for pointer to member function cast across virtual inheritance relationship
+ //if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
+ if ((sizeof(unknown_base_equiv) == m_size) || (sizeof(unknown_base_equiv_novtdisp) == m_size))
+ {
+ // add index from "this" pointer to location of vptr. This actually is an index,
+ // it needs to be multiplied by sizeof(void *)
+ byteptr += (m_vptr_index * static_cast<ptrdiff_t>(sizeof(void *)));
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr);
+ // and add offset to virtual base from vtable
+ if (sizeof(unknown_base_equiv) == m_size)
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
+ }
+
+ // add "this" pointer displacement if present in the pointer to member function
+ if (sizeof(single_base_equiv) < m_size)
+ byteptr += m_this_delta;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ object = reinterpret_cast<mfp_generic_class *>(byteptr);
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr)
+ auto const *funcx = reinterpret_cast<std::uint8_t const *>(m_function);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr)
+ func = reinterpret_cast<generic_function>(std::uintptr_t(funcx));
+ }
+
+} // namespace plib
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#endif
diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h
index 9ee759befa4..56f40bde922 100644
--- a/src/lib/netlist/plib/ppmf.h
+++ b/src/lib/netlist/plib/ppmf.h
@@ -1,280 +1,587 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * ppmf.h
- *
- */
#ifndef PPMF_H_
#define PPMF_H_
+///
+/// \file ppmf.h
+///
+///
+/// PMF_TYPE_PMF
+/// Use standard pointer to member function syntax C++11
+///
+/// PMF_TYPE_GNUC_PMF_CONV
+/// Use gnu extension and convert the pmf to a function pointer.
+/// This is not standard compliant and needs
+/// -Wno-pmf-conversions to compile.
+///
+/// PMF_TYPE_INTERNAL_*
+/// Use the same approach as MAME for deriving the function pointer.
+/// This is compiler-dependent as well
+///
+/// Benchmarks for `./nltool -c run -t 10 -n pong src/mame/machine/nl_pong.cpp`
+///
+/// PMF_TYPE_INTERNAL: 215% 215% 564% 580%
+/// PMF_TYPE_GNUC_PMF: 163% 196% 516% 490%
+/// PMF_TYPE_GNUC_PMF_CONV: 215% 215% 560% 575%
+///
+///
+
+/// \brief Enable experimental code on Visual Studio builds and VS clang llvm builds
+///
+/// This enables experimental code which uses optimized builds the
+/// ppmf_type::INTERNAL_MSC path also for complex (struct/union) return types.
+/// This currently depends on whether the code can adequately determine on
+/// x64 builds if the return type is returned through registers or passed as a
+/// second argument as a pointer to the member function.
+///
+/// The experimental code uses a temporary storage for the return value. This a
+/// copy overhead for causes for large sized return types a copy overhead.
+/// It would be easier if we would be able to obtain the RDX register on entry
+/// to the call. On MSVC this seems not to be possible since on x64 inline
+/// assembly is not supported. Even with clang-cl inline assembly this was not
+/// successful when optimized code was compiled. Therefore we have to live with
+/// the limitations.
+///
+/// This code path is disabled by default currently.
+///
+#if !defined(PPMF_EXPERIMENTAL)
+#define PPMF_EXPERIMENTAL 0
+#endif
+
+/// brief Enable using MAME delegates as a replacement for ppmf.
+///
+/// This define enables the use of MAME delegates (src/lib/util/delegate.h
+/// as a replacement to ppmf. Enable this setting if you want to use the nltool
+/// test suite (nltool -c tests) to produce comparisons to ppmf.
+///
+#if !defined(PPMF_USE_MAME_DELEGATES)
+#define PPMF_USE_MAME_DELEGATES 0
+#endif
+
+#if PPMF_USE_MAME_DELEGATES
+
+#include "../../util/delegate.h"
+
+namespace plib {
+ template<typename Signature>
+ class pmfp : public delegate<Signature>
+ {
+ public:
+ using basetype = delegate<Signature>;
+ using basetype::basetype;
+ using basetype::object;
+ explicit operator bool() const { return !this->isnull(); }
+ };
+}
+#else
+
#include "pconfig.h"
+#include "ptypes.h"
+#include <algorithm>
+#include <cstddef> // ptrdiff_t
#include <cstdint> // uintptr_t
+#include <type_traits>
#include <utility>
-/*
- *
- * NL_PMF_TYPE_GNUC_PMF
- * Use standard pointer to member function syntax C++11
- *
- * NL_PMF_TYPE_GNUC_PMF_CONV
- * Use gnu extension and convert the pmf to a function pointer.
- * This is not standard compliant and needs
- * -Wno-pmf-conversions to compile.
- *
- * NL_PMF_TYPE_INTERNAL
- * Use the same approach as MAME for deriving the function pointer.
- * This is compiler-dependent as well
- *
- * Benchmarks for ./nltool -c run -f src/mame/machine/nl_pong.cpp -t 10 -n pong_fast
- *
- * NL_PMF_TYPE_INTERNAL: 215% 215%
- * NL_PMF_TYPE_GNUC_PMF: 163% 196%
- * NL_PMF_TYPE_GNUC_PMF_CONV: 215% 215%
- * NL_PMF_TYPE_VIRTUAL: 213% 209%
- *
- * The whole exercise was done to avoid virtual calls. In prior versions of
- * netlist, the INTERNAL and GNUC_PMF_CONV approach provided significant improvement.
- * Since than, "hot" was removed from functions declared as virtual.
- * This may explain that the recent benchmarks show no difference at all.
- *
- */
-
-#if (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV)
-#pragma GCC diagnostic ignored "-Wpmf-conversions"
-#endif
+//============================================================
+// Macro magic
+//============================================================
-#if defined(__GNUC__) && (__GNUC__ > 6)
-#pragma GCC diagnostic ignored "-Wnoexcept-type"
+//#define PPMF_FORCE_TYPE 1
+
+#ifndef PPMF_FORCE_TYPE
+#define PPMF_FORCE_TYPE -1
#endif
namespace plib {
-/*
- * The following class was derived from the MAME delegate.h code.
- * It derives a pointer to a member function.
- */
-#if (PHAS_PMF_INTERNAL > 0)
- class mfp
+ enum class ppmf_type
+ {
+ PMF,
+ GNUC_PMF_CONV,
+ INTERNAL_ITANIUM,
+ INTERNAL_ARM,
+ INTERNAL_MSC
+ };
+
+
+ struct ppmf_internal_selector
+ {
+ using ci = compile_info;
+ constexpr static ppmf_type value =
+ (PPMF_FORCE_TYPE >= 0) ? static_cast<ppmf_type>(PPMF_FORCE_TYPE) :
+ (ci::type() == ci_compiler::CLANG && !ci::m64()
+ && ci::os() == ci_os::WINDOWS) ? ppmf_type::PMF :
+ (ci::mingw() && !ci::m64() && ci::version::full() >= typed_version<4,7>::full()) ? ppmf_type::PMF :
+ (ci::mingw() && !ci::m64()) ? ppmf_type::PMF : // Dropped support for mingw32 < 407 ppmf_type::INTERNAL_ITANIUM :
+ (ci::env() == ci_env::MSVC && ci::m64()) ? ppmf_type::INTERNAL_MSC :
+ ((ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC)
+ && (ci::arch() == ci_arch::MIPS
+ || ci::arch() == ci_arch::ARM
+ || ci::os() == ci_os::EMSCRIPTEN)) ? ppmf_type::INTERNAL_ARM :
+ (ci::type() == ci_compiler::CLANG || ci::type() == ci_compiler::GCC) ? ppmf_type::INTERNAL_ITANIUM :
+ ppmf_type::PMF
+ ;
+ };
+
+ static_assert(!(compile_info::type() == ci_compiler::CLANG && ppmf_internal_selector::value == (ppmf_type::GNUC_PMF_CONV)), "clang does not support ppmf_type::GNUC_PMF_CONV");
+ static_assert(!(compile_info::env() == ci_env::NVCC && ppmf_internal_selector::value == (ppmf_type::GNUC_PMF_CONV)), "nvcc does not support ppmf_type::GNUC_PMF_CONV");
+
+ template<typename R, typename... Targs>
+ struct mfp_traits
+ {
+ template<typename C> using specific_member_function = R (C::*)(Targs...);
+ template<typename C> using const_specific_member_function = R (C::*)(Targs...) const;
+ template<typename C> using member_static_ref = R (*)(C &, Targs...);
+ template<typename C> using member_static_ptr = R (*)(C *, Targs...);
+ };
+
+ class mfp_generic_class;
+
+
+ ///
+ /// \brief Used to derive a pointer to a member function.
+ ///
+ /// The following class was derived from the MAME delegate.h code.
+ ///
+ template <ppmf_type PMFINTERNAL>
+ class mfp_raw;
+
+ template <>
+ class mfp_raw<ppmf_type::INTERNAL_ITANIUM>
{
public:
// construct from any member function pointer
-#ifdef _MSC_VER
- class __single_inheritance si_generic_class;
- class generic_class { };
-#else
- class generic_class;
-#endif
using generic_function = void (*)();
template<typename MemberFunctionType>
- mfp(MemberFunctionType mftp) // NOLINT(cppcoreguidelines-pro-type-member-init)
+ mfp_raw(MemberFunctionType mftp);
- : m_function(0), m_this_delta(0), m_size(sizeof(mfp))
- {
- *reinterpret_cast<MemberFunctionType *>(this) = mftp;
- }
+ // extract the generic function and adjust the object pointer
+ void convert_to_generic(generic_function &func, mfp_generic_class *&object) const;
+
+ /// \brief Byte offset into the vtable
+ ///
+ /// On x86-64, the vtable contains pointers to code, and function pointers
+ /// are pointers to code. To obtain a function pointer for a virtual
+ /// member function, you fetch a pointer to code from the vtable.
+ ///
+ /// On traditional PPC64, the vtable contains pointers to function
+ /// descriptors, and function pointers are pointers to function descriptors.
+ /// To obtain a function pointer for a virtual member function, you
+ /// fetch a pointer to a function descriptor from the vtable.
+ ///
+ /// On IA64, the vtable contains function descriptors, and function
+ /// pointers are pointers to function descriptors. To obtain a
+ /// function pointer for a virtual member function, you calculate
+ /// the address of the function descriptor in the vtable.
+ ///
+ /// Simply adding the byte offset to the vtable pointer creates a
+ /// function pointer on IA64 because the vtable contains function
+ /// descriptors; on most other targets, the vtable contains function
+ /// pointers, so you need to fetch the function pointer after
+ /// calculating its address in the vtable.
+ ///
+ uintptr_t m_function; // first item can be one of two things:
+ // if even, it's a function pointer
+ // if odd, it's the byte offset into the vtable
+ ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer
+ };
- template<typename MemberFunctionType, typename FunctionType, typename ObjectType>
- static void get_mfp(MemberFunctionType mftp, FunctionType &func, ObjectType *&object)
- {
- mfp mfpo(mftp);
- //return mfpo.update_after_bind<FunctionType>(object);
- generic_function rfunc(nullptr);
- auto robject = reinterpret_cast<generic_class *>(object);
- mfpo.convert_to_generic(rfunc, robject);
- func = reinterpret_cast<FunctionType>(rfunc);
- object = reinterpret_cast<ObjectType *>(robject);
- }
+ template <>
+ class mfp_raw<ppmf_type::INTERNAL_ARM>
+ {
+ public:
+ // construct from any member function pointer
+ using generic_function = void (*)();
+
+ template<typename MemberFunctionType>
+ mfp_raw(MemberFunctionType mftp);
+
+ // extract the generic function and adjust the object pointer
+ void convert_to_generic(generic_function &func, mfp_generic_class *&object) const;
+
+ // actual state
+ uintptr_t m_function; // first item can be a function pointer or a byte offset into the vtable
+ ptrdiff_t m_this_delta; // delta to apply to the 'this' pointer after right shifting by one bit
+ // if even, m_function is a fuction pointer
+ // if odd, m_function is the byte offset into the vtable
+ };
+
+ template <>
+ class mfp_raw<ppmf_type::INTERNAL_MSC>
+ {
+ public:
+ // construct from any member function pointer
+ using generic_function = void (*)();
+ struct unknown_base_equiv { generic_function fptr; int thisdisp, vptrdisp, vtdisp; };
+ struct single_base_equiv { generic_function fptr; };
+
+ template<typename MemberFunctionType>
+ mfp_raw(MemberFunctionType mftp);
- private:
// extract the generic function and adjust the object pointer
- void convert_to_generic(generic_function &func, generic_class *&object) const
+ void convert_to_generic(generic_function &func, mfp_generic_class *&object) const;
+
+ // actual state
+ uintptr_t m_function; // pointer to the function
+ int m_this_delta; // delta to apply to the 'this' pointer
+
+ int m_vptr_index; // index into the vptr table.
+ int m_vt_index; // offset to be applied after vptr table lookup.
+ unsigned m_size;
+ };
+
+ template <typename R>
+ using pmf_is_register_return_type = std::integral_constant<bool,
+ std::is_void_v<R> ||
+ std::is_scalar_v<R> ||
+ std::is_reference_v<R> ||
+ std::is_same_v<std::remove_cv_t<R>, compile_info::int128_type> ||
+ std::is_same_v<std::remove_cv_t<R>, compile_info::uint128_type> >;
+
+ template<ppmf_type PMFINTERNAL, typename R, typename... Targs>
+ struct mfp_helper
+ {
+ protected:
+ static_assert(PMFINTERNAL >= ppmf_type::INTERNAL_ITANIUM && PMFINTERNAL <= ppmf_type::INTERNAL_MSC, "Invalid PMF type");
+
+ using traits = mfp_traits<R, Targs...>;
+ using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>;
+ using generic_member_abi_function = typename traits::template member_static_ptr<mfp_generic_class>;
+
+ using raw_type = mfp_raw<PMFINTERNAL>;
+ using generic_function_storage = typename raw_type::generic_function;
+
+ mfp_helper();
+
+ template<typename O, typename F>
+ void bind(O *object, F *mftp);
+
+ R call(Targs&&... args) const noexcept(true)
{
- if (PHAS_PMF_INTERNAL == 1)
+#if defined(_MSC_VER) && (PPMF_EXPERIMENTAL)
+ if constexpr (pmf_is_register_return_type<R>::value)
{
- // apply the "this" delta to the object first
- // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
- auto o_p_delta = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
-
- // if the low bit of the vtable index is clear, then it is just a raw function pointer
- if (!(m_function & 1))
- func = reinterpret_cast<generic_function>(m_function);
- else
- {
- // otherwise, it is the byte index into the vtable where the actual function lives
- std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(o_p_delta);
- func = *reinterpret_cast<generic_function *>(vtable_base + m_function - 1);
- }
- object = o_p_delta;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ const auto* func = reinterpret_cast<const generic_member_abi_function*>(&m_resolved);
+ return (*func)(m_obj, std::forward<Targs>(args)...);
}
- else if (PHAS_PMF_INTERNAL == 2)
+ else
{
- if ((m_this_delta & 1) == 0) {
- object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
- func = reinterpret_cast<generic_function>(m_function);
- }
- else
- {
- object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object));
-
- // otherwise, it is the byte index into the vtable where the actual function lives
- std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
- func = *reinterpret_cast<generic_function *>(vtable_base + m_function + m_this_delta - 1);
- }
+ using generic_member_abi_function_alt = void (*)(mfp_generic_class *,void *, Targs...);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ const auto* func = reinterpret_cast<const generic_member_abi_function_alt*>(&m_resolved);
+ std::uint8_t temp[sizeof(typename std::conditional<std::is_void_v<R>, void *, R>::type)];
+ (*func)(m_obj, &temp[0], std::forward<Targs>(args)...);
+ return *reinterpret_cast<R *>(&temp);
}
- else if (PHAS_PMF_INTERNAL == 3)
- {
- const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (generic_class::*)());
+#else
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ const auto* func = reinterpret_cast<const generic_member_abi_function*>(&m_resolved);
+ return (*func)(m_obj, std::forward<Targs>(args)...);
+#endif
+ }
- func = reinterpret_cast<generic_function>(m_function);
- if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int))
- object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
- }
+ generic_function_storage m_resolved;
+ mfp_generic_class *m_obj;
+ };
+
+ template<typename R, typename... Targs>
+ struct mfp_helper<ppmf_type::PMF, R, Targs...>
+ {
+ protected:
+ using traits = mfp_traits<R, Targs...>;
+ using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>;
+
+ template <class C>
+ using member_abi_function = typename traits::template specific_member_function<C>;
+ mfp_helper();
+
+ template<typename O, typename F>
+ void bind(O *object, F *mftp);
+
+ R call(Targs&&... args) const noexcept(true)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto* func = reinterpret_cast<const generic_member_function *>(&m_resolved);
+ return (*m_stub)(func, m_obj, std::forward<Targs>(args)...);
}
- // actual state
- uintptr_t m_function; // first item can be one of two things:
- // if even, it's a pointer to the function
- // if odd, it's the byte offset into the vtable
- int m_this_delta; // delta to apply to the 'this' pointer
-
- int m_dummy1; // only used for visual studio x64
- int m_dummy2;
- int m_size;
+ generic_member_function m_resolved;
+ mfp_generic_class *m_obj;
+ R (*m_stub)(const generic_member_function *funci, mfp_generic_class *obji, Targs&&... args);
+
+ private:
+ template<typename O>
+ static R stub(const generic_member_function* funci, mfp_generic_class* obji, Targs&&... args) noexcept(true);
};
-#endif
-#if (PPMF_TYPE == PPMF_TYPE_PMF)
template<typename R, typename... Targs>
- class pmfp_base
+ struct mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...>
{
+ protected:
+ using traits = mfp_traits<R, Targs...>;
+
+ template <class C>
+ using member_abi_function = typename traits::template member_static_ptr<C>;
+
+ mfp_helper();
+
+ template<typename O, typename F>
+ void bind(O *object, F *mftp);
+
+ R call(Targs&&... args) const noexcept(true)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto* func = reinterpret_cast<const member_abi_function<mfp_generic_class> *>(&m_resolved);
+ return (*func)(m_obj, std::forward<Targs>(args)...);
+ }
+
+ member_abi_function<mfp_generic_class> m_resolved;
+ mfp_generic_class *m_obj;
+ };
+
+ template <ppmf_type PMFINTERNAL, typename R, typename... Targs>
+ using pmfp_helper_select = std::conditional<
+ pmf_is_register_return_type<R>::value
+ || PMFINTERNAL != ppmf_type::INTERNAL_MSC || (PPMF_EXPERIMENTAL),
+ mfp_helper<PMFINTERNAL, R, Targs...>, mfp_helper<ppmf_type::PMF, R, Targs...>>;
+
+ template<ppmf_type PMFINTERNAL, typename SIGNATURE> class pmfp_base;
+
+ template<ppmf_type PMFINTERNAL, typename R, typename... Targs>
+ class pmfp_base<PMFINTERNAL, R (Targs...)> : public pmfp_helper_select<PMFINTERNAL, R, Targs...>::type
+ {
+ static_assert((compile_info::env::value != ci_env::NVCC) || (PMFINTERNAL != ppmf_type::GNUC_PMF_CONV), "GNUC_PMF_CONV not supported by nvcc");
public:
- class generic_class;
-#if defined (__INTEL_COMPILER) && defined (_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least
- using generic_function = int [((sizeof(void *) + 4 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
-#elif defined(_MSC_VER) // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
- using generic_function = int[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
-#else
- using generic_function = R (generic_class::*)(Targs...);
-#endif
+ using helper = typename pmfp_helper_select<PMFINTERNAL, R, Targs...>::type;
+
+ using traits = mfp_traits<R, Targs...>;
+
pmfp_base()
+ : helper()
{
- int *p = reinterpret_cast<int *>(&m_func);
- int *e = p + sizeof(generic_function) / sizeof(int);
- for (; p < e; p++)
- *p = 0;
}
- template<typename MemberFunctionType, typename O>
- void set_base(MemberFunctionType mftp, O *object)
+ template<typename O, typename P>
+ pmfp_base(typename traits::template specific_member_function<O> mftp, P *object)
+ : helper()
{
- using function_ptr = R (O::*)(Targs...);
- function_ptr t = mftp;
- *reinterpret_cast<function_ptr *>(&m_func) = t;
+ this->bind(static_cast<O*>(object), &mftp);
}
+
+ template<typename O, typename P>
+ pmfp_base(typename traits::template const_specific_member_function<O> mftp, P *object)
+ : helper()
+ {
+ this->bind(static_cast<O*>(object), &mftp);
+ }
+
+ mfp_generic_class *object() const noexcept { return this->m_obj; }
+
template<typename O>
- inline R call(O *obj, Targs... args)
+ void set(typename traits::template specific_member_function<O> mftp, O *object)
{
- using function_ptr = R (O::*)(Targs...);
- function_ptr t = *reinterpret_cast<function_ptr *>(&m_func);
- return (obj->*t)(std::forward<Targs>(args)...);
+ this->bind(object, &mftp);
}
- bool is_set() {
-#if defined(_MSC_VER) || (defined (__INTEL_COMPILER) && defined (_M_X64))
- int *p = reinterpret_cast<int *>(&m_func);
- int *e = p + sizeof(generic_function) / sizeof(int);
- for (; p < e; p++)
- if (*p != 0)
- return true;
-
- return false;
-#else
- return m_func != nullptr;
-#endif
+
+ R operator()(Targs... args) const noexcept(true)
+ {
+ return this->call(std::forward<Targs>(args)...);
}
+
+ bool isnull() const noexcept { return this->m_resolved == nullptr; }
+ explicit operator bool() const noexcept { return !isnull(); }
+ bool has_object() const noexcept { return this->m_obj != nullptr; }
+ bool operator==(const pmfp_base &rhs) const { return this->m_resolved == rhs.m_resolved; }
+ bool operator!=(const pmfp_base &rhs) const { return !(*this == rhs); }
+
private:
- generic_function m_func;
-#if 0 && defined(_MSC_VER)
- int dummy[4];
-#endif
};
-#elif ((PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV) || (PPMF_TYPE == PPMF_TYPE_INTERNAL))
- template<typename R, typename... Targs>
- class pmfp_base
+ template<typename Signature>
+ using pmfp = pmfp_base<ppmf_internal_selector::value, Signature>;
+
+ ///
+ /// \brief Class to support delegate late binding
+ ///
+ /// When constructing delegates in constructors AND the referenced function
+ /// is virtual, the vtable may not yet be fully constructed. In these cases
+ /// the following class allows to construct the delegate later.
+ ///
+ /// ```
+ /// plib::late_pmfp<plib::pmfp<void, pstring>> a(&nld_7493::printer);
+ /// // Store the a object somewhere
+ ///
+ /// // After full construction ...
+ ///
+ /// auto delegate_obj = a(this);
+ /// delegate_obj(pstring("Hello World!"));
+ /// ```
+ template<typename T>
+ class late_pmfp
{
public:
- using generic_function = void (*)();
- pmfp_base() : m_func(nullptr) {}
+ using return_type = T;
+ using traits = typename return_type::traits;
+ using generic_member_function = typename traits::template specific_member_function<mfp_generic_class>;
+ using static_creator = return_type (*)(const generic_member_function *, mfp_generic_class *);
- template<typename MemberFunctionType, typename O>
- void set_base(MemberFunctionType mftp, O *object)
- {
- #if (PPMF_TYPE == PPMF_TYPE_INTERNAL)
- using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
- function_ptr func(nullptr);
- plib::mfp::get_mfp(mftp, func, object);
- m_func = reinterpret_cast<generic_function>(func);
- #elif (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV)
- R (O::* pFunc)(Targs...) = mftp;
- m_func = reinterpret_cast<generic_function>((object->*pFunc));
- #endif
- }
template<typename O>
- R call(O *obj, Targs... args) const
+ late_pmfp(typename traits::template specific_member_function<O> mftp);
+
+ template<typename O>
+ return_type operator()(O *object) const
{
- using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
- return (reinterpret_cast<function_ptr>(m_func))(obj, std::forward<Targs>(args)...);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return m_creator(&m_raw, reinterpret_cast<mfp_generic_class *>(object));
}
- bool is_set() noexcept { return m_func != nullptr; }
- generic_function get_function() const noexcept { return m_func; }
+
private:
- generic_function m_func;
+
+ template <typename O>
+ static return_type creator(const generic_member_function *raw, mfp_generic_class *obj);
+
+ generic_member_function m_raw;
+ static_creator m_creator;
};
-#endif
- template<typename R, typename... Targs>
- class pmfp : public pmfp_base<R, Targs...>
+ template<typename MemberFunctionType>
+ mfp_raw<ppmf_type::INTERNAL_ITANIUM>::mfp_raw(MemberFunctionType mftp)
+ : m_function(0), m_this_delta(0)
{
- public:
- class generic_class;
+ static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch");
+ *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT
+ // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
+ }
+
+ template<typename MemberFunctionType>
+ mfp_raw<ppmf_type::INTERNAL_ARM>::mfp_raw(MemberFunctionType mftp)
+ : m_function(0), m_this_delta(0)
+ {
+ static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch");
+ *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT
+ }
- template <class C>
- using MemberFunctionType = R (C::*)(Targs...);
+ template<typename MemberFunctionType>
+ mfp_raw<ppmf_type::INTERNAL_MSC>::mfp_raw(MemberFunctionType mftp)
+ : m_function(0), m_this_delta(0), m_vptr_index(0), m_vt_index(0), m_size(0)
+ {
+ static_assert(sizeof(*this) >= sizeof(MemberFunctionType), "size mismatch");
+ *reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT
+ m_size = sizeof(mftp); //NOLINT
+ }
+
+ template<ppmf_type PMFINTERNAL, typename R, typename... Targs>
+ mfp_helper<PMFINTERNAL, R, Targs...>::mfp_helper()
+ : m_obj(nullptr)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
+ std::fill(s, s + sizeof(m_resolved), 0);
+ }
+
+ template<ppmf_type PMFINTERNAL, typename R, typename... Targs>
+ template<typename O, typename F>
+ void mfp_helper<PMFINTERNAL, R, Targs...>::bind(O *object, F *mftp)
+ {
+ typename traits::template specific_member_function<O> pFunc;
+ static_assert(sizeof(pFunc) >= sizeof(F), "size error");
+ //# NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ //# *reinterpret_cast<F *>(&pFunc) = *mftp;
+ reinterpret_copy(*mftp, pFunc);
+ raw_type mfpo(pFunc);
+ generic_function_storage rfunc(nullptr);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *robject = reinterpret_cast<mfp_generic_class *>(object);
+ mfpo.convert_to_generic(rfunc, robject);
+ reinterpret_copy(rfunc, this->m_resolved);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ m_obj = reinterpret_cast<mfp_generic_class *>(robject);
+ }
- pmfp() : pmfp_base<R, Targs...>(), m_obj(nullptr) {}
+ template<typename R, typename... Targs>
+ mfp_helper<ppmf_type::PMF, R, Targs...>::mfp_helper()
+ : m_obj(nullptr)
+ , m_stub(nullptr)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
+ std::fill(s, s + sizeof(m_resolved), 0);
+ }
- template<typename O>
- pmfp(MemberFunctionType<O> mftp, O *object)
- : pmfp_base<R, Targs...>()
- {
- this->set_base(mftp, object);
- m_obj = reinterpret_cast<generic_class *>(object);
- }
+ template<typename R, typename... Targs>
+ template<typename O, typename F>
+ void mfp_helper<ppmf_type::PMF, R, Targs...>::bind(O *object, F *mftp)
+ {
+ reinterpret_copy(*mftp, this->m_resolved);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ m_obj = reinterpret_cast<mfp_generic_class *>(object);
+ m_stub = &stub<O>;
+ }
- template<typename O>
- void set(MemberFunctionType<O> mftp, O *object)
- {
- this->set_base(mftp, object);
- m_obj = reinterpret_cast<generic_class *>(object);
- }
+ template<typename R, typename... Targs>
+ template<typename O>
+ R mfp_helper<ppmf_type::PMF, R, Targs...>::stub(const generic_member_function* funci, mfp_generic_class* obji, Targs&&... args) noexcept(true)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto* obj = reinterpret_cast<O*>(obji);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto* func = reinterpret_cast<const member_abi_function<O> *>(funci);
+ return (obj->*(*func))(std::forward<Targs>(args)...);
+ }
- inline R operator()(Targs ... args)
- {
- return this->call(m_obj, std::forward<Targs>(args)...);
- }
+ template<typename R, typename... Targs>
+ mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...>::mfp_helper()
+ : m_obj(nullptr)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *s = reinterpret_cast<std::uint8_t *>(&m_resolved);
+ std::fill(s, s + sizeof(m_resolved), 0);
+ }
- generic_class *object() const noexcept { return m_obj; }
- bool has_object() const noexcept { return m_obj != nullptr; }
- private:
- generic_class *m_obj;
- };
+ template<typename R, typename... Targs>
+ template<typename O, typename F>
+ void mfp_helper<ppmf_type::GNUC_PMF_CONV, R, Targs...>::bind(O *object, F *mftp)
+ {
+ // nvcc still tries to compile the code below - even when shielded with a `if constexpr`
+#if !defined(__NVCC__)
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ member_abi_function<O> t = reinterpret_cast<member_abi_function<O>>(object->*(*mftp));
+ reinterpret_copy(t, this->m_resolved);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ m_obj = reinterpret_cast<mfp_generic_class *>(object);
+#endif
+ }
+ template<typename T>
+ template<typename O>
+ late_pmfp<T>::late_pmfp(typename traits::template specific_member_function<O> mftp)
+ : m_creator(creator<O>)
+ {
+ static_assert(sizeof(m_raw) >= sizeof(typename traits::template specific_member_function<O>), "size issue");
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ *reinterpret_cast<typename traits::template specific_member_function<O> *>(&m_raw) = mftp;
+ }
+
+ template<typename T>
+ template<typename O>
+ typename late_pmfp<T>::return_type late_pmfp<T>::creator(const typename late_pmfp<T>::generic_member_function *raw, mfp_generic_class *obj)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto p = reinterpret_cast<const typename late_pmfp<T>::traits::template specific_member_function<O> *>(raw);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *o = reinterpret_cast<O *>(obj);
+ return return_type(*p, o);
+ }
} // namespace plib
-#endif /* PPMF_H_ */
+#endif
+#endif // PPMF_H_
diff --git a/src/lib/netlist/plib/ppreprocessor.cpp b/src/lib/netlist/plib/ppreprocessor.cpp
new file mode 100644
index 00000000000..0c3858862f6
--- /dev/null
+++ b/src/lib/netlist/plib/ppreprocessor.cpp
@@ -0,0 +1,766 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#include "ppreprocessor.h"
+#include "palloc.h"
+#include "pstonum.h"
+#include "pstrutil.h"
+#include "putil.h"
+
+namespace plib {
+
+ // ----------------------------------------------------------------------------------------
+ // A simple preprocessor
+ // ----------------------------------------------------------------------------------------
+
+ ppreprocessor::ppreprocessor(psource_collection_t &sources, defines_map_type *defines)
+ : m_sources(sources)
+ , m_if_flag(0)
+ , m_if_seen(0)
+ , m_elif(0)
+ , m_if_level(0)
+ , m_state(PROCESS)
+ , m_comment(false)
+ {
+ m_expr_sep.emplace_back("!");
+ m_expr_sep.emplace_back("(");
+ m_expr_sep.emplace_back(")");
+ m_expr_sep.emplace_back("+");
+ m_expr_sep.emplace_back("-");
+ m_expr_sep.emplace_back("*");
+ m_expr_sep.emplace_back("/");
+ m_expr_sep.emplace_back("&&");
+ m_expr_sep.emplace_back("||");
+ m_expr_sep.emplace_back("==");
+ m_expr_sep.emplace_back(",");
+ m_expr_sep.emplace_back(";");
+ m_expr_sep.emplace_back(".");
+ m_expr_sep.emplace_back("##");
+ m_expr_sep.emplace_back("#");
+ m_expr_sep.emplace_back(" ");
+ m_expr_sep.emplace_back("\t");
+ m_expr_sep.emplace_back("\"");
+
+ if (defines != nullptr)
+ m_defines = *defines;
+ m_defines.insert({"__PLIB_PREPROCESSOR__", define_t("__PLIB_PREPROCESSOR__", "1")});
+ auto idx = m_defines.find("__PREPROCESSOR_DEBUG__");
+ m_debug_out = idx != m_defines.end();
+
+ }
+
+ void ppreprocessor::error(const pstring &err)
+ {
+ pstring s("");
+ pstring trail (" from ");
+ pstring trail_first("In file included from ");
+ pstring e = plib::pfmt("{1}:{2}:0: error: {3}\n")
+ (m_stack.back().m_name, m_stack.back().m_lineno, err);
+ m_stack.pop_back();
+ while (!m_stack.empty())
+ {
+ if (m_stack.size() == 1)
+ trail = trail_first;
+ s = plib::pfmt("{1}{2}:{3}:0\n{4}")(trail, m_stack.back().m_name, m_stack.back().m_lineno, s);
+ m_stack.pop_back();
+ }
+ throw pexception("\n" + s + e + " " + m_line + "\n");
+ }
+
+ template <typename PP, typename L = ppreprocessor::string_list>
+ struct simple_iter
+ {
+ simple_iter(PP *parent, const L &tokens)
+ : m_tokens(tokens), m_parent(parent), m_pos(0)
+ {}
+
+ /// \brief skip white space in token list
+ ///
+ void skip_ws()
+ {
+ while (m_pos < m_tokens.size() && (m_tokens[m_pos] == " " || m_tokens[m_pos] == "\t"))
+ m_pos++;
+ }
+
+ /// \brief return next token skipping white space
+ ///
+ /// \return next token
+ ///
+ const pstring &next()
+ {
+ skip_ws();
+ if (m_pos >= m_tokens.size())
+ error("unexpected end of line");
+ return m_tokens[m_pos++];
+ }
+
+ /// \brief return next token including white space
+ ///
+ /// \return next token
+ ///
+ const pstring &next_ws()
+ {
+ if (m_pos >= m_tokens.size())
+ error("unexpected end of line");
+ return m_tokens[m_pos++];
+ }
+
+ const pstring &peek_ws()
+ {
+ if (m_pos >= m_tokens.size())
+ error("unexpected end of line");
+ return m_tokens[m_pos];
+ }
+
+ const pstring &last()
+ {
+ if (m_pos == 0)
+ error("no last token at beginning of line");
+ if (m_pos > m_tokens.size())
+ error("unexpected end of line");
+ return m_tokens[m_pos-1];
+ }
+
+ bool eod()
+ {
+ return (m_pos >= m_tokens.size());
+ }
+
+ void error(const pstring &err)
+ {
+ m_parent->error(err);
+ }
+ private:
+ const L m_tokens;
+ PP *m_parent;
+ std::size_t m_pos;
+ };
+
+ #define CHECKTOK2(p_op, p_prio) \
+ else if (tok == # p_op) \
+ { \
+ if (!has_val) \
+ { sexpr.error("parsing error!"); return 1;} \
+ if (prio < (p_prio)) \
+ return val; \
+ sexpr.next(); \
+ const auto v2 = prepro_expr(sexpr, (p_prio)); \
+ val = (val p_op v2); \
+ }
+
+ // Operator precedence see https://en.cppreference.com/w/cpp/language/operator_precedence
+
+ template <typename PP>
+ static int prepro_expr(simple_iter<PP> &sexpr, int prio)
+ {
+ int val(0);
+ bool has_val(false);
+
+ pstring tok=sexpr.peek_ws();
+ if (tok == "(")
+ {
+ sexpr.next();
+ val = prepro_expr(sexpr, 255);
+ if (sexpr.next() != ")")
+ sexpr.error("expected ')'");
+ has_val = true;
+ }
+ while (!sexpr.eod())
+ {
+ tok = sexpr.peek_ws();
+ if (tok == ")")
+ {
+ if (!has_val)
+ sexpr.error("Found ')' but have no value computed");
+ else
+ return val;
+ }
+ else if (tok == "!")
+ {
+ if (prio < 3)
+ {
+ if (!has_val)
+ sexpr.error("parsing error!");
+ else
+ return val;
+ }
+ sexpr.next();
+ val = !prepro_expr(sexpr, 3);
+ has_val = true;
+ }
+ CHECKTOK2(*, 5)
+ CHECKTOK2(/, 5) // NOLINT(clang-analyzer-core.DivideZero)
+ CHECKTOK2(+, 6)
+ CHECKTOK2(-, 6)
+ CHECKTOK2(==, 10)
+ CHECKTOK2(&&, 14)
+ CHECKTOK2(||, 15)
+ else
+ {
+ try
+ {
+ val = plib::pstonum<decltype(val)>(tok);
+ }
+ catch (pexception &e)
+ {
+ sexpr.error(e.text());
+ }
+ has_val = true;
+ sexpr.next();
+ }
+ }
+ if (!has_val)
+ sexpr.error("No value computed. Empty expression ?");
+ return val;
+ }
+
+ ppreprocessor::define_t *ppreprocessor::get_define(const pstring &name)
+ {
+ auto idx = m_defines.find(name);
+ return (idx != m_defines.end()) ? &idx->second : nullptr;
+ }
+
+ ppreprocessor::string_list ppreprocessor::tokenize(const pstring &str,
+ const string_list &sep, bool remove_ws, bool concat)
+ {
+ const pstring STR = "\"";
+ string_list tmp_ret;
+ string_list tmp(psplit(str, sep));
+ std::size_t pi(0);
+
+ while (pi < tmp.size())
+ {
+ if (tmp[pi] == STR)
+ {
+ pstring s(STR);
+ pi++;
+ while (pi < tmp.size() && tmp[pi] != STR)
+ {
+ s += tmp[pi];
+ pi++;
+ }
+ s += STR;
+ tmp_ret.push_back(s);
+ }
+ else
+ {
+ pstring tok=tmp[pi];
+ if (tok.length() >= 2 && pi < tmp.size() - 2 )
+ {
+ auto sc=tok.substr(0,1);
+ auto ec=tok.substr(tok.length()-1, 1);
+ if ((sc == "." || (sc>="0" && sc<="9")) && (ec=="e" || ec=="E"))
+ {
+ // looks like an incomplete float due splitting by - or +
+ tok = tok + tmp[pi+1] + tmp[pi+2];
+ pi += 2;
+ }
+ }
+ if (!remove_ws || (tok != " " && tok != "\t"))
+ tmp_ret.push_back(tok);
+ }
+ pi++;
+ }
+
+ if (!concat)
+ return tmp_ret;
+
+ // FIXME: error if concat at beginning or end
+ string_list ret;
+ pi = 0;
+ while (pi<tmp_ret.size())
+ {
+ if (tmp_ret[pi] == "##")
+ {
+ while (ret.back() == " " || ret.back() == "\t")
+ ret.pop_back();
+ pstring cc = ret.back();
+ ret.pop_back();
+ pi++;
+ while (pi < tmp_ret.size() && (tmp_ret[pi] == " " || tmp_ret[pi] == "\t"))
+ pi++;
+ if (pi == tmp_ret.size())
+ error("## found at end of sequence");
+ ret.push_back(cc + tmp_ret[pi]);
+ }
+ else
+ ret.push_back(tmp_ret[pi]);
+ pi++;
+ }
+ return ret;
+ }
+
+ bool ppreprocessor::is_valid_token(const pstring &str)
+ {
+ if (str.empty())
+ return false;
+ pstring::value_type c(str.at(0));
+ return ((c>='a' && c<='z') || (c>='A' && c<='Z') || c == '_');
+ }
+
+ pstring ppreprocessor::replace_macros(const pstring &line)
+ {
+ //std::vector<pstring> elems(psplit(line, m_expr_sep));
+ bool repeat(false);
+ pstring tmpret(line);
+ do
+ {
+ repeat = false;
+ simple_iter<ppreprocessor> elems(this, tokenize(tmpret, m_expr_sep, false, true));
+ tmpret = "";
+ while (!elems.eod())
+ {
+ auto token(elems.next_ws());
+ define_t *def = get_define(token);
+ if (def == nullptr)
+ tmpret += token;
+ else if (!def->m_has_params)
+ {
+ tmpret += def->m_replace;
+ repeat = true;
+ }
+ else
+ {
+ token = elems.next();
+ if (token != "(")
+ error("expected '(' in macro expansion of " + def->m_name);
+ string_list rep;
+ token = elems.next();
+ while (token != ")")
+ {
+ pstring par("");
+ int parenthesis_count(1);
+ while (true)
+ {
+ if (parenthesis_count==1 && token == ",")
+ {
+ token = elems.next();
+ break;
+ }
+ if (token == "(")
+ parenthesis_count++;
+ if (token == ")")
+ if (--parenthesis_count == 0)
+ break;
+ par += token;
+ token = elems.next();
+ }
+ rep.push_back(par);
+ }
+ repeat = true;
+ if (def->m_params.size() != rep.size())
+ error(pfmt("Expected {1} parameters, got {2}")(def->m_params.size(), rep.size()));
+ simple_iter<ppreprocessor> r(this, tokenize(def->m_replace, m_expr_sep, false, false));
+ bool stringify_next = false;
+ while (!r.eod())
+ {
+ token = r.next();
+ if (token == "#")
+ stringify_next = true;
+ else if (token != " " && token != "\t")
+ {
+ for (std::size_t i=0; i<def->m_params.size(); i++)
+ if (def->m_params[i] == token)
+ {
+ if (stringify_next)
+ {
+ stringify_next = false;
+ token = "\"" + rep[i] + "\"";
+ }
+ else
+ token = rep[i];
+ break;
+ }
+ if (stringify_next)
+ error("'#' is not followed by a macro parameter");
+ tmpret += token;
+ tmpret += " "; // make sure this is not concatenated with next token
+ }
+ else
+ tmpret += token;
+ }
+ }
+ }
+ } while (repeat);
+
+ return tmpret;
+ }
+
+ static pstring cat_remainder(const std::vector<pstring> &elems, std::size_t start, const pstring &sep)
+ {
+ pstring ret("");
+ for (std::size_t i = start; i < elems.size(); i++)
+ {
+ ret += elems[i];
+ ret += sep;
+ }
+ return ret;
+ }
+
+#if 0
+ pstring ppreprocessor::process_comments(pstring line)
+ {
+ bool in_string = false;
+
+ std::size_t e = line.size();
+ pstring ret = "";
+ for (std::size_t i=0; i < e; )
+ {
+ pstring c = plib::left(line, 1);
+ line = line.substr(1);
+ if (!m_comment)
+ {
+ if (c=="\"")
+ {
+ in_string = !in_string;
+ ret += c;
+ }
+ else if (in_string && c=="\\")
+ {
+ i++;
+ ret += (c + plib::left(line, 1));
+ line = line.substr(1);
+ }
+ else if (!in_string && c=="/" && plib::left(line,1) == "*")
+ m_comment = true;
+ else if (!in_string && c=="/" && plib::left(line,1) == "/")
+ break;
+ else
+ ret += c;
+ }
+ else
+ if (c=="*" && plib::left(line,1) == "/")
+ {
+ i++;
+ line = line.substr(1);
+ m_comment = false;
+ }
+ i++;
+ }
+ return ret;
+ }
+#else
+ pstring ppreprocessor::process_comments(const pstring &line)
+ {
+ bool in_string = false;
+
+ pstring ret = "";
+ for (auto c = line.begin(); c != line.end(); )
+ {
+ if (!m_comment)
+ {
+ if (*c == '"')
+ {
+ in_string = !in_string;
+ ret += *c;
+ }
+ else if (in_string && *c == '\\')
+ {
+ ret += *c;
+ ++c;
+ if (c == line.end())
+ break;
+ ret += *c;
+ }
+ else if (!in_string && *c == '/')
+ {
+ ++c;
+ if (c == line.end())
+ break;
+ if (*c == '*')
+ m_comment = true;
+ else if (*c == '/')
+ break;
+ else
+ ret += *c;
+ }
+ else
+ ret += *c;
+ }
+ else
+ if (*c == '*')
+ {
+ c++;
+ if (c == line.end())
+ break;
+ if (*c == '/')
+ m_comment = false;
+ }
+ c++;
+ }
+ return ret;
+ }
+#endif
+
+ std::pair<pstring,bool> ppreprocessor::process_line(const pstring &line_in)
+ {
+ bool line_cont = plib::right(line_in, 1) == "\\";
+
+ pstring line = line_cont ? plib::left(line_in, line_in.length() - 1) : line_in;
+
+ if (m_state == LINE_CONTINUATION)
+ m_line += line;
+ else
+ m_line = line;
+
+ if (line_cont)
+ {
+ m_state = LINE_CONTINUATION;
+ return {"", false};
+ }
+
+ m_state = PROCESS;
+
+ line = process_comments(m_line);
+
+ pstring lt = plib::trim(plib::replace_all(line, '\t', ' '));
+ if (plib::startsWith(lt, "#"))
+ {
+ string_list lti(psplit(lt, ' ', true));
+ if (lti[0] == "#if")
+ {
+ m_if_level++;
+ m_if_seen |= (1 << m_if_level);
+ if (m_if_flag == 0)
+ {
+ lt = replace_macros(lt);
+ simple_iter<ppreprocessor> t(this, tokenize(lt.substr(3), m_expr_sep, true, true));
+ auto val = narrow_cast<int>(prepro_expr(t, 255));
+ t.skip_ws();
+ if (!t.eod())
+ error("found unprocessed content at end of line");
+ if (val == 0)
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_elif |= (1 << m_if_level);
+ }
+ }
+ else if (lti[0] == "#ifdef")
+ {
+ m_if_level++;
+ m_if_seen |= (1 << m_if_level);
+ if (get_define(lti[1]) == nullptr)
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_elif |= (1 << m_if_level);
+ }
+ else if (lti[0] == "#ifndef")
+ {
+ m_if_level++;
+ m_if_seen |= (1 << m_if_level);
+ if (get_define(lti[1]) != nullptr)
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_elif |= (1 << m_if_level);
+ }
+ else if (lti[0] == "#else") // basically #elif (1)
+ {
+ if (!(m_if_seen & (1 << m_if_level)))
+ error("#else without #if");
+
+ if (m_elif & (1 << m_if_level)) // elif disabled
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_if_flag &= ~(1 << m_if_level);
+ m_elif |= (1 << m_if_level);
+ }
+ else if (lti[0] == "#elif")
+ {
+ if (!(m_if_seen & (1 << m_if_level)))
+ error("#elif without #if");
+
+ //if ((m_if_flag & (1 << m_if_level)) == 0)
+ // m_if_flag ^= (1 << m_if_level);
+ if (m_elif & (1 << m_if_level)) // elif disabled
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_if_flag &= ~(1 << m_if_level);
+ if (m_if_flag == 0)
+ {
+ //m_if_flag ^= (1 << m_if_level);
+ lt = replace_macros(lt);
+ simple_iter<ppreprocessor> t(this, tokenize(lt.substr(5), m_expr_sep, true, true));
+ auto val = narrow_cast<int>(prepro_expr(t, 255));
+ t.skip_ws();
+ if (!t.eod())
+ error("found unprocessed content at end of line");
+ if (val == 0)
+ m_if_flag |= (1 << m_if_level);
+ else
+ m_elif |= (1 << m_if_level);
+ }
+ }
+ else if (lti[0] == "#endif")
+ {
+ if (!(m_if_seen & (1 << m_if_level)))
+ error("#else without #if");
+ m_if_seen &= ~(1 << m_if_level);
+ m_elif &= ~(1 << m_if_level);
+ m_if_flag &= ~(1 << m_if_level);
+ m_if_level--;
+ }
+ else if (lti[0] == "#include")
+ {
+ if (m_if_flag == 0)
+ {
+ pstring arg("");
+ for (std::size_t i=1; i<lti.size(); i++)
+ arg += (lti[i] + " ");
+
+ arg = plib::trim(arg);
+
+ if (startsWith(arg, "\"") && endsWith(arg, "\""))
+ {
+ arg = arg.substr(1, arg.length() - 2);
+ // first try local context
+ auto l(plib::util::build_path({m_stack.back().m_local_path, arg}));
+ auto lstrm(m_sources.get_stream(l));
+ if (!lstrm.empty())
+ {
+ m_stack.emplace_back(input_context(lstrm.release_stream(), plib::util::path(l), l));
+ }
+ else
+ {
+ auto strm(m_sources.get_stream(arg));
+ if (!strm.empty())
+ {
+ m_stack.emplace_back(input_context(strm.release_stream(), plib::util::path(arg), arg));
+ }
+ else
+ error("include not found:" + arg);
+ }
+ }
+ else
+ error("include misspelled:" + arg);
+ pstring line_marker = pfmt("# {1} \"{2}\" 1\n")(m_stack.back().m_lineno, m_stack.back().m_name);
+ push_out(line_marker);
+ }
+ }
+ else if (lti[0] == "#pragma")
+ {
+ if (m_if_flag == 0 && lti.size() > 3 && lti[1] == "NETLIST")
+ {
+ if (lti[2] == "warning")
+ error("NETLIST: " + cat_remainder(lti, 3, " "));
+ }
+ }
+ else if (lti[0] == "#define")
+ {
+ if (m_if_flag == 0)
+ {
+ if (lti.size() < 2)
+ error("define needs at least one argument");
+ simple_iter<ppreprocessor> args(this, tokenize(lt.substr(8), m_expr_sep, false, false));
+ pstring n = args.next();
+ if (!is_valid_token(n))
+ error("define expected identifier");
+ auto *previous_define = get_define(n);
+ if (lti.size() == 2)
+ {
+ if (previous_define != nullptr && !previous_define->m_replace.empty())
+ error("redefinition of " + n);
+ m_defines.insert({n, define_t(n, "")});
+ }
+ else if (args.next_ws() == "(")
+ {
+ define_t def(n);
+ def.m_has_params = true;
+ auto token(args.next());
+ while (true)
+ {
+ if (token == ")")
+ break;
+ def.m_params.push_back(token);
+ token = args.next();
+ if (token != "," && token != ")")
+ error(pfmt("expected , or ), found <{1}>")(token));
+ if (token == ",")
+ token = args.next();
+ }
+ pstring r;
+ while (!args.eod())
+ r += args.next_ws();
+ def.m_replace = r;
+ if (previous_define != nullptr && previous_define->m_replace != r)
+ error("redefinition of " + n);
+ m_defines.insert({n, def});
+ }
+ else
+ {
+ pstring r;
+ while (!args.eod())
+ r += args.next_ws();
+ if (previous_define != nullptr && previous_define->m_replace != r)
+ error("redefinition of " + n);
+ m_defines.insert({n, define_t(n, r)});
+ }
+ }
+ }
+ else if (lti[0] == "#undef")
+ {
+ if (m_if_flag == 0)
+ {
+ if (lti.size() < 2)
+ error("undef needs at least one argument");
+ simple_iter<ppreprocessor> args(this, tokenize(lt.substr(7), m_expr_sep, false, false));
+ pstring n = args.next();
+ if (!is_valid_token(n))
+ error("undef expected identifier");
+ m_defines.erase(n);
+ }
+ }
+ else
+ {
+ if (m_if_flag == 0)
+ error("unknown directive");
+ }
+ return { "", false };
+ }
+
+ if (m_if_flag == 0)
+ return { replace_macros(lt), true };
+
+ return { "", false };
+ }
+
+ void ppreprocessor::push_out(const pstring &s)
+ {
+ m_outbuf += s;
+ if (m_debug_out)
+ std::cerr << putf8string(s);
+ }
+
+ void ppreprocessor::process_stack()
+ {
+ while (!m_stack.empty())
+ {
+ putf8string line;
+ pstring line_marker = pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno, m_stack.back().m_name);
+ push_out(line_marker);
+ bool last_skipped=false;
+ while (m_stack.back().m_reader.read_line(line))
+ {
+ m_stack.back().m_lineno++;
+ auto r(process_line(pstring(line)));
+ if (r.second)
+ {
+ if (last_skipped)
+ push_out(pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno, m_stack.back().m_name));
+ push_out(r.first + "\n");
+ last_skipped = false;
+ }
+ else
+ last_skipped = true;
+ }
+ m_stack.pop_back();
+ if (!m_stack.empty())
+ {
+ line_marker = pfmt("# {1} \"{2}\" 2\n")(m_stack.back().m_lineno, m_stack.back().m_name);
+ push_out(line_marker);
+ }
+ }
+ }
+
+
+
+} // namespace plib
diff --git a/src/lib/netlist/plib/ppreprocessor.h b/src/lib/netlist/plib/ppreprocessor.h
new file mode 100644
index 00000000000..1e21e72a5b0
--- /dev/null
+++ b/src/lib/netlist/plib/ppreprocessor.h
@@ -0,0 +1,127 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PPREPROCESSOR_H_
+#define PPREPROCESSOR_H_
+
+///
+/// \file ppreprocessor.h
+///
+
+#include "pstream.h"
+#include "pstring.h"
+
+#include "psource.h"
+#include "putil.h"
+
+#include <istream>
+#include <unordered_map>
+#include <vector>
+
+namespace plib {
+
+ class ppreprocessor
+ {
+ public:
+
+ using string_list = std::vector<pstring>;
+
+ struct define_t
+ {
+ define_t(const pstring &name, const pstring &replace)
+ : m_name(name), m_replace(replace), m_has_params(false)
+ {}
+ explicit define_t(const pstring &name)
+ : m_name(name), m_replace(""), m_has_params(false)
+ {}
+ pstring m_name;
+ pstring m_replace;
+ bool m_has_params;
+ string_list m_params;
+ };
+
+ using defines_map_type = std::unordered_map<pstring, define_t>;
+
+ explicit ppreprocessor(psource_collection_t &sources, defines_map_type *defines = nullptr);
+
+ PCOPYASSIGNMOVE(ppreprocessor, delete)
+
+ ~ppreprocessor() = default;
+
+ /// \brief process stream
+ ///
+ /// \param filename a filename or identifier identifying the stream.
+ ///
+ /// FIXME: this is sub-optimal. Refactor input_context into `pinput`_context
+ /// and pass this to `ppreprocessor`.
+ ///
+ template <typename T>
+ pstring process(T &&istrm, const pstring &filename)
+ {
+ m_outbuf.clear();
+ m_stack.emplace_back(input_context(istrm.release_stream(),plib::util::path(filename), filename));
+ process_stack();
+ return m_outbuf;
+ }
+
+ [[noreturn]] void error(const pstring &err) noexcept(false);
+
+ define_t *get_define(const pstring &name);
+ pstring replace_macros(const pstring &line);
+
+ private:
+
+ enum state_e
+ {
+ PROCESS,
+ LINE_CONTINUATION
+ };
+
+ void push_out(const pstring &s);
+
+ void process_stack();
+
+ string_list tokenize(const pstring &str, const string_list &sep, bool remove_ws, bool concat);
+ static bool is_valid_token(const pstring &str);
+
+ std::pair<pstring,bool> process_line(const pstring &line_in);
+ pstring process_comments(const pstring &line);
+
+ defines_map_type m_defines;
+ psource_collection_t &m_sources;
+ string_list m_expr_sep;
+
+ std::uint_least64_t m_if_flag; // 63 if levels
+ std::uint_least64_t m_if_seen; // 63 if levels
+ std::uint_least64_t m_elif; // 63 if levels - for #elif
+ int m_if_level;
+
+ struct input_context
+ {
+ template <typename T>
+ input_context(T &&istrm, const pstring &local_path, const pstring &name)
+ : m_reader(std::forward<T>(istrm))
+ , m_lineno(0)
+ , m_local_path(local_path)
+ , m_name(name)
+ {}
+
+ putf8_reader m_reader;
+ int m_lineno;
+ pstring m_local_path;
+ pstring m_name;
+ };
+
+ // vector used as stack because we need to loop through stack
+ std::vector<input_context> m_stack;
+ pstring m_outbuf;
+ state_e m_state;
+ pstring m_line;
+ bool m_comment;
+ bool m_debug_out;
+
+ };
+
+} // namespace plib
+
+#endif // PPREPROCESSOR_H_
diff --git a/src/lib/netlist/plib/prandom.h b/src/lib/netlist/plib/prandom.h
new file mode 100644
index 00000000000..3772343826a
--- /dev/null
+++ b/src/lib/netlist/plib/prandom.h
@@ -0,0 +1,233 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PRANDOM_H_
+#define PRANDOM_H_
+
+///
+/// \file pmath.h
+///
+
+#include "pconfig.h"
+#include "pgsl.h"
+#include "pmath.h"
+#include "ptypes.h"
+
+//#include <algorithm>
+//#include <cmath>
+//#include <type_traits>
+#include <array>
+
+namespace plib
+{
+
+ /// \brief Mersenne Twister implementation which is state saveable
+ ///
+ /// This is a Mersenne Twister implementation which is state saveable.
+ /// It has been written following this wikipedia entry:
+ ///
+ /// https://en.wikipedia.org/wiki/Mersenne_Twister
+ ///
+ /// The implementation has basic support for the interface described here
+ ///
+ /// https://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine
+ ///
+ /// so that it can be used with the C++11 random environment
+ ///
+ template<typename T,
+ std::size_t w, std::size_t N, std::size_t m, std::size_t r,
+ T a,
+ std::size_t u, T d,
+ std::size_t s, T b,
+ std::size_t t, T c,
+ std::size_t l, T f>
+ class mersenne_twister_t
+ {
+ public:
+
+ mersenne_twister_t()
+ : m_p(N)
+ {
+ seed(5489);
+ }
+
+ static constexpr T min() noexcept { return T(0); }
+ static constexpr T max() noexcept { return ~T(0) >> (sizeof(T)*8 - w); }
+
+ template <typename ST>
+ void save_state(ST &st)
+ {
+ st.save_item(m_p, "index");
+ st.save_item(m_mt, "mt");
+ }
+
+ void seed(T val) noexcept
+ {
+ const T lowest_w(~T(0) >> (sizeof(T)*8 - w));
+ m_p = N;
+ m_mt[0] = val;
+ for (std::size_t i=1; i< N; i++)
+ m_mt[i] = (f * (m_mt[i-1] ^ (m_mt[i-1] >> (w-2))) + i) & lowest_w;
+ }
+
+ T operator()() noexcept
+ {
+ const T lowest_w(~T(0) >> (sizeof(T)*8 - w));
+ if (m_p >= N)
+ twist();
+
+ T y = m_mt[m_p++];
+ y = y ^ ((y >> u) & d);
+ y = y ^ ((y << s) & b);
+ y = y ^ ((y << t) & c);
+ y = y ^ (y >> l);
+
+ return y & lowest_w;
+ }
+
+ void discard(std::size_t v) noexcept
+ {
+ if (v > N - m_p)
+ {
+ v -= N - m_p;
+ twist();
+ }
+ while (v > N)
+ {
+ v -= N;
+ twist();
+ }
+ m_p += v;
+ }
+
+ private:
+ void twist() noexcept
+ {
+ const T lowest_w(~T(0) >> (sizeof(T)*8 - w));
+ const T lower_mask((T(1) << r) - 1); // That is, the binary number of r 1's
+ const T upper_mask((~lower_mask) & lowest_w);
+
+ for (std::size_t i=0; i<N; i++)
+ {
+ const T x((m_mt[i] & upper_mask) + (m_mt[(i+1) % N] & lower_mask));
+ const T xA((x >> 1) ^ ((x & 1) ? a : 0));
+ m_mt[i] = m_mt[(i + m) % N] ^ xA;
+ }
+ m_p = 0;
+ }
+
+ std::size_t m_p;
+ std::array<T, N> m_mt;
+ };
+
+ template <typename FT, typename T>
+ FT normalize_uniform(T &p, FT m = constants<FT>::one(), FT b = constants<FT>::zero()) noexcept
+ {
+ constexpr const FT mmin(narrow_cast<FT>(T::min()));
+ constexpr const FT mmax(narrow_cast<FT>(T::max()));
+ // -> 0 to a
+ return (narrow_cast<FT>(p())- mmin) / (mmax - mmin) * m - b;
+ }
+
+ template<typename FT>
+ class uniform_distribution_t
+ {
+ public:
+ uniform_distribution_t(FT dev)
+ : m_stddev(dev) { }
+
+ template <typename P>
+ FT operator()(P &p) noexcept
+ {
+ // get -1 to 1
+ return normalize_uniform(p, constants<FT>::two(), constants<FT>::one())
+ * constants<FT>::sqrt3() * m_stddev;
+ }
+
+ template <typename ST>
+ void save_state([[maybe_unused]] ST &st)
+ {
+ // no state to save
+ }
+
+ private:
+ FT m_stddev;
+ };
+
+ template<typename FT>
+ class normal_distribution_t
+ {
+ public:
+ normal_distribution_t(FT dev)
+ : m_p(m_buf.size()), m_stddev(dev) { }
+
+ // Donald Knuth, Algorithm P (Polar method)
+
+ template <typename P>
+ FT operator()(P &p) noexcept
+ {
+ if (m_p >= m_buf.size())
+ fill(p);
+ return m_buf[m_p++];
+ }
+
+ template <typename ST>
+ void save_state(ST &st)
+ {
+ st.save_item(m_p, "m_p");
+ st.save_item(m_buf, "m_buf");
+ }
+
+ private:
+
+ template <typename P>
+ void fill(P &p) noexcept
+ {
+ for (std::size_t i = 0; i < m_buf.size(); i += 2)
+ {
+ FT s;
+ FT v1;
+ FT v2;
+ do
+ {
+ v1 = normalize_uniform(p, constants<FT>::two(), constants<FT>::one()); // [-1..1]
+ v2 = normalize_uniform(p, constants<FT>::two(), constants<FT>::one()); // [-1..1]
+ s = v1 * v1 + v2 * v2;
+ } while (s >= constants<FT>::one());
+ if (s == constants<FT>::zero())
+ {
+ m_buf[i] = s;
+ m_buf[i+1] = s;
+ }
+ else
+ {
+ // last value without error for log(s)/s
+ // double: 1.000000e-305
+ // float: 9.999999e-37
+ // FIXME: with 128 bit randoms log(s)/w will fail 1/(2^128) ~ 2.9e-39
+ const FT m(m_stddev * plib::sqrt(-constants<FT>::two() * plib::log(s)/s));
+ m_buf[i] = /*mean+*/ m * v1;
+ m_buf[i+1] = /*mean+*/ m * v2;
+ }
+ }
+ m_p = 0;
+ }
+
+ std::array<FT, 256> m_buf;
+ std::size_t m_p;
+ FT m_stddev;
+ };
+
+ using mt19937_64 = mersenne_twister_t<
+ uint_fast64_t,
+ 64, 312, 156, 31,
+ 0xb5026f5aa96619e9ULL,
+ 29, 0x5555555555555555ULL,
+ 17, 0x71d67fffeda60000ULL,
+ 37, 0xfff7eee000000000ULL,
+ 43,
+ 6364136223846793005ULL>;
+
+} // namespace plib
+
+#endif // PRANDOM_H_
diff --git a/src/lib/netlist/plib/psource.h b/src/lib/netlist/plib/psource.h
new file mode 100644
index 00000000000..80a313ec415
--- /dev/null
+++ b/src/lib/netlist/plib/psource.h
@@ -0,0 +1,173 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PSOURCE_H_
+#define PSOURCE_H_
+
+///
+/// \file putil.h
+///
+
+#include "palloc.h"
+#include "pexception.h"
+#include "pstream.h"
+#include "pstring.h"
+
+#include <algorithm>
+#include <initializer_list>
+#include <sstream>
+#include <vector>
+
+
+#define PSOURCELOC() plib::source_location(__FILE__, __LINE__)
+
+namespace plib
+{
+
+ /// \brief Source code locations.
+ ///
+ /// The c++20 draft for source locations is based on const char * strings.
+ /// It is thus only suitable for c++ source code and not for programmatic
+ /// parsing of files. This class is a replacement for dynamic use cases.
+ ///
+ struct source_location
+ {
+ source_location() noexcept
+ : m_file("unknown"), m_func(m_file), m_line(0), m_col(0)
+ { }
+
+ source_location(pstring file, unsigned line) noexcept
+ : m_file(std::move(file)), m_func("unknown"), m_line(line), m_col(0)
+ { }
+
+ source_location(pstring file, pstring func, unsigned line) noexcept
+ : m_file(std::move(file)), m_func(std::move(func)), m_line(line), m_col(0)
+ { }
+
+ PCOPYASSIGNMOVE(source_location, default)
+
+ ~source_location() = default;
+
+ unsigned line() const noexcept { return m_line; }
+ unsigned column() const noexcept { return m_col; }
+ pstring file_name() const noexcept { return m_file; }
+ pstring function_name() const noexcept { return m_func; }
+
+ source_location &operator ++() noexcept
+ {
+ ++m_line;
+ return *this;
+ }
+
+ private:
+ pstring m_file;
+ pstring m_func;
+ unsigned m_line;
+ unsigned m_col;
+ };
+
+ /// \brief Base source class.
+ ///
+ /// Pure virtual class all other source implementations are based on.
+ /// Sources provide an abstraction to read input from a variety of
+ /// sources, e.g. files, memory, remote locations.
+ ///
+ class psource_t
+ {
+ public:
+
+ psource_t() noexcept = default;
+
+ PCOPYASSIGNMOVE(psource_t, delete)
+
+ virtual ~psource_t() noexcept = default;
+
+ virtual istream_uptr stream(const pstring &name) = 0;
+ private:
+ };
+
+ /// \brief Generic string source.
+ ///
+ /// Will return the given string when name matches.
+ /// Is used in preprocessor code to eliminate inclusion of certain files.
+ ///
+ class psource_str_t : public psource_t
+ {
+ public:
+ psource_str_t(pstring name, pstring str)
+ : m_name(std::move(name)), m_str(std::move(str))
+ {}
+
+ PCOPYASSIGNMOVE(psource_str_t, delete)
+ ~psource_str_t() noexcept override = default;
+
+ istream_uptr stream(const pstring &name) override
+ {
+ if (name == m_name)
+ return {std::make_unique<std::stringstream>(putf8string(m_str)), name };
+
+ return istream_uptr();
+ }
+ private:
+ pstring m_name;
+ pstring m_str;
+ };
+
+ /// \brief Generic sources collection.
+ ///
+ class psource_collection_t
+ {
+ public:
+ using source_ptr = std::unique_ptr<psource_t>;
+ using list_t = std::vector<source_ptr>;
+
+ psource_collection_t() noexcept = default;
+
+ PCOPYASSIGNMOVE(psource_collection_t, delete)
+ virtual ~psource_collection_t() noexcept = default;
+
+ template <typename S, typename... Args>
+ void add_source(Args&&... args)
+ {
+ static_assert(std::is_base_of<psource_t, S>::value, "S must inherit from plib::psource_t");
+
+ auto src = std::make_unique<S>(std::forward<Args>(args)...);
+ m_collection.push_back(std::move(src));
+ }
+
+ template <typename S = psource_t>
+ istream_uptr get_stream(pstring name)
+ {
+ for (auto &s : m_collection)
+ {
+ if (auto source = plib::dynamic_downcast<S *>(s.get()))
+ {
+ auto strm = (*source)->stream(name);
+ if (!strm.empty())
+ return strm;
+ }
+ }
+ return istream_uptr();
+ }
+
+ template <typename S, typename F>
+ bool for_all(F lambda)
+ {
+ for (auto &s : m_collection)
+ {
+ if (auto source = plib::dynamic_downcast<S *>(s.get()))
+ {
+ if (lambda(*source))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ list_t m_collection;
+ };
+
+} // namespace plib
+
+#endif // PSOURCE_H_
diff --git a/src/lib/netlist/plib/pstate.cpp b/src/lib/netlist/plib/pstate.cpp
deleted file mode 100644
index 3bd93ed4f8e..00000000000
--- a/src/lib/netlist/plib/pstate.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * pstate.c
- *
- */
-
-#include "pstate.h"
-#include "palloc.h"
-
-namespace plib {
-
-void state_manager_t::save_state_ptr(const void *owner, const pstring &stname, const datatype_t &dt, const std::size_t count, void *ptr)
-{
- auto p = plib::make_unique<entry_t>(stname, dt, owner, count, ptr);
- m_save.push_back(std::move(p));
-}
-
-void state_manager_t::remove_save_items(const void *owner)
-{
- auto i = m_save.end();
- while (i != m_save.begin())
- {
- i--;
- if (i->get()->m_owner == owner)
- i = m_save.erase(i);
- }
- i = m_custom.end();
- while (i > m_custom.begin())
- {
- i--;
- if (i->get()->m_owner == owner)
- i = m_custom.erase(i);
- }
-}
-
-void state_manager_t::pre_save()
-{
- for (auto & s : m_custom)
- s->m_callback->on_pre_save(*this);
-}
-
-void state_manager_t::post_load()
-{
- for (auto & s : m_custom)
- s->m_callback->on_post_load(*this);
-}
-
-template<> void state_manager_t::save_item(const void *owner, callback_t &state, const pstring &stname)
-{
- callback_t *state_p = &state;
- auto p = plib::make_unique<entry_t>(stname, owner, state_p);
- m_custom.push_back(std::move(p));
- state.register_state(*this, stname);
-}
-
-} // namespace plib
diff --git a/src/lib/netlist/plib/pstate.h b/src/lib/netlist/plib/pstate.h
index ac75ca6b69e..72473590b9b 100644
--- a/src/lib/netlist/plib/pstate.h
+++ b/src/lib/netlist/plib/pstate.h
@@ -1,14 +1,13 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pstate.h
- *
- */
#ifndef PSTATE_H_
#define PSTATE_H_
-#include "palloc.h"
+///
+/// \file pstate.h
+///
+
#include "pstring.h"
#include "ptypes.h"
@@ -28,16 +27,22 @@ public:
struct datatype_t
{
datatype_t(std::size_t bsize, bool bintegral, bool bfloat)
- : size(bsize), is_integral(bintegral), is_float(bfloat), is_custom(false)
+ : m_size(bsize), m_is_integral(bintegral), m_is_float(bfloat), m_is_custom(false)
{}
explicit datatype_t(bool bcustom)
- : size(0), is_integral(false), is_float(false), is_custom(bcustom)
+ : m_size(0), m_is_integral(false), m_is_float(false), m_is_custom(bcustom)
{}
- const std::size_t size;
- const bool is_integral;
- const bool is_float;
- const bool is_custom;
+ std::size_t size() const noexcept { return m_size; }
+ bool is_integral() const noexcept { return m_is_integral; }
+ bool is_float() const noexcept { return m_is_float; }
+ bool is_custom() const noexcept { return m_is_custom; }
+
+ private:
+ std::size_t m_size;
+ bool m_is_integral;
+ bool m_is_float;
+ bool m_is_custom;
};
template<typename T>
@@ -45,10 +50,10 @@ public:
{
return datatype_t(sizeof(T),
plib::is_integral<T>::value || std::is_enum<T>::value,
- std::is_floating_point<T>::value);
+ plib::is_floating_point<T>::value);
}
- class callback_t
+ struct callback_t
{
public:
using list_t = std::vector<callback_t *>;
@@ -58,85 +63,165 @@ public:
virtual void on_post_load(state_manager_t &manager) = 0;
protected:
callback_t() = default;
- ~callback_t() = default;
- COPYASSIGNMOVE(callback_t, default)
+ virtual ~callback_t() = default;
+ PCOPYASSIGNMOVE(callback_t, default)
};
struct entry_t
{
- using list_t = std::vector<plib::unique_ptr<entry_t>>;
+ using list_t = std::vector<entry_t>;
- entry_t(const pstring &stname, const datatype_t &dt, const void *owner,
+ entry_t(const pstring &item_name, const datatype_t &dt, const void *owner,
const std::size_t count, void *ptr)
- : m_name(stname), m_dt(dt), m_owner(owner), m_callback(nullptr), m_count(count), m_ptr(ptr) { }
+ : m_name(item_name), m_dt(dt), m_owner(owner), m_callback(nullptr), m_count(count), m_ptr(ptr) { }
- entry_t(const pstring &stname, const void *owner, callback_t *callback)
- : m_name(stname), m_dt(datatype_t(true)), m_owner(owner), m_callback(callback), m_count(0), m_ptr(nullptr) { }
+ entry_t(const pstring &item_name, const void *owner, callback_t *callback)
+ : m_name(item_name), m_dt(datatype_t(true)), m_owner(owner), m_callback(callback), m_count(0), m_ptr(nullptr) { }
+ pstring name() const noexcept { return m_name; }
+ datatype_t dt() const noexcept { return m_dt; }
+ const void * owner() const noexcept { return m_owner; }
+ callback_t * callback() const noexcept { return m_callback; }
+ std::size_t count() const noexcept { return m_count; }
+ void * ptr() const noexcept { return m_ptr; }
+
+ private:
pstring m_name;
- const datatype_t m_dt;
+ datatype_t m_dt;
const void * m_owner;
callback_t * m_callback;
- const std::size_t m_count;
+ std::size_t m_count;
void * m_ptr;
};
state_manager_t() = default;
+ struct saver_t
+ {
+ saver_t(state_manager_t &sm, const void *owner, const pstring &member_name)
+ : m_sm(sm)
+ , m_owner(owner)
+ , m_member_name(member_name)
+ { }
+
+ template <typename XS>
+ void save_item(XS &some_state, const pstring &item_name)
+ {
+ m_sm.save_item(m_owner, some_state, m_member_name + "." + item_name);
+ }
+
+ state_manager_t &m_sm;
+ const void * m_owner;
+ const pstring m_member_name;
+ };
+
template<typename C>
- void save_item(const void *owner, C &state, const pstring &stname)
+ void save_item(const void *owner, C &state, const pstring &item_name)
{
- save_state_ptr( owner, stname, dtype<C>(), 1, &state);
+ save_item_dispatch(owner, state, item_name);
}
template<typename C, std::size_t N>
- void save_item(const void *owner, C (&state)[N], const pstring &stname) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
+ void save_item(const void *owner, C (&state)[N], const pstring &item_name) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
{
- save_state_ptr(owner, stname, dtype<C>(), N, &(state[0]));
+ save_state_ptr(owner, item_name, dtype<C>(), N, &(state[0]));
}
template<typename C>
- void save_item(const void *owner, C *state, const pstring &stname, const std::size_t count)
+ void save_item(const void *owner, C *state, const pstring &item_name, const std::size_t count)
{
- save_state_ptr(owner, stname, dtype<C>(), count, state);
+ save_state_ptr(owner, item_name, dtype<C>(), count, state);
}
- template<typename C>
- void save_item(const void *owner, std::vector<C> &v, const pstring &stname)
+ template<typename C, typename A>
+ void save_item(const void *owner, std::vector<C, A> &v, const pstring &item_name)
{
- save_state_ptr(owner, stname, dtype<C>(), v.size(), v.data());
+ save_state_ptr(owner, item_name, dtype<C>(), v.size(), v.data());
}
template<typename C, std::size_t N>
- void save_item(const void *owner, std::array<C, N> &a, const pstring &stname)
+ void save_item(const void *owner, std::array<C, N> &a, const pstring &item_name)
+ {
+ save_state_ptr(owner, item_name, dtype<C>(), N, a.data());
+ }
+
+ void save_state_ptr(const void *owner, const pstring &item_name, const datatype_t &dt, const std::size_t count, void *ptr)
+ {
+ m_save.emplace_back(item_name, dt, owner, count, ptr);
+ }
+
+ void pre_save()
+ {
+ for (auto & s : m_custom)
+ s.callback()->on_pre_save(*this);
+ }
+
+ void post_load()
{
- save_state_ptr(owner, stname, dtype<C>(), N, a.data());
+ for (auto & s : m_custom)
+ s.callback()->on_post_load(*this);
}
- void pre_save();
- void post_load();
- void remove_save_items(const void *owner);
+ void remove_save_items(const void *owner)
+ {
+ auto i = m_save.end();
+ while (i != m_save.begin())
+ {
+ i--;
+ if (i->owner() == owner)
+ i = m_save.erase(i);
+ }
+ i = m_custom.end();
+ while (i > m_custom.begin())
+ {
+ i--;
+ if (i->owner() == owner)
+ i = m_custom.erase(i);
+ }
+ }
- const std::vector<const entry_t *> save_list() const
+ std::vector<const entry_t *> save_list() const
{
std::vector<const entry_t *> ret;
- for (auto &i : m_save)
- ret.push_back(i.get());
+ for (const auto &i : m_save)
+ ret.push_back(&i);
return ret;
}
- void save_state_ptr(const void *owner, const pstring &stname, const datatype_t &dt, const std::size_t count, void *ptr);
-
protected:
private:
+
+ template<typename C>
+ std::enable_if_t<plib::is_integral<C>::value || std::is_enum<C>::value
+ || plib::is_floating_point<C>::value>
+ save_item_dispatch(const void *owner, C &state, const pstring &item_name)
+ {
+ save_state_ptr( owner, item_name, dtype<C>(), 1, &state);
+ }
+
+ template<typename C>
+ std::enable_if_t<!(plib::is_integral<C>::value || std::is_enum<C>::value
+ || plib::is_floating_point<C>::value)>
+ save_item_dispatch(const void *owner, C &state, const pstring &item_name)
+ {
+ saver_t sav(*this, owner, item_name);
+ state.save_state(sav);
+ }
+
entry_t::list_t m_save;
entry_t::list_t m_custom;
};
-template<> void state_manager_t::save_item(const void *owner, callback_t &state, const pstring &stname);
+template<>
+inline void state_manager_t::save_item(const void *owner, callback_t &state, const pstring &item_name)
+{
+ m_custom.emplace_back(item_name, owner, &state);
+ state.register_state(*this, item_name);
+}
+
} // namespace plib
-#endif /* PSTATE_H_ */
+#endif // PSTATE_H_
diff --git a/src/lib/netlist/plib/pstonum.h b/src/lib/netlist/plib/pstonum.h
new file mode 100644
index 00000000000..18c67ca2b86
--- /dev/null
+++ b/src/lib/netlist/plib/pstonum.h
@@ -0,0 +1,145 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PSTONUM_H_
+#define PSTONUM_H_
+
+///
+/// \file pstonum.h
+///
+
+#include "pconfig.h"
+#include "pexception.h"
+#include "pgsl.h"
+#include "pmath.h" // for pstonum
+#include "pstring.h"
+
+#include <algorithm>
+#include <initializer_list>
+#include <iostream>
+#include <locale>
+#include <sstream>
+
+namespace plib
+{
+ // ----------------------------------------------------------------------------------------
+ // number conversions
+ // ----------------------------------------------------------------------------------------
+
+ template <typename T, typename S>
+ T pstonum_locale(const std::locale &loc, const S &arg, bool *err)
+ {
+ std::stringstream ss;
+ ss.imbue(loc);
+ ss << putf8string(arg);
+ auto len(ss.tellp());
+ T x(constants<T>::zero());
+ if (ss >> x)
+ {
+ auto pos(ss.tellg());
+ if (pos == decltype(pos)(-1))
+ pos = len;
+ *err = (pos != len);
+ }
+ else
+ *err = true;
+ return x;
+ }
+
+ template <typename T, typename E = void>
+ struct pstonum_helper;
+
+ template<typename T>
+ struct pstonum_helper<T, std::enable_if_t<plib::is_integral<T>::value && plib::is_signed<T>::value>>
+ {
+ template <typename S>
+ long long operator()(std::locale loc, const S &arg, bool *err)
+ {
+ //return std::stoll(arg, idx);
+ return pstonum_locale<long long>(loc, arg, err);
+ }
+ };
+
+ template<typename T>
+ struct pstonum_helper<T, std::enable_if_t<plib::is_integral<T>::value && !plib::is_signed<T>::value>>
+ {
+ template <typename S>
+ unsigned long long operator()(std::locale loc, const S &arg, bool *err)
+ {
+ //return std::stoll(arg, idx);
+ return pstonum_locale<unsigned long long>(loc, arg, err);
+ }
+ };
+
+ template<typename T>
+ struct pstonum_helper<T, std::enable_if_t<std::is_floating_point<T>::value>>
+ {
+ template <typename S>
+ long double operator()(std::locale loc, const S &arg, bool *err)
+ {
+ return pstonum_locale<long double>(loc, arg, err);
+ }
+ };
+
+#if PUSE_FLOAT128
+ template<>
+ struct pstonum_helper<FLOAT128>
+ {
+ // FIXME: use `strtoflt128` from `quadmath.h`
+ template <typename S>
+ FLOAT128 operator()(std::locale loc, const S &arg, bool *err)
+ {
+ return narrow_cast<FLOAT128>(pstonum_locale<long double>(loc, arg, err));
+ }
+ };
+#endif
+
+ template<typename T, typename S>
+ T pstonum(const S &arg, const std::locale &loc = std::locale::classic()) noexcept(false)
+ {
+ bool err(false);
+ auto ret = pstonum_helper<T>()(loc, arg, &err);
+ if (err)
+ throw pexception(pstring("Error converting string to number: ") + pstring(arg));
+
+ using ret_type = decltype(ret);
+ if (ret >= narrow_cast<ret_type>(plib::numeric_limits<T>::lowest())
+ && ret <= narrow_cast<ret_type>(plib::numeric_limits<T>::max()))
+ {
+ return narrow_cast<T>(ret);
+ }
+
+ throw pexception(pstring("Out of range: ") + pstring(arg));
+ }
+
+ template<typename R, typename T>
+ R pstonum_ne(const T &str, bool &err, std::locale loc = std::locale::classic()) noexcept
+ {
+ try
+ {
+ err = false;
+ return pstonum<R>(str, loc);
+ }
+ catch (...)
+ {
+ err = true;
+ return R(0);
+ }
+ }
+
+ template<typename R, typename T>
+ R pstonum_ne_def(const T &str, R def, std::locale loc = std::locale::classic()) noexcept
+ {
+ try
+ {
+ return pstonum<R>(str, loc);
+ }
+ catch (...)
+ {
+ return def;
+ }
+ }
+
+} // namespace plib
+
+#endif // PUTIL_H_
diff --git a/src/lib/netlist/plib/pstream.cpp b/src/lib/netlist/plib/pstream.cpp
deleted file mode 100644
index 1e805825af3..00000000000
--- a/src/lib/netlist/plib/pstream.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * pstream.c
- *
- */
-
-#include "pstream.h"
-#include "palloc.h"
-
-#include <algorithm>
-#include <cstdio>
-//#include <cstdlib>
-
-// VS2015 prefers _dup
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-namespace plib {
-
-
-bool putf8_reader::readline(pstring &line)
-{
- putf8string::code_t c = 0;
- m_linebuf = "";
- if (!this->readcode(c))
- {
- line = "";
- return false;
- }
- while (true)
- {
- if (c == 10)
- break;
- else if (c != 13) /* ignore CR */
- m_linebuf += putf8string(1, c);
- if (!this->readcode(c))
- break;
- }
- line = m_linebuf.c_str();
- return true;
-}
-
-
-void putf8_fmt_writer::vdowrite(const pstring &ls) const
-{
- write(ls);
-}
-
-
-
-} // namespace plib
diff --git a/src/lib/netlist/plib/pstream.h b/src/lib/netlist/plib/pstream.h
index ca21bca795a..72a2f242106 100644
--- a/src/lib/netlist/plib/pstream.h
+++ b/src/lib/netlist/plib/pstream.h
@@ -1,108 +1,208 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pstream.h
- */
+
#ifndef PSTREAM_H_
#define PSTREAM_H_
+///
+/// \file pstream.h
+///
-#include "palloc.h"
#include "pconfig.h"
-#include "pexception.h"
#include "pfmtlog.h"
+#include "pgsl.h"
#include "pstring.h"
#include <array>
-#include <type_traits>
-#include <vector>
-#include <ios>
#include <fstream>
+#include <ios>
#include <iostream>
+#include <memory>
#include <sstream>
+#include <type_traits>
+#include <vector>
namespace plib {
-// -----------------------------------------------------------------------------
-// putf8reader_t: reader on top of istream
-// -----------------------------------------------------------------------------
+ /// \brief wrapper around istream read
+ ///
+ template <typename S, typename T>
+ static S & istream_read(S &is, T * data, size_t len)
+ {
+ using ct = typename S::char_type;
+ static_assert((sizeof(T) % sizeof(ct)) == 0, "istream_read sizeof issue");
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return is.read(reinterpret_cast<ct *>(data), gsl::narrow<std::streamsize>(len * sizeof(T)));
+ }
-/* this digests linux & dos/windows text files */
+ /// \brief wrapper around ostream write
+ ///
+ template <typename S, typename T>
+ static S & ostream_write(S &os, const T * data, size_t len)
+ {
+ using ct = typename S::char_type;
+ static_assert((sizeof(T) % sizeof(ct)) == 0, "ostream_write sizeof issue");
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return os.write(reinterpret_cast<const ct *>(data), gsl::narrow<std::streamsize>(len * sizeof(T)));
+ }
+ /// \brief a named istream pointer container
+ ///
+ /// This moveable object allows to pass istream unique pointers with
+ /// information about the origin (filename). This is useful in error
+ /// reporting where the source of the stream has to be logged.
+ ///
+ struct istream_uptr
+ {
+ explicit istream_uptr() = default;
-template <typename T>
-struct constructor_helper
-{
- plib::unique_ptr<std::istream> operator()(T &&s) { return std::move(plib::make_unique<T>(std::move(s))); }
-};
+ istream_uptr(std::unique_ptr<std::istream> &&strm, const pstring &filename)
+ : m_strm(std::move(strm))
+ , m_filename(filename)
+ {
+ }
+ istream_uptr(const istream_uptr &) = delete;
+ istream_uptr &operator=(const istream_uptr &) = delete;
+ istream_uptr(istream_uptr &&rhs) noexcept
+ : m_strm(std::move(rhs.m_strm))
+ , m_filename(std::move(rhs.m_filename))
+ {
+ }
+ istream_uptr &operator=(istream_uptr &&) = delete;
+
+ ~istream_uptr() = default;
+ std::istream * operator ->() noexcept { return m_strm.get(); }
+ std::istream & operator *() noexcept { return *m_strm; }
+ pstring filename() { return m_filename; }
+
+ bool empty() { return m_strm == nullptr; }
+
+ // FIXME: workaround input context should accept stream_ptr
+
+ std::unique_ptr<std::istream> release_stream() { return std::move(m_strm); }
+ private:
+ std::unique_ptr<std::istream> m_strm;
+ pstring m_filename;
+ };
+
+///
+/// \brief digests linux & dos/windows text files
+///
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
class putf8_reader
{
public:
- COPYASSIGN(putf8_reader, delete)
- putf8_reader &operator=(putf8_reader &&src) = delete;
+ PCOPYASSIGN(putf8_reader, delete)
virtual ~putf8_reader() = default;
- template <typename T>
- friend struct constructor_helper;
+ putf8_reader(putf8_reader &&rhs) noexcept
+ : m_strm(std::move(rhs.m_strm))
+ {
+ }
- template <typename T>
- putf8_reader(T &&strm) // NOLINT(cppcoreguidelines-special-member-functions, misc-forwarding-reference-overload, bugprone-forwarding-reference-overload)
- : m_strm(std::move(constructor_helper<T>()(std::move(strm)))) // NOLINT(bugprone-move-forwarding-reference)
- {}
+ putf8_reader(std::unique_ptr<std::istream> &&rhs) noexcept
+ : m_strm(std::move(rhs))
+ {
+ // no bad surprises
+ m_strm->imbue(std::locale::classic());
+ }
bool eof() const { return m_strm->eof(); }
- bool readline(pstring &line);
- bool readbyte1(std::istream::char_type &b)
+ /// \brief Read a line of UTF8 characters from the stream.
+ ///
+ /// The line will not contain a trailing linefeed
+ ///
+ /// \param line pstring reference to the result
+ /// \returns Returns false if at end of file
+ ///
+ bool read_line(putf8string &line)
+ {
+ putf8string::code_t c = 0;
+ line = "";
+ if (!this->read_code(c))
+ {
+ return false;
+ }
+ while (true)
+ {
+ if (c == 10)
+ break;
+ if (c != 13) // ignore CR
+ line += putf8string(1, c);
+ if (!this->read_code(c))
+ break;
+ }
+ return true;
+ }
+
+ /// \brief Read a line of UTF8 characters from the stream including trailing linefeed.
+ ///
+ /// The line will contain the trailing linefeed
+ ///
+ /// \param line pstring reference to the result
+ /// \returns Returns false if at end of file
+ ///
+ bool read_line_lf(putf8string &line)
+ {
+ putf8string::code_t c = 0;
+ line = "";
+ if (!this->read_code(c))
+ {
+ return false;
+ }
+ while (true)
+ {
+ if (c != 13) // ignore CR
+ line += putf8string(1, c);
+ if (c == 10)
+ break;
+ if (!this->read_code(c))
+ break;
+ }
+ return true;
+ }
+
+ bool readbyte(std::istream::char_type &b)
{
if (m_strm->eof())
return false;
m_strm->read(&b, 1);
- return true;
+ return (!m_strm->eof());
}
- bool readcode(putf8string::traits_type::code_t &c)
+
+ bool read_code(putf8string::traits_type::code_t &c)
{
std::array<std::istream::char_type, 4> b{0};
if (m_strm->eof())
return false;
m_strm->read(&b[0], 1);
+ if (m_strm->eof())
+ return false;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
const std::size_t l = putf8string::traits_type::codelen(reinterpret_cast<putf8string::traits_type::mem_t *>(&b));
for (std::size_t i = 1; i < l; i++)
{
+ m_strm->read(&b[i], 1);
if (m_strm->eof())
return false;
- m_strm->read(&b[i], 1);
}
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
c = putf8string::traits_type::code(reinterpret_cast<putf8string::traits_type::mem_t *>(&b));
return true;
}
std::istream &stream() { return *m_strm; }
private:
- plib::unique_ptr<std::istream> m_strm;
- putf8string m_linebuf;
-};
-
-template <>
-struct constructor_helper<putf8_reader>
-{
- plib::unique_ptr<std::istream> operator()(putf8_reader &&s) { return std::move(s.m_strm); }
-};
-
-template <>
-struct constructor_helper<plib::unique_ptr<std::istream>>
-{
- plib::unique_ptr<std::istream> operator()(plib::unique_ptr<std::istream> &&s) { return std::move(s); }
+ std::unique_ptr<std::istream> m_strm;
};
-
-// -----------------------------------------------------------------------------
-// putf8writer_t: writer on top of ostream
-// -----------------------------------------------------------------------------
+///
+/// \brief writer on top of ostream
+///
class putf8_writer
{
@@ -111,12 +211,12 @@ public:
putf8_writer(putf8_writer &&src) noexcept : m_strm(src.m_strm) {}
- COPYASSIGN(putf8_writer, delete)
+ PCOPYASSIGN(putf8_writer, delete)
putf8_writer &operator=(putf8_writer &&src) = delete;
virtual ~putf8_writer() = default;
- void writeline(const pstring &line) const
+ void write_line(const pstring &line) const
{
write(line);
write(10);
@@ -126,15 +226,17 @@ public:
{
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
const putf8string conv_utf8(text);
- m_strm->write(conv_utf8.c_str(), static_cast<std::streamsize>(plib::strlen(conv_utf8.c_str())));
+ //m_strm->write(conv_utf8.c_str(), static_cast<std::streamsize>(plib::strlen(conv_utf8.c_str() )));
+ ostream_write(*m_strm, conv_utf8.c_str(), conv_utf8.size());
}
void write(const pstring::value_type c) const
{
- pstring t = pstring("") + c;
+ pstring t(1,c);
write(t);
}
+ void flush() { m_strm->flush(); }
private:
std::ostream *m_strm;
};
@@ -144,24 +246,27 @@ class putf8_fmt_writer : public pfmt_writer_t<putf8_fmt_writer>, public putf8_wr
public:
explicit putf8_fmt_writer(std::ostream *strm)
- : pfmt_writer_t()
- , putf8_writer(strm)
+ : putf8_writer(strm)
{
}
- COPYASSIGNMOVE(putf8_fmt_writer, delete)
+ PCOPYASSIGNMOVE(putf8_fmt_writer, delete)
~putf8_fmt_writer() override = default;
//protected:
- void vdowrite(const pstring &ls) const;
+ void upstream_write(const pstring &s) const
+ {
+ write(s);
+ }
+
private:
};
-// -----------------------------------------------------------------------------
-// pbinary_writer_t: writer on top of ostream
-// -----------------------------------------------------------------------------
+///
+/// \brief writer on top of ostream
+///
class pbinary_writer
{
@@ -169,7 +274,7 @@ public:
explicit pbinary_writer(std::ostream &strm) : m_strm(strm) {}
pbinary_writer(pbinary_writer &&src) noexcept : m_strm(src.m_strm) {}
- COPYASSIGN(pbinary_writer, delete)
+ PCOPYASSIGN(pbinary_writer, delete)
pbinary_writer &operator=(pbinary_writer &&src) = delete;
virtual ~pbinary_writer() = default;
@@ -177,23 +282,24 @@ public:
template <typename T>
void write(const T &val)
{
- m_strm.write(reinterpret_cast<const std::ostream::char_type *>(&val), sizeof(T));
+ ostream_write(m_strm, &val, 1);
}
void write(const pstring &s)
{
- const auto sm = reinterpret_cast<const std::ostream::char_type *>(s.c_str());
- const std::streamsize sl(static_cast<std::streamsize>(pstring_mem_t_size(s)));
+ const auto *sm = s.c_str();
+ //const auto sl(std::char_traits<pstring::mem_t>::length(sm));
+ const auto sl(s.size());
write(sl);
- m_strm.write(sm, sl);
+ ostream_write(m_strm, sm, sl);
}
template <typename T>
void write(const std::vector<T> &val)
{
- const std::streamsize sz(static_cast<std::streamsize>(val.size()));
+ const typename std::vector<T>::size_type sz(val.size());
write(sz);
- m_strm.write(reinterpret_cast<const std::ostream::char_type *>(val.data()), sz * static_cast<std::streamsize>(sizeof(T)));
+ ostream_write(m_strm, val.data(), sz);
}
private:
@@ -206,7 +312,7 @@ public:
explicit pbinary_reader(std::istream &strm) : m_strm(strm) {}
pbinary_reader(pbinary_reader &&src) noexcept : m_strm(src.m_strm) { }
- COPYASSIGN(pbinary_reader, delete)
+ PCOPYASSIGN(pbinary_reader, delete)
pbinary_reader &operator=(pbinary_reader &&src) = delete;
virtual ~pbinary_reader() = default;
@@ -214,14 +320,14 @@ public:
template <typename T>
void read(T &val)
{
- m_strm.read(reinterpret_cast<std::istream::char_type *>(&val), sizeof(T));
+ istream_read(m_strm, &val, 1);
}
void read( pstring &s)
{
std::size_t sz = 0;
read(sz);
- std::vector<plib::string_info<pstring>::mem_t> buf(sz+1);
+ std::vector<plib::string_info<putf8string>::mem_t> buf(sz+1);
m_strm.read(buf.data(), static_cast<std::streamsize>(sz));
buf[sz] = 0;
s = pstring(buf.data());
@@ -233,14 +339,14 @@ public:
std::size_t sz = 0;
read(sz);
val.resize(sz);
- m_strm.read(reinterpret_cast<std::istream::char_type *>(val.data()), static_cast<std::streamsize>(sizeof(T) * sz));
+ istream_read(m_strm, val.data(), sz);
}
private:
std::istream &m_strm;
};
-inline void copystream(std::ostream &dest, std::istream &src)
+inline void copy_stream(std::ostream &dest, std::istream &src)
{
// FIXME: optimize
std::array<std::ostream::char_type, 1024> buf; // NOLINT(cppcoreguidelines-pro-type-member-init)
@@ -251,12 +357,71 @@ inline void copystream(std::ostream &dest, std::istream &src)
}
}
+///
+/// \brief utf8 filename aware ifstream wrapper
+///
+class ifstream : public std::ifstream
+{
+public:
+
+ using filename_type = std::conditional<compile_info::win32() && (!compile_info::mingw() || compile_info::version::vmajor()>=9),
+ pstring_t<pwchar_traits>, pstring_t<putf8_traits>>::type;
+
+ template <typename T>
+ explicit ifstream(const pstring_t<T> &name, ios_base::openmode mode = ios_base::in)
+ : std::ifstream(filename_type(name).c_str(), mode)
+ {
+ }
+
+ explicit ifstream(const std::string &name, ios_base::openmode mode = ios_base::in)
+ : std::ifstream(filename_type(putf8string(name)).c_str(), mode)
+ {
+ }
+};
+
+///
+/// \brief utf8 filename aware ofstream wrapper
+///
+class ofstream : public std::ofstream
+{
+public:
+ using filename_type = std::conditional<compile_info::win32() && (!compile_info::mingw() || compile_info::version::vmajor()>=9),
+ pstring_t<pwchar_traits>, pstring_t<putf8_traits>>::type;
+
+ ofstream() : std::ofstream() {}
+
+ template <typename T>
+ explicit ofstream(const pstring_t<T> &name, ios_base::openmode mode = ios_base::out | ios_base::trunc)
+ : std::ofstream(filename_type(name).c_str(), mode)
+ {
+ }
+
+ explicit ofstream(const std::string &name, ios_base::openmode mode = ios_base::out | ios_base::trunc)
+ : std::ofstream(filename_type(putf8string(name)).c_str(), mode)
+ {
+ }
+
+ template <typename T>
+ void open(const pstring_t<T> &name, ios_base::openmode mode = ios_base::out | ios_base::trunc)
+ {
+ std::ofstream::open(filename_type(name).c_str(), mode);
+ }
+
+ template <typename T>
+ void open(const std::string &name, ios_base::openmode mode = ios_base::out | ios_base::trunc)
+ {
+ std::ofstream::open(filename_type(putf8string(name)).c_str(), mode);
+ }
+
+};
+
+
struct perrlogger
{
template <typename ... Args>
explicit perrlogger(Args&& ... args)
{
- h()(std::forward<Args>(args)...);
+ h()(std::forward<Args>(args)...); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
}
private:
static putf8_fmt_writer &h()
@@ -272,14 +437,17 @@ private:
namespace filesystem
{
+
+ // FIXME: u8path should return a path object (c++17)
+
template< class Source >
- pstring /*path */ u8path( const Source& source )
+ pstring u8path( const Source& source )
{
- return pstring(source);
+ return source;
}
-}
+} // namespace filesystem
} // namespace plib
-#endif /* PSTREAM_H_ */
+#endif // PSTREAM_H_
diff --git a/src/lib/netlist/plib/pstring.cpp b/src/lib/netlist/plib/pstring.cpp
index c304f11d1fd..7d5d5a7dbc7 100644
--- a/src/lib/netlist/plib/pstring.cpp
+++ b/src/lib/netlist/plib/pstring.cpp
@@ -1,113 +1,89 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nl_string.c
- *
- */
#include "pstring.h"
#include "palloc.h"
-#include "plists.h"
#include <algorithm>
#include <atomic>
#include <stack>
template<typename F>
-int pstring_t<F>::compare(const pstring_t &right) const
+int pstring_t<F>::compare(const pstring_t &right) const noexcept
{
- if (mem_t_size() == 0 && right.mem_t_size() == 0)
- return 0;
- else if (right.mem_t_size() == 0)
- return 1;
- else if (mem_t_size() == 0)
- return -1;
-
+#if 0
+ return m_str.compare(right.m_str);
+#else
auto si = this->begin();
auto ri = right.begin();
- while (si != this->end() && ri != right.end() && *si == *ri)
+ const auto se = this->end();
+ const auto re = right.end();
+
+ while (si != se && ri != re && *si == *ri)
{
- ri++;
- si++;
+ ++ri;
+ ++si;
}
- if (si != this->end() && ri != right.end())
- return static_cast<int>(*si) - static_cast<int>(*ri);
- else if (this->mem_t_size() > right.mem_t_size())
+ if (si != se && ri != re)
+ return plib::narrow_cast<int>(*si) - plib::narrow_cast<int>(*ri);
+ if (si != se)
return 1;
- else if (this->mem_t_size() < right.mem_t_size())
+ if (ri != re)
return -1;
return 0;
+#endif
}
template<typename F>
pstring_t<F> pstring_t<F>::substr(size_type start, size_type nlen) const
{
pstring_t ret;
+ auto ps = begin();
+ while (ps != end() && start > 0)
+ {
+ ++ps;
+ --start;
+ }
//FIXME: throw ?
- const size_type l = length();
- if (start < l)
+ if (ps != end())
{
- if (nlen == npos || start + nlen > l)
- nlen = l - start;
- auto ps = std::next(begin(), static_cast<difference_type>(start));
- auto pe = std::next(ps, static_cast<difference_type>(nlen));
+ auto pe = ps;
+ while (pe != end() && nlen > 0)
+ {
+ ++pe;
+ --nlen;
+ }
ret.m_str.assign(ps.p, pe.p);
}
return ret;
}
template<typename F>
-typename pstring_t<F>::size_type pstring_t<F>::find_first_not_of(const pstring_t &no) const
+pstring_t<F> pstring_t<F>::substr(size_type start) const
{
- size_type pos = 0;
- for (auto it = begin(); it != end(); ++it, ++pos)
+ pstring_t ret;
+ auto ps = begin();
+ while (ps != end() && start > 0)
{
- bool f = true;
- for (code_t const jt : no)
- {
- if (*it == jt)
- {
- f = false;
- break;
- }
- }
- if (f)
- return pos;
+ ++ps;
+ --start;
}
- return npos;
-}
-
-template<typename F>
-typename pstring_t<F>::size_type pstring_t<F>::find_last_not_of(const pstring_t &no) const
-{
- /* FIXME: reverse iterator */
- size_type last_found = npos;
- size_type pos = 0;
- for (auto it = begin(); it != end(); ++it, ++pos)
+ //FIXME: throw ?
+ if (ps != end())
{
- bool f = true;
- for (code_t const jt : no)
- {
- if (*it == jt)
- {
- f = false;
- break;
- }
- }
- if (f)
- last_found = pos;
+ ret.m_str.assign(ps.p, end().p);
}
- return last_found;
+ return ret;
}
template<typename F>
-typename pstring_t<F>::size_type pstring_t<F>::find(const pstring_t &search, size_type start) const
+typename pstring_t<F>::size_type pstring_t<F>::find(const pstring_t &search, size_type start) const noexcept
{
auto istart = std::next(begin(), static_cast<difference_type>(start));
for (; istart != end(); ++istart)
{
- auto itc(istart);
+ auto itc = istart;
auto cmp = search.begin();
while (itc != end() && cmp != search.end() && *itc == *cmp)
{
@@ -122,11 +98,16 @@ typename pstring_t<F>::size_type pstring_t<F>::find(const pstring_t &search, siz
}
template<typename F>
-typename pstring_t<F>::size_type pstring_t<F>::find(code_t search, size_type start) const
+typename pstring_t<F>::size_type pstring_t<F>::find(code_t search, size_type start) const noexcept
{
- pstring_t ss;
- traits_type::encode(search, ss.m_str);
- return find(ss, start);
+ auto i = std::next(begin(), static_cast<difference_type>(start));
+ for (; i != end(); ++i)
+ {
+ if (*i == search)
+ return start;
+ ++start;
+ }
+ return npos;
}
// ----------------------------------------------------------------------------------------
@@ -136,4 +117,5 @@ typename pstring_t<F>::size_type pstring_t<F>::find(code_t search, size_type sta
template struct pstring_t<pu8_traits>;
template struct pstring_t<putf8_traits>;
template struct pstring_t<putf16_traits>;
+template struct pstring_t<putf32_traits>;
template struct pstring_t<pwchar_traits>;
diff --git a/src/lib/netlist/plib/pstring.h b/src/lib/netlist/plib/pstring.h
index 96ab0bd0fa5..33ef2b083cf 100644
--- a/src/lib/netlist/plib/pstring.h
+++ b/src/lib/netlist/plib/pstring.h
@@ -1,17 +1,19 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * pstring.h
- */
#ifndef PSTRING_H_
#define PSTRING_H_
+///
+/// \file pstring.h
+///
+
#include "ptypes.h"
#include <exception>
#include <iterator>
#include <limits>
+#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
@@ -51,8 +53,8 @@ public:
constexpr bool operator==(const pstring_const_iterator& rhs) const noexcept { return p == rhs.p; }
constexpr bool operator!=(const pstring_const_iterator& rhs) const noexcept { return p != rhs.p; }
- reference operator*() const noexcept { return *reinterpret_cast<pointer>(&(*p)); }
- pointer operator->() const noexcept { return reinterpret_cast<pointer>(&(*p)); }
+ reference operator*() const noexcept { return *reinterpret_cast<pointer>(&(*p)); } // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ pointer operator->() const noexcept { return reinterpret_cast<pointer>(&(*p)); } // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
private:
template <typename G> friend struct pstring_t;
@@ -72,6 +74,9 @@ public:
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using string_type = typename traits_type::string_type;
+ // no non-const const_iterator for now
+ using iterator = pstring_const_iterator<pstring_t<F> >;
+ using const_iterator = pstring_const_iterator<pstring_t<F> >;
// FIXME: this is ugly
struct ref_value_type final
@@ -94,32 +99,45 @@ public:
pstring_t() = default;
~pstring_t() noexcept = default;
- // FIXME: Do something with encoding
- pstring_t(const mem_t *string)
- : m_str(string)
+ pstring_t(const mem_t *string, const size_type len)
+ : m_str(string, len)
{
}
- pstring_t(const mem_t *string, const size_type len)
- : m_str(string, len)
+ // mingw treats string constants as char* instead of char[N]
+ template<typename C,
+ class = std::enable_if_t<std::is_same<C, const mem_t>::value>>
+ pstring_t(const C *string)
+ : m_str(string)
{
}
template<typename C, std::size_t N,
- class = typename std::enable_if<std::is_same<C, const mem_t>::value>::type>
- pstring_t(C (&string)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
+ class = std::enable_if_t<std::is_same<C, const mem_t>::value>>
+ pstring_t(C (&string)[N]) noexcept(false) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
{
static_assert(N > 0,"pstring from array of length 0");
+ // need std::exception since pexception depends on pstring
if (string[N-1] != 0)
throw std::exception();
m_str.assign(string, N - 1);
}
+ // interpret other string as putf8strings
+ template <typename C,
+ class = std::enable_if_t<std::is_same<std::remove_const_t<C>, char>::value
+ && !std::is_same<std::remove_const_t<C>, mem_t>::value>>
+ pstring_t(C *string);
explicit pstring_t(const string_type &string)
: m_str(string)
{ }
+ pstring_t(iterator first, iterator last)
+ {
+ m_str.assign(first.p, last.p);
+ }
+
pstring_t(const pstring_t &string) = default;
pstring_t(pstring_t &&string) noexcept = default;
pstring_t &operator=(const pstring_t &string) = default;
@@ -127,12 +145,12 @@ public:
explicit pstring_t(size_type n, code_t code)
{
- while (n--)
+ while (n-- != 0)
*this += code;
}
template <typename T,
- class = typename std::enable_if<!std::is_same<T, pstring_t::traits_type>::value>::type>
+ class = std::enable_if_t<!std::is_same<T, pstring_t::traits_type>::value>>
explicit pstring_t(const pstring_t<T> &string)
{
m_str.clear();
@@ -142,9 +160,8 @@ public:
operator string_type () const { return m_str; }
-
template <typename T,
- class = typename std::enable_if<!std::is_same<T, pstring_t::traits_type>::value>::type>
+ class = std::enable_if_t<!std::is_same<T, pstring_t::traits_type>::value>>
pstring_t &operator=(const pstring_t<T> &string)
{
m_str.clear();
@@ -153,66 +170,72 @@ public:
return *this;
}
- // no non-const const_iterator for now
- using iterator = pstring_const_iterator<pstring_t<F> >;
- using const_iterator = pstring_const_iterator<pstring_t<F> >;
-
- iterator begin() { return iterator(m_str.begin()); }
- iterator end() { return iterator(m_str.end()); }
- const_iterator begin() const { return const_iterator(m_str.begin()); }
- const_iterator end() const { return const_iterator(m_str.end()); }
- const_iterator cbegin() const { return const_iterator(m_str.begin()); }
- const_iterator cend() const { return const_iterator(m_str.end()); }
+ iterator begin() noexcept { return iterator(m_str.begin()); }
+ iterator end() noexcept { return iterator(m_str.end()); }
+ const_iterator begin() const noexcept { return const_iterator(m_str.begin()); }
+ const_iterator end() const noexcept { return const_iterator(m_str.end()); }
+ const_iterator cbegin() const noexcept { return const_iterator(m_str.begin()); }
+ const_iterator cend() const noexcept { return const_iterator(m_str.end()); }
// C string conversion helpers
- const mem_t *c_str() const { return static_cast<const mem_t *>(m_str.c_str()); }
- const mem_t *data() const { return c_str(); }
+ const mem_t *c_str() const noexcept { return static_cast<const mem_t *>(m_str.c_str()); }
+ const mem_t *data() const noexcept { return c_str(); }
+
+ /// \brief return number of codes in the string
+ ///
+ /// This may report a number less than what \ref size reports. pstrings
+ /// operate on character codes. In the case of utf pstrings thus the physical size
+ /// may be bigger than the logical size.
+ size_type length() const noexcept { return traits_type::len(m_str); }
- size_type length() const { return traits_type::len(m_str); }
- size_type size() const { return traits_type::len(m_str); }
- bool empty() const { return m_str.size() == 0; }
+ /// \brief return number of memory units in the string
+ ///
+ /// This function returns the number of memory units used by a string.
+ /// Depending on the string type the size may be reported as bytes, words
+ /// or quad-words.
+ size_type size() const noexcept { return m_str.size(); }
- pstring_t substr(size_type start, size_type nlen = npos) const;
- int compare(const pstring_t &right) const;
+ bool empty() const noexcept { return m_str.empty(); }
+ void clear() noexcept { m_str.clear(); }
- size_type find(const pstring_t &search, size_type start = 0) const;
- size_type find(code_t search, size_type start = 0) const;
+ pstring_t substr(size_type start, size_type nlen) const;
+ pstring_t substr(size_type start) const;
+ int compare(const pstring_t &right) const noexcept;
- size_type find_first_not_of(const pstring_t &no) const;
- size_type find_last_not_of(const pstring_t &no) const;
+ size_type find(const pstring_t &search, size_type start = 0) const noexcept;
+ size_type find(code_t search, size_type start = 0) const noexcept;
// concatenation operators
pstring_t& operator+=(const pstring_t &string) { m_str.append(string.m_str); return *this; }
pstring_t& operator+=(const code_t c) { traits_type::encode(c, m_str); return *this; }
friend pstring_t operator+(const pstring_t &lhs, const pstring_t &rhs) { return pstring_t(lhs) += rhs; }
- friend pstring_t operator+(const pstring_t &lhs, const code_t rhs) { return pstring_t(lhs) += rhs; }
- friend pstring_t operator+(const code_t lhs, const pstring_t &rhs) { return pstring_t(1, lhs) += rhs; }
+ friend pstring_t operator+(const pstring_t &lhs, code_t rhs) { return pstring_t(lhs) += rhs; }
+ friend pstring_t operator+(code_t lhs, const pstring_t &rhs) { return pstring_t(1, lhs) += rhs; }
// comparison operators
- bool operator==(const pstring_t &string) const { return (compare(string) == 0); }
- bool operator!=(const pstring_t &string) const { return (compare(string) != 0); }
+ bool operator==(const pstring_t &string) const noexcept { return m_str == string.m_str; }
+ bool operator!=(const pstring_t &string) const noexcept { return m_str != string.m_str; }
- bool operator<(const pstring_t &string) const { return (compare(string) < 0); }
- bool operator<=(const pstring_t &string) const { return (compare(string) <= 0); }
- bool operator>(const pstring_t &string) const { return (compare(string) > 0); }
- bool operator>=(const pstring_t &string) const { return (compare(string) >= 0); }
+ bool operator<(const pstring_t &string) const noexcept { return (compare(string) < 0); }
+ bool operator<=(const pstring_t &string) const noexcept { return (compare(string) <= 0); }
+ bool operator>(const pstring_t &string) const noexcept { return (compare(string) > 0); }
+ bool operator>=(const pstring_t &string) const noexcept { return (compare(string) >= 0); }
- friend std::ostream& operator<<(std::ostream &ostrm, const pstring_t &str)
+ friend auto operator<<(std::basic_ostream<typename string_type::value_type> &ostrm, const pstring_t &str) -> std::basic_ostream<typename string_type::value_type> &
{
ostrm << str.m_str;
return ostrm;
}
- const_reference at(const size_type pos) const { return *reinterpret_cast<const ref_value_type *>(F::nthcode(m_str.c_str(),pos)); }
+ const_reference at(const size_type pos) const { return *reinterpret_cast<const ref_value_type *>(F::nthcode(m_str.c_str(),pos)); } // NOLINT(cppcoreguidelines-pro-type-reinterpret
static constexpr const size_type npos = static_cast<size_type>(-1);
- /* the following are extensions to <string> */
-
+ // the following are extensions to <string>
// FIXME: remove those
- size_type mem_t_size() const { return m_str.size(); }
-
+ size_type mem_t_size() const noexcept { return m_str.size(); }
private:
+
string_type m_str;
};
@@ -221,112 +244,111 @@ struct pu8_traits
using mem_t = char;
using code_t = char;
using string_type = std::string;
- static std::size_t len(const string_type &p) { return p.size(); }
- static std::size_t codelen(const mem_t *p) { plib::unused_var(p); return 1; }
- static std::size_t codelen(const code_t c) { plib::unused_var(c); return 1; }
- static code_t code(const mem_t *p) { return *p; }
+ static std::size_t len(const string_type &p) noexcept { return p.size(); }
+ static std::size_t codelen(const mem_t *p) noexcept { plib::unused_var(p); return 1; }
+ static std::size_t codelen(code_t c) noexcept { plib::unused_var(c); return 1; }
+ static code_t code(const mem_t *p) noexcept { return *p; }
static void encode(const code_t c, string_type &s) { s += static_cast<mem_t>(c); }
- static const mem_t *nthcode(const mem_t *p, const std::size_t n) { return &(p[n]); }
+ static const mem_t *nthcode(const mem_t *p, std::size_t n) noexcept { return &(p[n]); }
};
-/* No checking, this may deliver invalid codes */
-struct putf8_traits
+// No checking, this may deliver invalid codes
+
+
+template <std::size_t N, typename CT>
+struct putf_traits
{
- using mem_t = char;
+};
+
+template<typename CT>
+struct putf_traits<1, CT>
+{
+ using mem_t = CT;
using code_t = char32_t;
- using string_type = std::string;
- static std::size_t len(const string_type &p)
+ using string_type = std::basic_string<CT>;
+ static std::size_t len(const string_type &p) noexcept
{
std::size_t ret = 0;
for (const auto &c : p)
{
- if (!((c & 0xC0) == 0x80))
- ret++;
+ ret += (!((c & 0xC0) == 0x80)); // NOLINT
}
return ret;
}
- static std::size_t codelen(const mem_t *p)
+
+ static constexpr std::size_t codelen(const mem_t *p) noexcept
{
- const auto p1 = reinterpret_cast<const unsigned char *>(p);
- if ((*p1 & 0x80) == 0x00)
- return 1;
- else if ((*p1 & 0xE0) == 0xC0)
- return 2;
- else if ((*p1 & 0xF0) == 0xE0)
- return 3;
- else if ((*p1 & 0xF8) == 0xF0)
- return 4;
- else
- {
- return 1; // not correct
- }
+ const auto *p1 = reinterpret_cast<const unsigned char *>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret
+ return ((*p1 & 0x80) == 0x00) ? 1 : // NOLINT
+ ((*p1 & 0xE0) == 0xC0) ? 2 : // NOLINT
+ ((*p1 & 0xF0) == 0xE0) ? 3 : // NOLINT
+ ((*p1 & 0xF8) == 0xF0) ? 4 : // NOLINT
+ 1; // Invalid UTF8 code - ignore
}
- static std::size_t codelen(const code_t c)
+
+ static constexpr std::size_t codelen(code_t c) noexcept
{
- if (c < 0x0080)
- return 1;
- else if (c < 0x800)
- return 2;
- else if (c < 0x10000)
- return 3;
- else /* U+10000 U+1FFFFF */
- return 4; /* no checks */
+ return (c < 0x00080) ? 1 : // NOLINT
+ (c < 0x00800) ? 2 : // NOLINT
+ (c < 0x10000) ? 3 : // NOLINT
+ 4; // U+10000 U+1FFFFF
}
- static code_t code(const mem_t *p)
+
+ static constexpr code_t code(const mem_t *p) noexcept
{
- const auto p1 = reinterpret_cast<const unsigned char *>(p);
- if ((*p1 & 0x80) == 0x00)
- return *p1;
- else if ((*p1 & 0xE0) == 0xC0)
- return static_cast<code_t>(((p1[0] & 0x3f) << 6) | (p1[1] & 0x3f));
- else if ((*p1 & 0xF0) == 0xE0)
- return static_cast<code_t>(((p1[0] & 0x1f) << 12) | ((p1[1] & 0x3f) << 6) | ((p1[2] & 0x3f) << 0));
- else if ((*p1 & 0xF8) == 0xF0)
- return static_cast<code_t>(((p1[0] & 0x0f) << 18) | ((p1[1] & 0x3f) << 12) | ((p1[2] & 0x3f) << 6) | ((p1[3] & 0x3f) << 0));
- else
- return *p1; // not correct
+ const auto *p1 = reinterpret_cast<const unsigned char *>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ return ((*p1 & 0x80) == 0x00) ? *p1 : // NOLINT
+ ((*p1 & 0xE0) == 0xC0) ? static_cast<code_t>(((p1[0] & 0x3f) << 6) | (p1[1] & 0x3f)) : // NOLINT
+ ((*p1 & 0xF0) == 0xE0) ? static_cast<code_t>(((p1[0] & 0x1f) << 12) | ((p1[1] & 0x3f) << 6) | ((p1[2] & 0x3f) << 0)) : // NOLINT
+ ((*p1 & 0xF8) == 0xF0) ? static_cast<code_t>(((p1[0] & 0x0f) << 18) | ((p1[1] & 0x3f) << 12) | ((p1[2] & 0x3f) << 6) | ((p1[3] & 0x3f) << 0)) : // NOLINT
+ 0xFFFD; // NOLINT: unicode-replacement character
}
+
static void encode(const code_t c, string_type &s)
{
- if (c < 0x0080)
+ if (c < 0x0080) // NOLINT
{
s += static_cast<mem_t>(c);
}
- else if (c < 0x800)
+ else if (c < 0x800) // NOLINT
{
- s += static_cast<mem_t>(0xC0 | (c >> 6));
- s += static_cast<mem_t>(0x80 | (c & 0x3f));
+ s += static_cast<mem_t>(0xC0 | (c >> 6)); // NOLINT
+ s += static_cast<mem_t>(0x80 | (c & 0x3f)); // NOLINT
}
- else if (c < 0x10000)
+ else if (c < 0x10000) // NOLINT
{
- s += static_cast<mem_t>(0xE0 | (c >> 12));
- s += static_cast<mem_t>(0x80 | ((c>>6) & 0x3f));
- s += static_cast<mem_t>(0x80 | (c & 0x3f));
+ s += static_cast<mem_t>(0xE0 | (c >> 12)); // NOLINT
+ s += static_cast<mem_t>(0x80 | ((c>>6) & 0x3f)); // NOLINT
+ s += static_cast<mem_t>(0x80 | (c & 0x3f)); // NOLINT
}
- else /* U+10000 U+1FFFFF */
+ else // U+10000 U+1FFFFF
{
- s += static_cast<mem_t>(0xF0 | (c >> 18));
- s += static_cast<mem_t>(0x80 | ((c>>12) & 0x3f));
- s += static_cast<mem_t>(0x80 | ((c>>6) & 0x3f));
- s += static_cast<mem_t>(0x80 | (c & 0x3f));
+ s += static_cast<mem_t>(0xF0 | (c >> 18)); // NOLINT
+ s += static_cast<mem_t>(0x80 | ((c>>12) & 0x3f)); // NOLINT
+ s += static_cast<mem_t>(0x80 | ((c>>6) & 0x3f)); // NOLINT
+ s += static_cast<mem_t>(0x80 | (c & 0x3f)); // NOLINT
}
}
- static const mem_t *nthcode(const mem_t *p, const std::size_t n)
+
+ static const mem_t *nthcode(const mem_t *p, std::size_t n) noexcept
{
const mem_t *p1 = p;
std::size_t i = n;
while (i-- > 0)
+ {
p1 += codelen(p1);
+ }
return p1;
}
};
-struct putf16_traits
+template<typename CT>
+struct putf_traits<2, CT>
{
- using mem_t = char16_t;
+ using mem_t = CT;
using code_t = char32_t;
- using string_type = std::u16string;
- static std::size_t len(const string_type &p)
+ using string_type = std::basic_string<CT>;
+ static std::size_t len(const string_type &p) noexcept
{
std::size_t ret = 0;
auto i = p.begin();
@@ -334,40 +356,39 @@ struct putf16_traits
{
// FIXME: check that size is equal
auto c = static_cast<uint16_t>(*i++);
- if (!((c & 0xd800) == 0xd800))
+ if (!((c & 0xd800) == 0xd800)) // NOLINT
+ {
ret++;
+ }
}
return ret;
}
- static std::size_t codelen(const mem_t *p)
+ static std::size_t codelen(const mem_t *p) noexcept
{
auto c = static_cast<uint16_t>(*p);
- return ((c & 0xd800) == 0xd800) ? 2 : 1;
+ return ((c & 0xd800) == 0xd800) ? 2 : 1; // NOLINT
}
- static std::size_t codelen(const code_t c)
+ static std::size_t codelen(code_t c) noexcept
{
- if (c < 0x10000)
- return 1;
- else /* U+10000 U+1FFFFF */
- return 2;
+ return (c < 0x10000) ? 1 : 2; // NOLINT: U+10000 U+1FFFFF
}
- static code_t code(const mem_t *p)
+ static code_t code(const mem_t *p) noexcept
{
auto c = static_cast<uint32_t>(*p++);
- if ((c & 0xd800) == 0xd800)
+ if ((c & 0xd800) == 0xd800) // NOLINT
{
- c = (c - 0xd800) << 10;
- c += static_cast<uint32_t>(*p) - 0xdc00 + 0x10000;
+ c = (c - 0xd800) << 10; // NOLINT
+ c += static_cast<uint32_t>(*p) - 0xdc00 + 0x10000; // NOLINT
}
return static_cast<code_t>(c);
}
- static void encode(code_t c, string_type &s)
+ static void encode(code_t c, string_type &s) noexcept
{
auto cu = static_cast<uint32_t>(c);
- if (c > 0xffff)
+ if (c > 0xffff) // NOLINT
{ //make a surrogate pair
- uint32_t t = ((cu - 0x10000) >> 10) + 0xd800;
- cu = (cu & 0x3ff) + 0xdc00;
+ uint32_t t = ((cu - 0x10000) >> 10) + 0xd800; // NOLINT
+ cu = (cu & 0x3ff) + 0xdc00; // NOLINT
s += static_cast<mem_t>(t);
s += static_cast<mem_t>(cu);
}
@@ -376,327 +397,131 @@ struct putf16_traits
s += static_cast<mem_t>(cu);
}
}
- static const mem_t *nthcode(const mem_t *p, const std::size_t n)
+ static const mem_t *nthcode(const mem_t *p, std::size_t n) noexcept
{
std::size_t i = n;
while (i-- > 0)
+ {
p += codelen(p);
+ }
return p;
}
};
-struct pwchar_traits
+template<typename CT>
+struct putf_traits<4, CT>
{
- using mem_t = wchar_t;
+ using mem_t = CT;
using code_t = char32_t;
- using string_type = std::wstring;
- static std::size_t len(const string_type &p)
+ using string_type = std::basic_string<CT>;
+ static std::size_t len(const string_type &p) noexcept
{
- if (sizeof(wchar_t) == 2)
- {
- std::size_t ret = 0;
- auto i = p.begin();
- while (i != p.end())
- {
- // FIXME: check that size is equal
- auto c = static_cast<uint32_t>(*i++);
- if (!((c & 0xd800) == 0xd800))
- ret++;
- }
- return ret;
- }
- else
- return p.size();
+ return p.size();
}
- static std::size_t codelen(const mem_t *p)
+ static std::size_t codelen(const mem_t *p) noexcept
{
- if (sizeof(wchar_t) == 2)
- {
- auto c = static_cast<uint16_t>(*p);
- return ((c & 0xd800) == 0xd800) ? 2 : 1;
- }
- else
- return 1;
+ plib::unused_var(p);
+ return 1;
}
- static std::size_t codelen(const code_t c)
+ static std::size_t codelen(code_t c) noexcept
{
- if (sizeof(wchar_t) == 2)
- return ((c & 0xd800) == 0xd800) ? 2 : 1;
- else
- return 1;
+ plib::unused_var(c);
+ return 1;
}
static code_t code(const mem_t *p)
{
- if (sizeof(wchar_t) == 2)
- {
- auto c = static_cast<uint32_t>(*p++);
- if ((c & 0xd800) == 0xd800)
- {
- c = (c - 0xd800) << 10;
- c += static_cast<uint32_t>(*p) - 0xdc00 + 0x10000;
- }
- return static_cast<code_t>(c);
- }
- else
- return static_cast<code_t>(*p);
+ return static_cast<code_t>(*p);
}
static void encode(code_t c, string_type &s)
{
- if (sizeof(wchar_t) == 2)
- {
- auto cu = static_cast<uint32_t>(c);
- if (c > 0xffff)
- { //make a surrogate pair
- uint32_t t = ((cu - 0x10000) >> 10) + 0xd800;
- cu = (cu & 0x3ff) + 0xdc00;
- s += static_cast<mem_t>(t);
- s += static_cast<mem_t>(cu);
- }
- else
- s += static_cast<mem_t>(cu);
- }
- else
- s += static_cast<wchar_t>(c);
+ s += static_cast<mem_t>(c);
}
- static const mem_t *nthcode(const mem_t *p, const std::size_t n)
+ static const mem_t *nthcode(const mem_t *p, std::size_t n) noexcept
{
- if (sizeof(wchar_t) == 2)
- {
- std::size_t i = n;
- while (i-- > 0)
- p += codelen(p);
- return p;
- }
- else
- return p + n;
+ return p + n;
}
};
+using putf8_traits = putf_traits<sizeof(char), char>;
+using putf16_traits = putf_traits<sizeof(char16_t), char16_t>;
+using putf32_traits = putf_traits<sizeof(char32_t), char32_t>;
+using pwchar_traits = putf_traits<sizeof(wchar_t), wchar_t>;
+
extern template struct pstring_t<pu8_traits>;
extern template struct pstring_t<putf8_traits>;
extern template struct pstring_t<putf16_traits>;
+extern template struct pstring_t<putf32_traits>;
extern template struct pstring_t<pwchar_traits>;
#if (PSTRING_USE_STD_STRING)
using pstring = std::string;
-static inline pstring::size_type pstring_mem_t_size(const pstring &s) { return s.size(); }
#else
using pstring = pstring_t<putf8_traits>;
-template <typename T>
-static inline pstring::size_type pstring_mem_t_size(const pstring_t<T> &s) { return s.mem_t_size(); }
#endif
+using pu8string = pstring_t<pu8_traits>;
using putf8string = pstring_t<putf8_traits>;
-using pu16string = pstring_t<putf16_traits>;
+using putf16string = pstring_t<putf16_traits>;
+using putf32string = pstring_t<putf32_traits>;
using pwstring = pstring_t<pwchar_traits>;
+// interpret other string as putf8strings
+template <typename F>
+template <typename C, class>
+pstring_t<F>::pstring_t(C *string)
+{
+ m_str.clear();
+ putf8string utf8(string);
+ for (const auto &c : utf8)
+ *this += c;
+}
+
namespace plib
{
template<class T>
struct string_info
{
- using mem_t = typename T::mem_t;
};
- template<>
- struct string_info<std::string>
- {
- using mem_t = char;
- };
-
- template<typename T>
- pstring to_string(const T &v)
- {
- return pstring(std::to_string(v));
- }
-
template<typename T>
- pwstring to_wstring(const T &v)
+ struct string_info<pstring_t<T>>
{
- return pwstring(std::to_wstring(v));
- }
-
-
- template<typename T>
- typename T::size_type find_first_not_of(const T &str, const T &no)
- {
- typename T::size_type pos = 0;
- for (auto it = str.begin(); it != str.end(); ++it, ++pos)
- {
- bool f = true;
- for (typename T::value_type const jt : no)
- {
- if (*it == jt)
- {
- f = false;
- break;
- }
- }
- if (f)
- return pos;
- }
- return T::npos;
- }
-
- template<typename T>
- typename T::size_type find_last_not_of(const T &str, const T &no)
- {
- /* FIXME: reverse iterator */
- typename T::size_type last_found = T::npos;
- typename T::size_type pos = 0;
- for (auto it = str.begin(); it != str.end(); ++it, ++pos)
- {
- bool f = true;
- for (typename T::value_type const jt : no)
- {
- if (*it == jt)
- {
- f = false;
- break;
- }
- }
- if (f)
- last_found = pos;
- }
- return last_found;
- }
-
- template<typename T>
- T ltrim(const T &str, const T &ws = T(" \t\n\r"))
- {
- auto f = find_first_not_of(str, ws);
- return (f == T::npos) ? T() : str.substr(f);
- }
-
- template<typename T>
- T rtrim(const T &str, const T &ws = T(" \t\n\r"))
- {
- auto f = find_last_not_of(str, ws);
- return (f == T::npos) ? T() : str.substr(0, f + 1);
- }
-
- template<typename T>
- T trim(const T &str, const T &ws = T(" \t\n\r"))
- {
- return rtrim(ltrim(str, ws), ws);
- }
-
- template<typename T>
- T left(const T &str, typename T::size_type len)
- {
- return str.substr(0, len);
- }
-
- template<typename T>
- T right(const T &str, typename T::size_type nlen)
- {
- return nlen >= str.length() ? str : str.substr(str.length() - nlen, nlen);
- }
-
- template<typename T>
- bool startsWith(const T &str, const T &arg)
- {
- return (arg == left(str, arg.length()));
- }
-
- template<typename T>
- bool endsWith(const T &str, const T &arg)
- {
- return (right(str, arg.length()) == arg);
- }
-
- template<typename T>
- bool startsWith(const T &str, const char *arg)
- {
- return startsWith(str, static_cast<pstring>(arg));
- }
-
- template<typename T>
- bool endsWith(const T &str, const char *arg)
- {
- return endsWith(str, static_cast<pstring>(arg));
- }
-
- template<typename T>
- std::size_t strlen(const T *str)
- {
- const T *p = str;
- while (*p)
- p++;
- return static_cast<std::size_t>(p - str);
- }
-
- template<typename T>
- T ucase(const T &str)
- {
- T ret;
- for (const auto &c : str)
- if (c >= 'a' && c <= 'z')
- ret += (c - 'a' + 'A');
- else
- ret += c;
- return ret;
- }
-
- template<typename T>
- T rpad(const T &str, const T &ws, const typename T::size_type cnt)
- {
- // FIXME: pstringbuffer ret(*this);
-
- T ret(str);
- typename T::size_type wsl = ws.length();
- for (auto i = ret.length(); i < cnt; i+=wsl)
- ret += ws;
- return ret;
- }
+ using mem_t = typename T::mem_t;
+#if 0
+ static std::size_t mem_size(const pstring_t<T> &s) { return s.mem_t_size(); }
+#endif
+ };
template<typename T>
- T replace_all(const T &str, const T &search, const T &replace)
- {
- T ret;
- const typename T::size_type slen = search.length();
-
- typename T::size_type last_s = 0;
- typename T::size_type s = str.find(search, last_s);
- while (s != T::npos)
- {
- ret += str.substr(last_s, s - last_s);
- ret += replace;
- last_s = s + slen;
- s = str.find(search, last_s);
- }
- ret += str.substr(last_s);
- return ret;
- }
-
- template<typename T, typename T1, typename T2>
- T replace_all(const T &str, const T1 &search, const T2 &replace)
+ struct string_info<std::basic_string<T>>
{
- return replace_all(str, static_cast<T>(search), static_cast<T>(replace));
- }
-
+ using mem_t = T;
+#if 0
+ static std::size_t mem_size(const std::basic_string<T> &s) { return s.size(); }
+#endif
+ };
} // namespace plib
// custom specialization of std::hash can be injected in namespace std
namespace std
{
-
- template<typename T> struct hash<pstring_t<T>>
+ template<typename T>
+ struct hash<pstring_t<T>>
{
using argument_type = pstring_t<T>;
using result_type = std::size_t;
result_type operator()(const argument_type & s) const
{
const typename argument_type::mem_t *string = s.c_str();
- result_type result = 5381;
+ result_type result = 5381; // NOLINT
for (typename argument_type::mem_t c = *string; c != 0; c = *string++)
- result = ((result << 5) + result ) ^ (result >> (32 - 5)) ^ static_cast<result_type>(c);
+ result = ((result << 5) + result ) ^ (result >> (32 - 5)) ^ static_cast<result_type>(c); // NOLINT
return result;
}
};
} // namespace std
-#endif /* PSTRING_H_ */
+#endif // PSTRING_H_
diff --git a/src/lib/netlist/plib/pstrutil.h b/src/lib/netlist/plib/pstrutil.h
new file mode 100644
index 00000000000..e88469a438c
--- /dev/null
+++ b/src/lib/netlist/plib/pstrutil.h
@@ -0,0 +1,358 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PSTRUTIL_H_
+#define PSTRUTIL_H_
+
+///
+/// \file pstrutil.h
+///
+
+#include "pgsl.h"
+#include "pstring.h"
+#include "ptypes.h"
+
+#include <algorithm>
+#include <exception>
+#include <iterator>
+#include <limits>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace plib
+{
+ template<typename T>
+ pstring to_string(const T &v)
+ {
+ return pstring(putf8string(std::to_string(v)));
+ }
+
+ template<typename T>
+ pwstring to_wstring(const T &v)
+ {
+ return pwstring(std::to_wstring(v));
+ }
+
+ template<typename T>
+ typename T::size_type find_first_not_of(const T &str, const T &no)
+ {
+ typename T::size_type pos = 0;
+ for (auto it = str.begin(); it != str.end(); ++it, ++pos)
+ {
+ bool f = true;
+ for (typename T::value_type const jt : no)
+ {
+ if (*it == jt)
+ {
+ f = false;
+ break;
+ }
+ }
+ if (f)
+ return pos;
+ }
+ return T::npos;
+ }
+
+ template<typename T>
+ typename T::size_type find_last_not_of(const T &str, const T &no)
+ {
+ // FIXME: use reverse iterator
+ typename T::size_type last_found = T::npos;
+ typename T::size_type pos = 0;
+ for (auto it = str.begin(); it != str.end(); ++it, ++pos)
+ {
+ bool f = true;
+ for (typename T::value_type const jt : no)
+ {
+ if (*it == jt)
+ {
+ f = false;
+ break;
+ }
+ }
+ if (f)
+ last_found = pos;
+ }
+ return last_found;
+ }
+
+ template<typename T>
+ typename T::size_type find_last_of(const T &str, const T &no)
+ {
+ typename T::size_type last_found = T::npos;
+ typename T::size_type pos = 0;
+ for (auto it = str.begin(); it != str.end(); ++it, ++pos)
+ {
+ bool f = false;
+ for (typename T::value_type const jt : no)
+ {
+ if (*it == jt)
+ {
+ f = true;
+ break;
+ }
+ }
+ if (f)
+ last_found = pos;
+ }
+ return last_found;
+ }
+
+ template<typename T>
+ T ltrim(const T &str, const T &ws = T(" \t\n\r"))
+ {
+ auto f = find_first_not_of(str, ws);
+ return (f == T::npos) ? T() : str.substr(f);
+ }
+
+ template<typename T>
+ T rtrim(const T &str, const T &ws = T(" \t\n\r"))
+ {
+ auto f = find_last_not_of(str, ws);
+ return (f == T::npos) ? T() : str.substr(0, f + 1);
+ }
+
+ template<typename T>
+ T trim(const T &str, const T &ws = T(" \t\n\r"))
+ {
+ return rtrim(ltrim(str, ws), ws);
+ }
+
+ template<typename T>
+ T left(const T &str, typename T::size_type len)
+ {
+ return str.substr(0, len);
+ }
+
+ template<typename T>
+ T right(const T &str, typename T::size_type nlen)
+ {
+ return nlen >= str.length() ? str : str.substr(str.length() - nlen, nlen);
+ }
+
+ template<typename T>
+ bool startsWith(const T &str, const T &arg)
+ {
+ return (arg == left(str, arg.length()));
+ }
+
+ template<typename T>
+ bool endsWith(const T &str, const T &arg)
+ {
+ return (right(str, arg.length()) == arg);
+ }
+
+ template<typename T, typename TA>
+ bool startsWith(const T &str, const TA &arg)
+ {
+ return startsWith(str, static_cast<T>(arg));
+ }
+
+ template<typename T, typename TA>
+ bool endsWith(const T &str, const TA &arg)
+ {
+ return endsWith(str, static_cast<T>(arg));
+ }
+
+ template<typename T>
+ std::size_t strlen(const T *str)
+ {
+ const T *p = str;
+ while (*p != 0)
+ p++;
+ return narrow_cast<std::size_t>(p - str);
+ }
+
+ template<typename T>
+ T ucase(const T &str)
+ {
+ T ret;
+ for (const auto &c : str)
+ if (c >= 'a' && c <= 'z')
+ ret += (c - 'a' + 'A');
+ else
+ ret += c;
+ return ret;
+ }
+
+ template<typename T>
+ T lcase(const T &str)
+ {
+ T ret;
+ for (const auto &c : str)
+ if (c >= 'A' && c <= 'Z')
+ ret += (c - 'A' + 'a');
+ else
+ ret += c;
+ return ret;
+ }
+
+ template<typename T>
+ T rpad(const T &str, const T &ws, const typename T::size_type cnt)
+ {
+ // FIXME: pstringbuffer ret(*this);
+
+ T ret(str);
+ typename T::size_type wsl = ws.length();
+ for (auto i = ret.length(); i < cnt; i+=wsl)
+ ret += ws;
+ return ret;
+ }
+
+ template<typename T>
+ T replace_all(const T &str, typename T::value_type search, typename T::value_type replace)
+ {
+ T ret;
+ for (auto &c : str)
+ ret += (c == search) ? replace : c;
+ return ret;
+ }
+
+ template<typename T>
+ T replace_all(const T &str, const T &search, const T &replace)
+ {
+ T ret;
+ const typename T::size_type slen = search.length();
+
+ typename T::size_type last_s = 0;
+ typename T::size_type s = str.find(search, last_s);
+ while (s != T::npos)
+ {
+ ret += str.substr(last_s, s - last_s);
+ ret += replace;
+ last_s = s + slen;
+ s = str.find(search, last_s);
+ }
+ ret += str.substr(last_s);
+ return ret;
+ }
+
+ template<typename T, typename T1, typename T2>
+ std::enable_if_t<!std::is_integral<T1>::value, T>
+ replace_all(const T &str, const T1 &search, const T2 &replace)
+ {
+ return replace_all(str, static_cast<T>(search), static_cast<T>(replace));
+ }
+
+ template <typename T>
+ std::vector<T> psplit(const T &str, const T &onstr, bool ignore_empty = false)
+ {
+ std::vector<T> ret;
+
+ auto p = str.begin();
+ auto pn = std::search(p, str.end(), onstr.begin(), onstr.end());
+ const auto ol = static_cast<typename T::difference_type>(onstr.length());
+
+ while (pn != str.end())
+ {
+ if (!ignore_empty || p != pn)
+ ret.emplace_back(p, pn);
+ p = std::next(pn, ol);
+ pn = std::search(p, str.end(), onstr.begin(), onstr.end());
+ }
+ if (p != str.end())
+ {
+ ret.emplace_back(p, str.end());
+ }
+ return ret;
+ }
+
+ template <typename T>
+ std::vector<T> psplit(const T &str, const typename T::value_type &onstr, bool ignore_empty = false)
+ {
+ std::vector<T> ret;
+
+ auto p = str.begin();
+ auto pn = std::find(p, str.end(), onstr);
+
+ while (pn != str.end())
+ {
+ if (!ignore_empty || p != pn)
+ ret.emplace_back(p, pn);
+ p = std::next(pn, 1);
+ pn = std::find(p, str.end(), onstr);
+ }
+ if (p != str.end())
+ {
+ ret.emplace_back(p, str.end());
+ }
+ return ret;
+ }
+
+ template <typename T>
+ std::vector<T> psplit_r(const T &stri,
+ const T &token,
+ const std::size_t max_split)
+ {
+ T str(stri);
+ std::vector<T> result;
+ std::size_t splits = 0;
+
+ while(!str.empty())
+ {
+ std::size_t index = str.rfind(token);
+ bool found = index!=T::npos;
+ if (found)
+ splits++;
+ if ((splits <= max_split || max_split == 0) && found)
+ {
+ result.push_back(str.substr(index+token.size()));
+ str = str.substr(0, index);
+ if (str.empty())
+ result.push_back(str);
+ }
+ else
+ {
+ result.push_back(str);
+ str.clear();
+ }
+ }
+ return result;
+ }
+
+ template <typename T>
+ std::vector<T> psplit(const T &str, const std::vector<T> &onstrl)
+ {
+ T col = "";
+ std::vector<T> ret;
+
+ auto i = str.begin();
+ while (i != str.end())
+ {
+ auto p = T::npos;
+ for (std::size_t j=0; j < onstrl.size(); j++)
+ {
+ if (std::equal(onstrl[j].begin(), onstrl[j].end(), i))
+ {
+ p = j;
+ break;
+ }
+ }
+ if (p != T::npos)
+ {
+ if (!col.empty())
+ ret.push_back(col);
+
+ col.clear();
+ ret.push_back(onstrl[p]);
+ i = std::next(i, narrow_cast<typename T::difference_type>(onstrl[p].length()));
+ }
+ else
+ {
+ typename T::value_type c = *i;
+ col += c;
+ i++;
+ }
+ }
+ if (!col.empty())
+ ret.push_back(col);
+
+ return ret;
+ }
+
+} // namespace plib
+
+#endif // PSTRUTIL_H_
diff --git a/src/lib/netlist/plib/ptests.h b/src/lib/netlist/plib/ptests.h
new file mode 100644
index 00000000000..4e65de7f978
--- /dev/null
+++ b/src/lib/netlist/plib/ptests.h
@@ -0,0 +1,313 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PTESTS_H_
+#define PTESTS_H_
+
+///
+/// \file ptests.h
+///
+/// google tests compatible (hopefully) test macros. This is work in progress!
+///
+
+#include <algorithm>
+#include <exception>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+
+#define PEXPECT_EQ(exp1, exp2) PINT_EXPECT(eq, exp1, exp2)
+#define PEXPECT_NE(exp1, exp2) PINT_EXPECT(ne, exp1, exp2)
+#define PEXPECT_GT(exp1, exp2) PINT_EXPECT(gt, exp1, exp2)
+#define PEXPECT_LT(exp1, exp2) PINT_EXPECT(lt, exp1, exp2)
+#define PEXPECT_GE(exp1, exp2) PINT_EXPECT(ge, exp1, exp2)
+#define PEXPECT_LE(exp1, exp2) PINT_EXPECT(le, exp1, exp2)
+
+#define PEXPECT_TRUE(exp1) PINT_EXPECT(eq, exp1, true)
+#define PEXPECT_FALSE(exp1) PINT_EXPECT(eq, exp1, false)
+
+#define PEXPECT_THROW(exp, excep) PINT_EXPECT_THROW(exp, excep)
+#define PEXPECT_NO_THROW(exp) PINT_EXPECT_NO_THROW(exp)
+
+#define PTEST(name, desc) PINT_TEST(name, desc)
+#define PTEST_F(name, desc) PINT_TEST_F(name, desc, name)
+#define PRUN_ALL_TESTS(loglevel) plib::testing::run_all_tests(loglevel)
+
+#define PINT_TEST(name, desc) PINT_TEST_F(name, desc, plib::testing::Test)
+
+#define PINT_TESTNAME(name, desc) name ## _ ## desc
+#define PINT_LOCATION() ::plib::testing::test_location(__FILE__, __LINE__)
+#define PINT_SET_LAST(loc) this->m_parameters->m_last_source = loc
+
+#define PINT_REGISTER(name, desc) \
+ extern const plib::testing::reg_entry<PINT_TESTNAME(name, desc)> PINT_TESTNAME(name, desc ## _reg); \
+ const plib::testing::reg_entry<PINT_TESTNAME(name, desc)> PINT_TESTNAME(name, desc ## _reg)(#name, #desc, PINT_LOCATION());
+
+#define PINT_TEST_F(name, desc, base) \
+ class PINT_TESTNAME(name, desc) : public base \
+ { public:\
+ void desc (); \
+ void run() override { desc (); } \
+ }; \
+ PINT_REGISTER(name, desc) \
+ void PINT_TESTNAME(name, desc) :: desc ()
+
+#define PINT_EXPECT(comp, exp1, exp2) \
+ if (true) \
+ { \
+ ::plib::testing::test_location source = PINT_LOCATION(); PINT_SET_LAST(source); \
+ m_parameters->m_num_tests++; \
+ if (!this->internal_assert(plib::testing::comp_ ## comp (), # exp1, # exp2, exp1, exp2)) \
+ this->test_error(source) << "test failed" << std::endl; \
+ } else do {} while (0)
+
+#define PINT_EXPECT_THROW(exp, excep) \
+ if (true) \
+ { \
+ ::plib::testing::test_location source = PINT_LOCATION(); PINT_SET_LAST(source); \
+ m_parameters->m_num_tests++; \
+ try { exp; this->test_error(source) << "no " #excep " exception thrown" << std::endl;} \
+ catch (excep &) { this->test_ok() << "got " #excep " for " # exp "" << std::endl;} \
+ catch (std::exception &ptest_e) { this->test_error(source) << "unexpected exception thrown: " << ptest_e.what() << std::endl; } \
+ catch (...) { this->test_error(source) << "unexpected exception thrown" << std::endl; } \
+ } else do {} while (0)
+
+#define PINT_EXPECT_NO_THROW(exp) \
+ if (true) \
+ { \
+ ::plib::testing::test_location source = PINT_LOCATION(); PINT_SET_LAST(source); \
+ m_parameters->m_num_tests++; \
+ try { exp; this->test_ok() << "got no exception for " # exp << std::endl;} \
+ catch (std::exception &test_exception) { this->test_error(source) << "unexpected exception thrown: " << test_exception.what() << std::endl; } \
+ catch (...) { this->test_error(source) << "unexpected exception thrown" << std::endl; } \
+ } else do {} while (0)
+
+namespace plib::testing
+{
+ enum class loglevel
+ {
+ INFO,
+ WARNING,
+ ERROR
+ };
+
+ using test_location = std::pair<const char *, std::size_t>;
+
+ struct test_parameters
+ {
+ loglevel m_loglevel = loglevel::INFO;
+ std::size_t m_num_errors = 0;
+ std::size_t m_num_tests = 0;
+ test_location m_last_source = {"", 0 };
+ };
+
+ static std::ostream &stream_error(std::ostream &os, test_location loc)
+ {
+ return os << loc.first << ":" << loc.second << ":1: error: ";
+ }
+
+ class Test
+ {
+ public:
+ Test() = default;
+ virtual ~Test() = default;
+
+ Test(const Test &) = delete;
+ Test &operator=(const Test &) = delete;
+ Test(Test &&) = delete;
+ Test &operator=(Test &&) = delete;
+
+ virtual void run() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+
+ void set_parameters(test_parameters *params)
+ {
+ m_parameters = params;
+ }
+ protected:
+ std::ostream & test_ok() { return output(loglevel::INFO) << "\tOK: "; }
+ std::ostream & test_fail() { return output(loglevel::WARNING) << "\tFAIL: "; }
+ std::ostream & test_error(const test_location & loc)
+ {
+ return stream_error(output(loglevel::ERROR), loc);
+ }
+
+ template <typename C, typename T1, typename T2>
+ bool internal_assert(C comp,
+ const char* exp1, const char* exp2,
+ const T1& val1, const T2& val2);
+ test_parameters *m_parameters = nullptr;
+ private:
+ std::ostream &output(loglevel ll)
+ {
+ if (ll == loglevel::ERROR)
+ m_parameters->m_num_errors++;
+ return (ll >= m_parameters->m_loglevel) ? std::cout : m_nulstream;
+ }
+ std::ostringstream m_nulstream;
+ };
+
+ template <typename C, typename T1, typename T2>
+ bool Test::internal_assert(C comp,
+ const char* exp1, const char* exp2,
+ const T1& val1, const T2& val2)
+ {
+ if (comp(val1, val2))
+ {
+ test_ok() << exp1 << " " << C::opstr() << " " << exp2 << std::endl;
+ return true;
+ }
+ test_fail() << exp1 << " " << C::opstr() << " " << exp2
+ << " <" << val1 << ">,<" << val2 << ">" << std::endl;
+ return false;
+ }
+
+ struct reg_entry_base
+ {
+ using list_t = std::vector<reg_entry_base *>;
+ reg_entry_base(const char *n, const char *d, test_location l)
+ : name(n), desc(d), location(l)
+ {
+ try
+ {
+ registry().push_back(this);
+ }
+ catch (...)
+ {
+ std::terminate();
+ }
+ }
+
+ reg_entry_base(const reg_entry_base &) = delete;
+ reg_entry_base &operator=(const reg_entry_base &) = delete;
+ reg_entry_base(reg_entry_base &&) = delete;
+ reg_entry_base &operator=(reg_entry_base &&) = delete;
+
+ virtual ~reg_entry_base() = default;
+
+ virtual Test *create() const { return nullptr; }
+
+ const char *name;
+ const char *desc;
+ test_location location;
+ public:
+ static list_t & registry()
+ {
+ static list_t prlist;
+ return prlist;
+ }
+ };
+
+ template <typename T>
+ struct reg_entry : public reg_entry_base
+ {
+ using reg_entry_base::reg_entry_base;
+
+ Test *create() const override { return new T(); } // NOLINT
+ };
+
+ template <typename L>
+ std::pair<bool, std::string> catch_exception(L lambda)
+ {
+ try {
+ lambda();
+ }
+ catch (std::exception &ptest_e)
+ {
+ return { true, ptest_e.what() };
+ }
+ catch (...)
+ {
+ return { true, "" };
+ }
+ return { false, "" };
+ }
+
+ static inline int run_all_tests(loglevel ll)
+ {
+ std::cout << "================================================" << std::endl;
+ std::cout << "Running " << reg_entry_base::registry().size() << " test groups" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ auto &list = reg_entry_base::registry();
+
+ std::sort(list.begin(), list.end(), [](reg_entry_base *a, reg_entry_base *b) {
+ return (a->name < b->name);
+ });
+
+ std::size_t total_errors(0);
+ std::size_t total_tests(0);
+
+ for (auto &e : list)
+ {
+ test_parameters params;
+ params.m_loglevel = ll;
+ params.m_last_source = e->location;
+
+ std::cout << e->name << "::" << e->desc << ":" << std::endl;
+ Test *t = nullptr;
+
+ std::pair<bool, std::string> r;
+ if ((r = catch_exception([&]{
+ t = e->create();
+ t->set_parameters(&params);
+ })).first)
+ {
+ stream_error(std::cout, e->location) << "unexpected exception thrown during instantiation" << (!r.second.empty() ? ": " + r.second : "") << std::endl;
+ total_errors++;
+ }
+ else if ((r = catch_exception([&]{ t->SetUp(); })).first)
+ {
+ stream_error(std::cout, e->location) << "unexpected exception thrown during Setup" << (!r.second.empty() ? ": " + r.second : "") << std::endl;
+ total_errors++;
+ }
+ else if ((r = catch_exception([&]{ t->run(); })).first)
+ {
+ stream_error(std::cout, params.m_last_source) << "unexpected exception thrown during run after this line" << (!r.second.empty() ? ": " + r.second : "") << std::endl;
+ total_errors++;
+ }
+ else if ((r = catch_exception([&]{ t->TearDown(); })).first)
+ {
+ stream_error(std::cout, e->location) << "unexpected exception thrown during Teardown" << (!r.second.empty() ? ": " + r.second : "") << std::endl;
+ total_errors++;
+ }
+
+ total_errors += params.m_num_errors;
+ total_tests += params.m_num_tests;
+ if (t != nullptr)
+ delete t;
+ }
+ std::cout << "================================================" << std::endl;
+ std::cout << "Found " << total_errors << " errors in " << total_tests << " tests from " << reg_entry_base::registry().size() << " test groups" << std::endl;
+ std::cout << "================================================" << std::endl;
+ return (total_errors ? 1 : 0);
+ }
+
+#define DEF_COMP(name, op) \
+ struct comp_ ## name \
+ { \
+ static const char * opstr() { return #op ; } \
+ template <typename T1, typename T2> \
+ bool operator()(const T1 &v1, const T2 &v2) { return v1 op v2; } \
+ };
+
+ DEF_COMP(eq, ==)
+ DEF_COMP(ne, !=)
+ DEF_COMP(gt, >)
+ DEF_COMP(lt, <)
+ DEF_COMP(ge, >=)
+ DEF_COMP(le, <=)
+
+#undef DEF_COMP
+
+} // namespace plib::testing
+
+
+#endif // PTESTS_H_
diff --git a/src/lib/netlist/plib/ptime.h b/src/lib/netlist/plib/ptime.h
index 9668e0cf428..0881f337371 100644
--- a/src/lib/netlist/plib/ptime.h
+++ b/src/lib/netlist/plib/ptime.h
@@ -1,18 +1,19 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * ptime.h
- */
#ifndef PTIME_H_
#define PTIME_H_
+///
+/// \file ptime.h
+///
+
+#include <type_traits>
+
#include "pconfig.h"
+#include "pmath.h" // std::floor
#include "ptypes.h"
-#include <cmath> // std::floor
-//#include <cstdint>
-
// ----------------------------------------------------------------------------------------
// netlist_time
// ----------------------------------------------------------------------------------------
@@ -20,6 +21,12 @@
namespace plib
{
+ template <typename T, typename U>
+ struct ptime_le
+ {
+ const static bool value = sizeof(T) <= sizeof(U);
+ };
+
template <typename TYPE, TYPE RES>
struct ptime final
{
@@ -28,6 +35,9 @@ namespace plib
using internal_type = TYPE;
using mult_type = TYPE;
+ template <typename altTYPE, altTYPE altRES>
+ friend struct ptime;
+
constexpr ptime() noexcept : m_time(0) {}
~ptime() noexcept = default;
@@ -36,102 +46,184 @@ namespace plib
constexpr ptime(ptime &&rhs) noexcept = default;
constexpr explicit ptime(const internal_type &time) noexcept : m_time(time) {}
constexpr explicit ptime(internal_type &&time) noexcept : m_time(time) {}
- C14CONSTEXPR ptime &operator=(const ptime &rhs) noexcept = default;
- C14CONSTEXPR ptime &operator=(ptime &&rhs) noexcept = default;
+ constexpr ptime &operator=(const ptime &rhs) noexcept = default;
+ constexpr ptime &operator=(ptime &&rhs) noexcept = default;
constexpr explicit ptime(const double t) = delete;
- //: m_time((internal_type) ( t * (double) resolution)) { }
constexpr explicit ptime(const internal_type nom, const internal_type den) noexcept
: m_time(nom * (RES / den)) { }
- C14CONSTEXPR ptime &operator+=(const ptime rhs) noexcept { m_time += rhs.m_time; return *this; }
- C14CONSTEXPR ptime &operator-=(const ptime rhs) noexcept { m_time -= rhs.m_time; return *this; }
- C14CONSTEXPR ptime &operator*=(const mult_type factor) noexcept { m_time *= static_cast<internal_type>(factor); return *this; }
+ template <typename O, typename = std::enable_if_t<ptime_le<ptime<O, RES>, ptime>::value>>
+ constexpr ptime(const ptime<O, RES> &rhs) noexcept
+ : m_time(static_cast<TYPE>(rhs.m_time))
+ {
+ }
+
+ template <typename O, typename T = std::enable_if_t<!ptime_le<ptime<O, RES>, ptime>::value, int>>
+ constexpr explicit ptime(const ptime<O, RES> &rhs, [[maybe_unused]] T dummy = 0) noexcept
+ : m_time(static_cast<TYPE>(rhs.m_time))
+ {
+ }
- friend constexpr const ptime operator-(ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ constexpr ptime &operator+=(const ptime<O, RES> &rhs) noexcept
+ {
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ m_time += rhs.m_time;
+ return *this;
+ }
+ template <typename O>
+ constexpr ptime &operator-=(const ptime<O, RES> &rhs) noexcept
{
- return ptime(lhs.m_time - rhs.m_time);
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ m_time -= rhs.m_time;
+ return *this;
}
- friend constexpr const ptime operator+(ptime lhs, const ptime rhs) noexcept
+ template <typename M>
+ constexpr ptime &operator*=(const M &factor) noexcept
{
- return ptime(lhs.m_time + rhs.m_time);
+ static_assert(plib::is_integral<M>::value, "Factor must be an integral type");
+ m_time *= factor;
+ return *this;
}
- friend constexpr const ptime operator*(ptime lhs, const mult_type &factor) noexcept
+ template <typename O>
+ constexpr ptime operator-(const ptime<O, RES> &rhs) const noexcept
{
- return ptime(lhs.m_time * factor);
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ return ptime(m_time - rhs.m_time);
}
- friend constexpr mult_type operator/(const ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ constexpr ptime operator+(ptime<O, RES> rhs) const noexcept
{
- return static_cast<mult_type>(lhs.m_time / rhs.m_time);
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ return ptime(m_time + rhs.m_time);
}
- friend constexpr bool operator<(const ptime lhs, const ptime rhs) noexcept
+ template <typename M>
+ constexpr ptime operator*(const M &factor) const noexcept
{
- return (lhs.m_time < rhs.m_time);
+ static_assert(plib::is_integral<M>::value, "Factor must be an integral type");
+ return ptime(m_time * static_cast<mult_type>(factor));
}
- friend constexpr bool operator>(const ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ constexpr mult_type operator/(const ptime<O, RES> &rhs) const noexcept
{
- return (rhs < lhs);
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ return static_cast<mult_type>(m_time / rhs.m_time);
}
- friend constexpr bool operator<=(const ptime lhs, const ptime rhs) noexcept
+ template <typename T>
+ constexpr std::enable_if_t<std::is_integral<T>::value, ptime>
+ operator/(const T &rhs) const noexcept
+ {
+ return ptime(m_time / rhs);
+ }
+
+ template <typename O>
+ friend constexpr bool operator<(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
+ {
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ return (lhs.m_time < rhs.as_raw());
+ //return (lhs.m_time < rhs.m_time);
+ }
+
+ template <typename O>
+ friend constexpr bool operator>(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
+ {
+ static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
+ return (lhs.m_time > rhs.as_raw());
+ }
+
+ template <typename O>
+ friend constexpr bool operator<=(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
{
return !(lhs > rhs);
}
- friend constexpr bool operator>=(const ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ friend constexpr bool operator>=(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
{
return !(lhs < rhs);
}
- friend constexpr bool operator==(const ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ friend constexpr bool operator==(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
{
- return lhs.m_time == rhs.m_time;
+ return lhs.m_time == rhs.as_raw();
}
- friend constexpr bool operator!=(const ptime lhs, const ptime rhs) noexcept
+ template <typename O>
+ friend constexpr bool operator!=(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
{
return !(lhs == rhs);
}
constexpr internal_type as_raw() const noexcept { return m_time; }
- constexpr double as_double() const noexcept
+
+ template <typename FT, typename = std::enable_if<plib::is_floating_point<FT>::value, FT>>
+ constexpr FT
+ as_fp() const noexcept
{
- return static_cast<double>(m_time) * inv_res;
+ return static_cast<FT>(m_time) * inv_res<FT>();
}
+ constexpr double as_double() const noexcept { return as_fp<double>(); }
+ constexpr float as_float() const noexcept { return as_fp<float>(); }
+ constexpr long double as_long_double() const noexcept { return as_fp<long double>(); }
+
+
+ constexpr ptime shl(unsigned shift) const noexcept { return ptime(m_time << shift); }
+ constexpr ptime shr(unsigned shift) const noexcept { return ptime(m_time >> shift); }
+
// for save states ....
- C14CONSTEXPR internal_type *get_internaltype_ptr() noexcept { return &m_time; }
- static constexpr ptime from_nsec(const internal_type ns) noexcept { return ptime(ns, UINT64_C(1000000000)); }
- static constexpr ptime from_usec(const internal_type us) noexcept { return ptime(us, UINT64_C( 1000000)); }
- static constexpr ptime from_msec(const internal_type ms) noexcept { return ptime(ms, UINT64_C( 1000)); }
- static constexpr ptime from_sec(const internal_type s) noexcept { return ptime(s, UINT64_C( 1)); }
- static constexpr ptime from_hz(const internal_type hz) noexcept { return ptime(1 , hz); }
- static constexpr ptime from_raw(const internal_type raw) noexcept { return ptime(raw); }
- static constexpr ptime from_double(const double t) noexcept { return ptime(static_cast<internal_type>(std::floor(t * static_cast<double>(RES) + 0.5)), RES); }
+ template <typename ST>
+ void save_state(ST &&st)
+ {
+ st.save_item(m_time, "m_time");
+ }
+
+ static constexpr ptime from_nsec(internal_type ns) noexcept { return ptime(ns, UINT64_C(1000000000)); }
+ static constexpr ptime from_usec(internal_type us) noexcept { return ptime(us, UINT64_C( 1000000)); }
+ static constexpr ptime from_msec(internal_type ms) noexcept { return ptime(ms, UINT64_C( 1000)); }
+ static constexpr ptime from_sec(internal_type s) noexcept { return ptime(s, UINT64_C( 1)); }
+ static constexpr ptime from_hz(internal_type hz) noexcept { return ptime(1 , hz); }
+ static constexpr ptime from_raw(internal_type raw) noexcept { return ptime(raw); }
+
+ template <typename FT>
+ static constexpr std::enable_if_t<plib::is_floating_point<FT>::value, ptime>
+ from_fp(FT t) noexcept { return ptime(static_cast<internal_type>(plib::floor(t * static_cast<FT>(RES) + static_cast<FT>(0.5))), RES); }
+
+ static constexpr ptime from_double(double t) noexcept
+ { return from_fp<double>(t); }
+
+ static constexpr ptime from_float(float t) noexcept
+ { return from_fp<float>(t); }
+
+ static constexpr ptime from_long_double(long double t) noexcept
+ { return from_fp<long double>(t); }
static constexpr ptime zero() noexcept { return ptime(0, RES); }
static constexpr ptime quantum() noexcept { return ptime(1, RES); }
static constexpr ptime never() noexcept { return ptime(plib::numeric_limits<internal_type>::max(), RES); }
static constexpr internal_type resolution() noexcept { return RES; }
- constexpr internal_type in_nsec() const noexcept { return m_time / (RES / UINT64_C(1000000000)); }
- constexpr internal_type in_usec() const noexcept { return m_time / (RES / UINT64_C( 1000000)); }
- constexpr internal_type in_msec() const noexcept { return m_time / (RES / UINT64_C( 1000)); }
- constexpr internal_type in_sec() const noexcept { return m_time / (RES / UINT64_C( 1)); }
+ constexpr internal_type in_nsec() const noexcept { return m_time / (RES / INT64_C(1000000000)); }
+ constexpr internal_type in_usec() const noexcept { return m_time / (RES / INT64_C( 1000000)); }
+ constexpr internal_type in_msec() const noexcept { return m_time / (RES / INT64_C( 1000)); }
+ constexpr internal_type in_sec() const noexcept { return m_time / (RES / INT64_C( 1)); }
private:
- static constexpr const double inv_res = 1.0 / static_cast<double>(RES);
+ template <typename FT>
+ static constexpr FT inv_res() noexcept { return static_cast<FT>(1.0) / static_cast<FT>(RES); }
internal_type m_time;
};
-
} // namespace plib
-#endif /* PTIME_H_ */
+#endif // PTIME_H_
diff --git a/src/lib/netlist/plib/ptimed_queue.h b/src/lib/netlist/plib/ptimed_queue.h
new file mode 100644
index 00000000000..495a8c89a89
--- /dev/null
+++ b/src/lib/netlist/plib/ptimed_queue.h
@@ -0,0 +1,357 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PTIMED_QUEUE_H_
+#define PTIMED_QUEUE_H_
+
+///
+/// \file ptimed_queue.h
+///
+
+#include "palloc.h" // FIXME: for aligned_vector
+#include "pchrono.h"
+#include "pmulti_threading.h"
+#include "ptypes.h"
+
+#include <algorithm>
+#include <mutex>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace plib {
+
+ // ----------------------------------------------------------------------------------------
+ // timed queue
+ // ----------------------------------------------------------------------------------------
+
+ template <typename Time, typename Element>
+ struct queue_entry_t final
+ {
+ using element_type = Element;
+
+ constexpr queue_entry_t() noexcept : m_exec_time(), m_object(nullptr) { }
+ constexpr queue_entry_t(const Time &t, const Element &o) noexcept : m_exec_time(t), m_object(o) { }
+
+ queue_entry_t(const queue_entry_t &) = default;
+ queue_entry_t &operator=(const queue_entry_t &) = default;
+ queue_entry_t(queue_entry_t &&) noexcept = default;
+ queue_entry_t &operator=(queue_entry_t &&) noexcept = default;
+
+ ~queue_entry_t() = default;
+
+ constexpr bool operator ==(const queue_entry_t &rhs) const noexcept
+ {
+ return m_object == rhs.m_object;
+ }
+
+ constexpr bool operator ==(const Element &rhs) const noexcept
+ {
+ return m_object == rhs;
+ }
+
+ constexpr bool operator <=(const queue_entry_t &rhs) const noexcept
+ {
+ return (m_exec_time <= rhs.m_exec_time);
+ }
+
+ constexpr bool operator <(const queue_entry_t &rhs) const noexcept
+ {
+ return (m_exec_time < rhs.m_exec_time);
+ }
+
+ static constexpr queue_entry_t never() noexcept { return queue_entry_t(Time::never(), nullptr); }
+
+ constexpr const Time &exec_time() const noexcept { return m_exec_time; }
+ constexpr const Element &object() const noexcept { return m_object; }
+ private:
+ Time m_exec_time;
+ Element m_object;
+ };
+
+ template <class A, class T>
+ class timed_queue_linear
+ {
+ public:
+
+ explicit timed_queue_linear(A &arena, const std::size_t list_size)
+ : m_list(arena, list_size)
+ {
+ clear();
+ }
+ ~timed_queue_linear() = default;
+
+ timed_queue_linear(const timed_queue_linear &) = delete;
+ timed_queue_linear &operator=(const timed_queue_linear &) = delete;
+ timed_queue_linear(timed_queue_linear &&) noexcept = delete;
+ timed_queue_linear &operator=(timed_queue_linear &&) noexcept = delete;
+
+ constexpr std::size_t capacity() const noexcept { return m_list.capacity() - 1; }
+ constexpr bool empty() const noexcept { return (m_end == &m_list[1]); }
+
+ template <bool KEEPSTAT, typename... Args>
+ constexpr void emplace (Args&&... args) noexcept;
+
+ template <bool KEEPSTAT>
+ constexpr void push(T && e) noexcept;
+
+ constexpr void pop() noexcept { --m_end; }
+
+ constexpr const T &top() const noexcept { return *(m_end-1); }
+
+ constexpr bool exists(const typename T::element_type &elem) const noexcept;
+
+ template <bool KEEPSTAT>
+ constexpr void remove(const T &elem) noexcept;
+
+ template <bool KEEPSTAT>
+ constexpr void remove(const typename T::element_type &elem) noexcept;
+
+ constexpr void clear() noexcept
+ {
+ m_end = &m_list[0];
+ // put an empty element with maximum time into the queue.
+ // the insert algo above will run into this element and doesn't
+ // need a comparison with queue start.
+ //
+ m_list[0] = T::never();
+ m_end++;
+ }
+
+ // save state support & mame disassembler
+
+ constexpr const T *list_pointer() const noexcept { return &m_list[1]; }
+ constexpr std::size_t size() const noexcept { return narrow_cast<std::size_t>(m_end - &m_list[1]); }
+ constexpr const T & operator[](std::size_t index) const noexcept { return m_list[ 1 + index]; }
+
+ private:
+ PALIGNAS(PALIGN_CACHELINE)
+ T * m_end;
+ plib::arena_vector<A, T, PALIGN_CACHELINE> m_list;
+
+ public:
+ // profiling
+ // FIXME: Make those private
+ pperfcount_t<true> m_prof_sort_move; // NOLINT
+ pperfcount_t<true> m_prof_call; // NOLINT
+ pperfcount_t<true> m_prof_remove; // NOLINT
+ };
+
+ template <class A, class T>
+ template <bool KEEPSTAT, typename... Args>
+ inline constexpr void timed_queue_linear<A, T>::emplace (Args&&... args) noexcept
+ {
+ T * i(m_end);
+ *i = T(std::forward<Args>(args)...);
+ //if (i->object() != nullptr && exists(i->object()))
+ // printf("Object exists %s\n", i->object()->name().c_str());
+ m_end++;
+ if constexpr (!KEEPSTAT)
+ {
+ for (; *(i-1) < *i; --i)
+ {
+ std::swap(*(i-1), *(i));
+ }
+ }
+ else
+ {
+ for (; *(i-1) < *i; --i)
+ {
+ std::swap(*(i-1), *(i));
+ m_prof_sort_move.inc();
+ }
+ m_prof_call.inc();
+ }
+ }
+
+ template <class A, class T>
+ template <bool KEEPSTAT>
+ inline constexpr void timed_queue_linear<A, T>::push(T && e) noexcept
+ {
+#if 0
+ // The code is not as fast as the code path which is enabled.
+ // It is left here in case on a platform different to x64 it may
+ // be faster.
+ T * i(m_end-1);
+ for (; *i < e; --i)
+ {
+ *(i+1) = *(i);
+ if (KEEPSTAT)
+ m_prof_sort_move.inc();
+ }
+ *(i+1) = std::move(e);
+ ++m_end;
+#else
+ //if (e.object() != nullptr && exists(e.object()))
+ // printf("Object exists %s\n", e.object()->name().c_str());
+ T * i(m_end++);
+ *i = std::move(e);
+ for (; *(i-1) < *i; --i)
+ {
+ std::swap(*(i-1), *(i));
+ if constexpr (KEEPSTAT)
+ m_prof_sort_move.inc();
+ }
+#endif
+ if constexpr (KEEPSTAT)
+ m_prof_call.inc();
+ }
+
+ template <class A, class T>
+ template <bool KEEPSTAT>
+ inline constexpr void timed_queue_linear<A, T>::remove(const T &elem) noexcept
+ {
+ if constexpr (KEEPSTAT)
+ m_prof_remove.inc();
+ for (T * i = m_end - 1; i > &m_list[0]; --i)
+ {
+ // == operator ignores time!
+ if (*i == elem)
+ {
+ std::copy(i+1, m_end--, i);
+ return;
+ }
+ }
+ //printf("Element not found in delete %s\n", elem->name().c_str());
+ }
+
+ template <class A, class T>
+ template <bool KEEPSTAT>
+ inline constexpr void timed_queue_linear<A, T>::remove(const typename T::element_type &elem) noexcept
+ {
+ if constexpr (KEEPSTAT)
+ m_prof_remove.inc();
+ for (T * i = m_end - 1; i > &m_list[0]; --i)
+ {
+ // == operator ignores time!
+ if (*i == elem)
+ {
+ std::copy(i+1, m_end--, i);
+ return;
+ }
+ }
+ //printf("Element not found in delete %s\n", elem->name().c_str());
+ }
+
+ template <class A, class T>
+ inline constexpr bool timed_queue_linear<A, T>::exists(const typename T::element_type &elem) const noexcept
+ {
+ for (T * i = &m_list[1]; i < m_end; ++i)
+ {
+ if (*i == elem)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ template <class A, class T>
+ class timed_queue_heap
+ {
+ public:
+
+ struct compare
+ {
+ constexpr bool operator()(const T &a, const T &b) const noexcept { return b <= a; }
+ };
+
+ explicit timed_queue_heap(A &arena, const std::size_t list_size)
+ : m_list(arena, list_size)
+ {
+ clear();
+ }
+ ~timed_queue_heap() = default;
+
+ PCOPYASSIGNMOVE(timed_queue_heap, delete)
+
+ std::size_t capacity() const noexcept { return m_list.capacity(); }
+ bool empty() const noexcept { return &m_list[0] == m_end; }
+
+ template <bool KEEPSTAT, typename... Args>
+ void emplace(Args&&... args) noexcept
+ {
+ *m_end++ = T(std::forward<Args>(args)...);
+ std::push_heap(&m_list[0], m_end, compare());
+ if (KEEPSTAT)
+ m_prof_call.inc();
+ }
+
+ template <bool KEEPSTAT>
+ void push(T &&e) noexcept
+ {
+ *m_end++ = e;
+ std::push_heap(&m_list[0], m_end, compare());
+ if (KEEPSTAT)
+ m_prof_call.inc();
+ }
+
+ void pop() noexcept
+ {
+ std::pop_heap(&m_list[0], m_end, compare());
+ m_end--;
+ }
+
+ const T &top() const noexcept { return m_list[0]; }
+
+ template <bool KEEPSTAT>
+ void remove(const T &elem) noexcept
+ {
+ if (KEEPSTAT)
+ m_prof_remove.inc();
+ for (T * i = m_end - 1; i >= &m_list[0]; i--)
+ {
+ if (*i == elem)
+ {
+ m_end--;
+ *i = *m_end;
+ std::make_heap(&m_list[0], m_end, compare());
+ return;
+ }
+ }
+ }
+
+ template <bool KEEPSTAT>
+ void remove(const typename T::element_type &elem) noexcept
+ {
+ if (KEEPSTAT)
+ m_prof_remove.inc();
+ for (T * i = m_end - 1; i >= &m_list[0]; i--)
+ {
+ if (*i == elem)
+ {
+ m_end--;
+ *i = *m_end;
+ std::make_heap(&m_list[0], m_end, compare());
+ return;
+ }
+ }
+ }
+
+ void clear()
+ {
+ m_list.clear();
+ m_end = &m_list[0];
+ }
+
+ // save state support & mame disassembler
+
+ constexpr const T *list_pointer() const { return &m_list[0]; }
+ constexpr std::size_t size() const noexcept { return m_list.size(); }
+ constexpr const T & operator[](const std::size_t index) const { return m_list[ 0 + index]; }
+ private:
+ T * m_end;
+ plib::arena_vector<A, T> m_list;
+
+ public:
+ // profiling
+ pperfcount_t<true> m_prof_sort_move; // NOLINT
+ pperfcount_t<true> m_prof_call; // NOLINT
+ pperfcount_t<true> m_prof_remove; // NOLINT
+ };
+
+} // namespace plib
+
+#endif // PTIMED_QUEUE_H_
diff --git a/src/lib/netlist/plib/ptokenizer.cpp b/src/lib/netlist/plib/ptokenizer.cpp
new file mode 100644
index 00000000000..59468bf84cd
--- /dev/null
+++ b/src/lib/netlist/plib/ptokenizer.cpp
@@ -0,0 +1,371 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#include "ptokenizer.h"
+
+#include "palloc.h"
+#include "pstonum.h"
+#include "pstrutil.h"
+
+namespace plib {
+
+ PERRMSGV(MF_EXPECTED_TOKEN_1_GOT_2, 2, "Expected token <{1}>, got <{2}>")
+ PERRMSGV(MF_EXPECTED_STRING_GOT_1, 1, "Expected a string, got <{1}>")
+ PERRMSGV(MF_EXPECTED_IDENTIFIER_GOT_1, 1, "Expected an identifier, got <{1}>")
+ PERRMSGV(MF_EXPECTED_ID_OR_NUM_GOT_1, 1, "Expected an identifier or number, got <{1}>")
+ PERRMSGV(MF_EXPECTED_NUMBER_GOT_1, 1, "Expected a number, got <{1}>")
+ PERRMSGV(MF_EXPECTED_LONGINT_GOT_1, 1, "Expected a long int, got <{1}>")
+ PERRMSGV(MF_EXPECTED_LINENUM_GOT_1, 1, "Expected line number after line marker but got <{1}>")
+ PERRMSGV(MF_EXPECTED_FILENAME_GOT_1, 1, "Expected file name after line marker but got <{1}>")
+
+ // ----------------------------------------------------------------------------------------
+ // A simple tokenizer
+ // ----------------------------------------------------------------------------------------
+
+ void tokenizer_t::skip_eol()
+ {
+ pstring::value_type c = getc();
+ while (c != 0)
+ {
+ if (c == 10)
+ {
+ c = getc();
+ if (c != 13)
+ ungetc(c);
+ return;
+ }
+ c = getc();
+ }
+ }
+
+ pstring::value_type tokenizer_t::getc()
+ {
+ if (m_unget != 0)
+ {
+ pstring::value_type c = m_unget;
+ m_unget = 0;
+ return c;
+ }
+ if (m_px == m_cur_line.end())
+ {
+ //++m_source_location.back();
+ putf8string line;
+ if (m_strm->read_line_lf(line))
+ {
+ m_cur_line = pstring(line);
+ m_px = m_cur_line.begin();
+ if (*m_px != '#')
+ m_token_queue->push_back(token_t(token_type::SOURCELINE, m_cur_line));
+ }
+ else
+ return 0;
+ }
+ pstring::value_type c = *(m_px++);
+ return c;
+ }
+
+ void tokenizer_t::ungetc(pstring::value_type c)
+ {
+ m_unget = c;
+ }
+
+ void tokenizer_t::append_to_store(putf8_reader *reader, token_store_t &store)
+ {
+ clear();
+ m_strm = reader;
+ // Process tokens into queue
+ token_t ret(token_type::UNKNOWN);
+ m_token_queue = &store;
+ do {
+ ret = get_token_comment();
+ store.push_back(ret);
+ } while (!ret.is_type(token_type::token_type::ENDOFFILE));
+ m_token_queue = nullptr;
+ }
+
+ void token_reader_t::require_token(const token_id_t &token_num)
+ {
+ require_token(get_token(), token_num);
+ }
+
+ void token_reader_t::require_token(const token_t &tok, const token_id_t &token_num)
+ {
+ if (!tok.is(token_num))
+ {
+ error(MF_EXPECTED_TOKEN_1_GOT_2(token_num.name(), tok.str()));
+ }
+ }
+
+ pstring token_reader_t::get_string()
+ {
+ token_t tok = get_token();
+ if (!tok.is_type(token_type::STRING))
+ {
+ error(MF_EXPECTED_STRING_GOT_1(tok.str()));
+ }
+ return tok.str();
+ }
+
+
+ pstring token_reader_t::get_identifier()
+ {
+ token_t tok = get_token();
+ if (!tok.is_type(token_type::IDENTIFIER))
+ {
+ error(MF_EXPECTED_IDENTIFIER_GOT_1(tok.str()));
+ }
+ return tok.str();
+ }
+
+ pstring token_reader_t::get_identifier_or_number()
+ {
+ token_t tok = get_token();
+ if (!(tok.is_type(token_type::IDENTIFIER) || tok.is_type(token_type::NUMBER)))
+ {
+ error(MF_EXPECTED_ID_OR_NUM_GOT_1(tok.str()));
+ }
+ return tok.str();
+ }
+
+ // FIXME: combine into template
+ double token_reader_t::get_number_double()
+ {
+ token_t tok = get_token();
+ if (!tok.is_type(token_type::NUMBER))
+ {
+ error(MF_EXPECTED_NUMBER_GOT_1(tok.str()));
+ }
+ bool err(false);
+ auto ret = plib::pstonum_ne<double>(tok.str(), err);
+ if (err)
+ error(MF_EXPECTED_NUMBER_GOT_1(tok.str()));
+ return ret;
+ }
+
+ long token_reader_t::get_number_long()
+ {
+ token_t tok = get_token();
+ if (!tok.is_type(token_type::NUMBER))
+ {
+ error(MF_EXPECTED_LONGINT_GOT_1(tok.str()) );
+ }
+ bool err(false);
+ auto ret = plib::pstonum_ne<long>(tok.str(), err);
+ if (err)
+ error(MF_EXPECTED_LONGINT_GOT_1(tok.str()) );
+ return ret;
+ }
+
+ bool token_reader_t::process_line_token(const token_t &tok)
+ {
+ if (tok.is_type(token_type::LINEMARKER))
+ {
+ bool benter(false);
+ bool bexit(false);
+ pstring file;
+ unsigned lineno(0);
+
+ auto sp = psplit(tok.str(), ' ');
+ //printf("%d %s\n", (int) sp.size(), ret.str().c_str());
+
+ bool err = false;
+ lineno = pstonum_ne<unsigned>(sp[1], err);
+ if (err)
+ error(MF_EXPECTED_LINENUM_GOT_1(tok.str()));
+ if (sp[2].substr(0,1) != "\"")
+ error(MF_EXPECTED_FILENAME_GOT_1(tok.str()));
+ file = sp[2].substr(1, sp[2].length() - 2);
+
+ for (std::size_t i = 3; i < sp.size(); i++)
+ {
+ if (sp[i] == "1")
+ benter = true;
+ if (sp[i] == "2")
+ bexit = true;
+ // FIXME: process flags; actually only 1 (file enter) and 2 (after file exit)
+ }
+ if (bexit) // pop the last location
+ m_source_location.pop_back();
+ if (!benter) // new location!
+ m_source_location.pop_back();
+ m_source_location.emplace_back(plib::source_location(file, lineno));
+ return true;
+ }
+
+ if (tok.is_type(token_type::SOURCELINE))
+ {
+ m_line = tok.str();
+ ++m_source_location.back();
+ return true;
+ }
+
+ return false;
+ }
+
+ token_reader_t::token_t token_reader_t::get_token()
+ {
+ token_t ret = get_token_queue();
+ while (true)
+ {
+ if (ret.is_type(token_type::token_type::ENDOFFILE))
+ return ret;
+
+ //printf("%s\n", ret.str().c_str());
+ if (process_line_token(ret))
+ {
+ ret = get_token_queue();
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ }
+
+ token_reader_t::token_t token_reader_t::get_token_raw()
+ {
+ token_t ret = get_token_queue();
+ process_line_token(ret);
+ return ret;
+ }
+
+ token_reader_t::token_t tokenizer_t::get_token_internal()
+ {
+ // skip ws
+ pstring::value_type c = getc();
+ while (m_whitespace.find(c) != pstring::npos)
+ {
+ c = getc();
+ if (eof())
+ {
+ return token_t(token_type::ENDOFFILE);
+ }
+ }
+ if (m_support_line_markers && c == '#')
+ {
+ pstring lm("#");
+ do
+ {
+ c = getc();
+ if (eof())
+ return token_t(token_type::ENDOFFILE);
+ if (c == '\r' || c == '\n')
+ return { token_type::LINEMARKER, lm };
+ lm += c;
+ } while (true);
+ }
+ if (m_number_chars_start.find(c) != pstring::npos)
+ {
+ // read number while we receive number or identifier chars
+ // treat it as an identifier when there are identifier chars in it
+ token_type ret = token_type::NUMBER;
+ pstring tokstr = "";
+ while (true) {
+ if (m_identifier_chars.find(c) != pstring::npos && m_number_chars.find(c) == pstring::npos)
+ ret = token_type::IDENTIFIER;
+ else if (m_number_chars.find(c) == pstring::npos)
+ break;
+ tokstr += c;
+ c = getc();
+ }
+ ungetc(c);
+ return { ret, tokstr };
+ }
+
+ // not a number, try identifier
+ if (m_identifier_chars.find(c) != pstring::npos)
+ {
+ // read identifier till non identifier char
+ pstring tokstr = "";
+ while (m_identifier_chars.find(c) != pstring::npos)
+ {
+ tokstr += c;
+ c = getc();
+ }
+ ungetc(c);
+ auto id = m_tokens.find(tokstr);
+ return (id != m_tokens.end()) ?
+ token_t(id->second, tokstr)
+ : token_t(token_type::IDENTIFIER, tokstr);
+ }
+
+ if (c == m_string)
+ {
+ pstring tokstr = "";
+ c = getc();
+ while (c != m_string)
+ {
+ tokstr += c;
+ c = getc();
+ }
+ return { token_type::STRING, tokstr };
+ }
+
+ // read identifier till first identifier char or ws
+ pstring tokstr = "";
+ while ((m_identifier_chars.find(c) == pstring::npos) && (m_whitespace.find(c) == pstring::npos))
+ {
+ tokstr += c;
+ // expensive, check for single char tokens
+ if (tokstr.length() == 1)
+ {
+ auto id = m_tokens.find(tokstr);
+ if (id != m_tokens.end())
+ return { id->second, tokstr };
+ }
+ c = getc();
+ }
+ ungetc(c);
+ auto id = m_tokens.find(tokstr);
+ return (id != m_tokens.end()) ?
+ token_t(id->second, tokstr)
+ : token_t(token_type::UNKNOWN, tokstr);
+ }
+
+ token_reader_t::token_t tokenizer_t::get_token_comment()
+ {
+ token_t ret = get_token_internal();
+ while (true)
+ {
+ if (ret.is_type(token_type::token_type::ENDOFFILE))
+ return ret;
+
+ if (ret.is(m_tok_comment_start))
+ {
+ do {
+ ret = get_token_internal();
+ } while (ret.is_not(m_tok_comment_end));
+ ret = get_token_internal();
+ }
+ else if (ret.is(m_tok_line_comment))
+ {
+ skip_eol();
+ ret = get_token_internal();
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ }
+
+
+ void token_reader_t::error(const perrmsg &errs)
+ {
+ pstring s("");
+ pstring trail (" from ");
+ pstring trail_first("In file included from ");
+ pstring e = plib::pfmt("{1}:{2}:0: error: {3}\n")
+ (m_source_location.back().file_name(), m_source_location.back().line(), errs());
+ m_source_location.pop_back();
+ while (!m_source_location.empty())
+ {
+ if (m_source_location.size() == 1)
+ trail = trail_first;
+ s = plib::pfmt("{1}{2}:{3}:0\n{4}")(trail, m_source_location.back().file_name(), m_source_location.back().line(), s);
+ m_source_location.pop_back();
+ }
+ verror("\n" + s + e + " " + m_line + "\n");
+ }
+
+} // namespace plib
diff --git a/src/lib/netlist/plib/ptokenizer.h b/src/lib/netlist/plib/ptokenizer.h
new file mode 100644
index 00000000000..5bc69eecc6b
--- /dev/null
+++ b/src/lib/netlist/plib/ptokenizer.h
@@ -0,0 +1,277 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef PTOKENIZER_H_
+#define PTOKENIZER_H_
+
+///
+/// \file ptokenizer.h
+///
+
+#include "pstream.h"
+#include "pstring.h"
+
+#include "penum.h"
+#include "psource.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace plib {
+
+ namespace detail {
+
+ PENUM(token_type,
+ IDENTIFIER,
+ NUMBER,
+ TOKEN,
+ STRING,
+ COMMENT,
+ LINEMARKER,
+ SOURCELINE,
+ UNKNOWN,
+ ENDOFFILE
+ )
+
+ struct token_id_t
+ {
+ public:
+
+ static constexpr std::size_t npos = static_cast<std::size_t>(-1);
+
+ token_id_t()
+ : m_id(npos)
+ {}
+
+ explicit token_id_t(std::size_t id, const pstring &name)
+ : m_id(id)
+ , m_name(name)
+ {}
+
+ token_id_t(const token_id_t &) = default;
+ token_id_t &operator=(const token_id_t &) = default;
+ token_id_t(token_id_t &&) noexcept = default;
+ token_id_t &operator=(token_id_t &&) noexcept = default;
+
+ ~token_id_t() = default;
+
+ constexpr std::size_t id() const { return m_id; }
+ constexpr const pstring & name() const { return m_name; }
+ private:
+ std::size_t m_id;
+ pstring m_name;
+ };
+
+ struct token_t
+ {
+ explicit token_t(token_type type)
+ : m_type(type), m_id(token_id_t::npos), m_token("")
+ {
+ }
+ token_t(token_type type, const pstring &str)
+ : m_type(type), m_id(token_id_t::npos), m_token(str)
+ {
+ }
+ token_t(const token_id_t &id)
+ : m_type(token_type::TOKEN), m_id(id.id()), m_token(id.name())
+ {
+ }
+ token_t(const token_id_t &id, const pstring &str)
+ : m_type(token_type::TOKEN), m_id(id.id()), m_token(str)
+ {
+ }
+
+ token_t(const token_t &) = default;
+ token_t &operator=(const token_t &) = default;
+ token_t(token_t &&) noexcept = default;
+ token_t &operator=(token_t &&) noexcept = default;
+
+ ~token_t() = default;
+
+ constexpr bool is(const token_id_t &tok_id) const noexcept { return m_id == tok_id.id(); }
+ constexpr bool is_not(const token_id_t &tok_id) const noexcept { return !is(tok_id); }
+ constexpr bool is_type(const token_type type) const noexcept { return m_type == type; }
+ constexpr token_type type() const noexcept { return m_type; }
+ constexpr const pstring &str() const noexcept { return m_token; }
+
+ private:
+ token_type m_type;
+ std::size_t m_id;
+ pstring m_token;
+ };
+
+ class token_store_t : public std::vector<token_t>
+ {
+ using std::vector<token_t>::vector;
+ };
+
+ } // namespace detail
+
+ class tokenizer_t
+ {
+ public:
+ using token_type = detail::token_type;
+ using token_id_t = detail::token_id_t;
+ using token_t = detail::token_t;
+ using token_store_t = detail::token_store_t;
+
+ explicit tokenizer_t() // NOLINT(misc-forwarding-reference-overload, bugprone-forwarding-reference-overload)
+ : m_strm(nullptr)
+ , m_unget(0)
+ , m_string('"')
+ , m_support_line_markers(true) // FIXME
+ , m_token_queue(nullptr)
+ {
+ clear();
+ }
+
+ tokenizer_t(const tokenizer_t &) = delete;
+ tokenizer_t &operator=(const tokenizer_t &) = delete;
+ tokenizer_t(tokenizer_t &&) noexcept = delete;
+ tokenizer_t &operator=(tokenizer_t &&) noexcept = delete;
+
+ virtual ~tokenizer_t() = default;
+
+ // tokenizer stuff follows ...
+
+ token_id_t register_token(const pstring &token)
+ {
+ token_id_t ret(m_tokens.size(), token);
+ m_tokens.emplace(token, ret);
+ return ret;
+ }
+
+ tokenizer_t & identifier_chars(const pstring &s) { m_identifier_chars = s; return *this; }
+ tokenizer_t & number_chars(const pstring &st, const pstring & rem) { m_number_chars_start = st; m_number_chars = rem; return *this; }
+ tokenizer_t & string_char(pstring::value_type c) { m_string = c; return *this; }
+ tokenizer_t & whitespace(const pstring & s) { m_whitespace = s; return *this; }
+ tokenizer_t & comment(const pstring &start, const pstring &end, const pstring &line)
+ {
+ m_tok_comment_start = register_token(start);
+ m_tok_comment_end = register_token(end);
+ m_tok_line_comment = register_token(line);
+ return *this;
+ }
+
+ void append_to_store(putf8_reader *reader, token_store_t &store);
+
+ private:
+
+ void clear()
+ {
+ m_cur_line = "";
+ m_px = m_cur_line.begin();
+ m_unget = 0;
+ }
+
+ token_t get_token_internal();
+
+ // get internal token with comment processing
+ token_t get_token_comment();
+
+ void skip_eol();
+
+ pstring::value_type getc();
+ void ungetc(pstring::value_type c);
+
+ bool eof() const { return m_strm->eof(); }
+
+ putf8_reader *m_strm;
+
+ pstring m_cur_line;
+ pstring::const_iterator m_px;
+ pstring::value_type m_unget;
+
+ // tokenizer stuff follows ...
+
+ pstring m_identifier_chars;
+ pstring m_number_chars;
+ pstring m_number_chars_start;
+ std::unordered_map<pstring, token_id_t> m_tokens;
+ pstring m_whitespace;
+ pstring::value_type m_string;
+
+ token_id_t m_tok_comment_start;
+ token_id_t m_tok_comment_end;
+ token_id_t m_tok_line_comment;
+
+ protected:
+ bool m_support_line_markers;
+ token_store_t *m_token_queue;
+ };
+
+ class token_reader_t
+ {
+ public:
+
+ using token_t = tokenizer_t::token_t;
+ using token_type = tokenizer_t::token_type;
+ using token_id_t = tokenizer_t::token_id_t;
+ using token_store = tokenizer_t::token_store_t;
+
+ explicit token_reader_t()
+ : m_idx(0)
+ , m_token_store(nullptr)
+ {
+ // add a first entry to the stack
+ m_source_location.emplace_back(plib::source_location("Unknown", 0));
+ }
+
+ token_reader_t(const token_reader_t &) = delete;
+ token_reader_t &operator=(const token_reader_t &) = delete;
+ token_reader_t(token_reader_t &&) noexcept = delete;
+ token_reader_t &operator=(token_reader_t &&) noexcept = delete;
+
+ virtual ~token_reader_t() = default;
+
+ void set_token_source(const token_store *store)
+ {
+ m_token_store = store;
+ }
+
+ pstring current_line_str() const;
+
+ // tokenizer stuff follows ...
+
+ token_t get_token();
+ token_t get_token_raw(); // includes line information
+ pstring get_string();
+ pstring get_identifier();
+ pstring get_identifier_or_number();
+
+ double get_number_double();
+ long get_number_long();
+
+ void require_token(const token_id_t &token_num);
+ void require_token(const token_t &tok, const token_id_t &token_num);
+
+ void error(const perrmsg &errs);
+
+ plib::source_location location() { return m_source_location.back(); }
+
+ pstring current_line() const { return m_line; }
+
+ protected:
+ virtual void verror(const pstring &msg) = 0;
+
+ private:
+ bool process_line_token(const token_t &tok);
+
+ token_t get_token_queue()
+ {
+ if (m_idx < m_token_store->size())
+ return (*m_token_store)[m_idx++];
+ return token_t(token_type::ENDOFFILE);
+ }
+
+ // source locations, vector used as stack because we need to loop through stack
+
+ std::vector<plib::source_location> m_source_location;
+ pstring m_line;
+ std::size_t m_idx;
+ const token_store * m_token_store;
+ };
+
+} // namespace plib
+
+#endif // PTOKENIZER_H_
diff --git a/src/lib/netlist/plib/ptypes.h b/src/lib/netlist/plib/ptypes.h
index bda62099150..971a509406c 100644
--- a/src/lib/netlist/plib/ptypes.h
+++ b/src/lib/netlist/plib/ptypes.h
@@ -1,138 +1,411 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * ptypes.h
- *
- */
#ifndef PTYPES_H_
#define PTYPES_H_
+///
+/// \file ptypes.h
+///
+
#include "pconfig.h"
+#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
-#define COPYASSIGNMOVE(name, def) \
- name(const name &) = def; \
- name(name &&) noexcept = def; \
- name &operator=(const name &) = def; \
- name &operator=(name &&) noexcept = def;
+#if (PUSE_FLOAT128)
+#if defined(__has_include)
+#if __has_include(<quadmath.h>)
+#include <quadmath.h>
+#endif
+#endif
+#endif
+
+#define PCOPYASSIGNMOVE(name, def) \
+ PCOPYASSIGN(name, def) \
+ PMOVEASSIGN(name, def)
+
+#define PCOPYASSIGN(name, def) \
+ name(const name &) = def; \
+ name &operator=(const name &) = def;
+
+#define PMOVEASSIGN(name, def) \
+ name(name &&) noexcept = def; \
+ name &operator=(name &&) noexcept = def;
+
+#if defined(EMSCRIPTEN)
+#undef EMSCRIPTEN
+#endif
+
+// -----------------------------------------------------------------------------
+// forward definitions
+// -----------------------------------------------------------------------------
+
+namespace plib
+{
+ template <typename BASEARENA, std::size_t MINALIGN>
+ class mempool_arena;
+
+ template <std::size_t MINALLOC = 0>
+ struct aligned_arena;
+
+ class dynamic_library_base;
+
+ template<bool debug_enabled>
+ class plog_base;
+
+ struct plog_level;
-#define COPYASSIGN(name, def) \
- name(const name &) = def; \
- name &operator=(const name &) = def; \
+ template <typename A, typename T>
+ class timed_queue_linear;
+
+ template <typename A, typename T>
+ class timed_queue_heap;
+
+ namespace detail
+ {
+ class token_store_t;
+ } // namespace detail
+
+} // namespace plib
namespace plib
{
+ //============================================================
+ // compile time information
+ //============================================================
+
+ /// \brief Dummy 128 bit types for platforms which don't support 128 bit
+ ///
+ /// Users should always consult compile_info::has_int128 prior to
+ /// using the UINT128/INT128 data type.
+ ///
+ struct UINT128_DUMMY {};
+ struct INT128_DUMMY {};
+
+ enum class ci_compiler
+ {
+ UNKNOWN,
+ CLANG,
+ GCC,
+ MSC
+ };
+
+ enum class ci_cpp_stdlib
+ {
+ UNKNOWN,
+ LIBSTDCXX,
+ LIBCPP,
+ MSVCPRT
+ };
+
+ enum class ci_os
+ {
+ UNKNOWN,
+ LINUX,
+ FREEBSD,
+ OPENBSD,
+ WINDOWS,
+ MACOSX,
+ EMSCRIPTEN
+ };
+
+ enum class ci_arch
+ {
+ UNKNOWN,
+ X86,
+ ARM,
+ MIPS,
+ IA64
+ };
+
+ enum class ci_env
+ {
+ DEFAULT,
+ MSVC,
+ NVCC
+ };
+
+ // <sys/types.h> on ubuntu system may define major and minor as macros
+ // That's why we use vmajor, .. here
+ template <std::size_t MAJOR, std::size_t MINOR, std::size_t PL = 0>
+ struct typed_version
+ {
+ static_assert((MINOR < 100) && (PL < 100), "typed_version: MAJOR, MINOR or PATCHLEVEL exceeds or equal to 100");
+ using vmajor = std::integral_constant<std::size_t, MAJOR>;
+ using vminor = std::integral_constant<std::size_t, MINOR>;
+ using vpatchlevel = std::integral_constant<std::size_t, PL>;
+ using full = std::integral_constant<std::size_t, MAJOR * 10000 + MINOR * 100 + PL>;
+ };
+
+ struct compile_info
+ {
+ #ifdef _WIN32
+ using win32 = std::integral_constant<bool, true>;
+ #ifdef UNICODE
+ using unicode = std::integral_constant<bool, true>;
+ #else
+ using unicode = std::integral_constant<bool, false>;
+ #endif
+ #else
+ using win32 = std::integral_constant<bool, false>;
+ using unicode = std::integral_constant<bool, true>;
+ #endif
+ #ifdef __SIZEOF_INT128__
+ using has_int128 = std::integral_constant<bool, true>;
+ using int128_type = __int128_t;
+ using uint128_type = __uint128_t;
+ static constexpr int128_type int128_max() { return (~static_cast<uint128_type>(0)) >> 1; }
+ static constexpr uint128_type uint128_max() { return ~(static_cast<uint128_type>(0)); }
+ #else
+ using has_int128 = std::integral_constant<bool, false>;
+ using int128_type = INT128_DUMMY;
+ using uint128_type = UINT128_DUMMY;
+ static constexpr int128_type int128_max() { return int128_type(); }
+ static constexpr uint128_type uint128_max() { return uint128_type(); }
+ #endif
+ #if defined(__clang__)
+ using type = std::integral_constant<ci_compiler, ci_compiler::CLANG>;
+ using version = typed_version<__clang_major__, __clang_minor__, __clang_patchlevel__>;
+ #elif defined(__GNUC__)
+ using type = std::integral_constant<ci_compiler, ci_compiler::GCC>;
+ using version = typed_version<__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__>;
+ #elif defined(_MSC_VER)
+ using type = std::integral_constant<ci_compiler, ci_compiler::MSC>;
+ using version = typed_version<_MSC_VER / 100, _MSC_VER % 100>;
+ #else
+ using type = std::integral_constant<ci_compiler, ci_compiler::UNKNOWN>;
+ using version = typed_version<0, 0>;
+ #endif
+ #if defined(_LIBCPP_VERSION)
+ using cpp_stdlib = std::integral_constant<ci_cpp_stdlib, ci_cpp_stdlib::LIBCPP>;
+ using cpp_stdlib_version = typed_version<(_LIBCPP_VERSION) / 1000, ((_LIBCPP_VERSION) / 100) % 10, _LIBCPP_VERSION % 100>;
+ #elif defined(__GLIBCXX__)
+ using cpp_stdlib = std::integral_constant<ci_cpp_stdlib, ci_cpp_stdlib::LIBSTDCXX>;
+ using cpp_stdlib_version = typed_version<(_GLIBCXX_RELEASE), 0>;
+ #elif defined(_CPPLIB_VER) && defined(_MSVC_STL_VERSION)
+ using cpp_stdlib = std::integral_constant<ci_cpp_stdlib, ci_cpp_stdlib::MSVCPRT>;
+ using cpp_stdlib_version = typed_version<_CPPLIB_VER, 0>;
+ #else
+ using cpp_stdlib = std::integral_constant<ci_cpp_stdlib, ci_cpp_stdlib::UNKNOWN>;
+ using cpp_stdlib_version = typed_version<0, 0, 0>;
+ #endif
+ #ifdef __unix__
+ using is_unix = std::integral_constant<bool, true>;
+ #else
+ using is_unix = std::integral_constant<bool, false>;
+ #endif
+ #if defined(__linux__)
+ using os = std::integral_constant<ci_os, ci_os::LINUX>;
+ #elif defined(__FreeBSD__)
+ using os = std::integral_constant<ci_os, ci_os::FREEBSD>;
+ #elif defined(__OpenBSD__)
+ using os = std::integral_constant<ci_os, ci_os::OPENBSD>;
+ #elif defined(__EMSCRIPTEN__)
+ using os = std::integral_constant<ci_os, ci_os::EMSCRIPTEN>;
+ #elif defined(_WIN32)
+ using os = std::integral_constant<ci_os, ci_os::WINDOWS>;
+ #elif defined(__APPLE__)
+ using os = std::integral_constant<ci_os, ci_os::MACOSX>;
+ #else
+ using os = std::integral_constant<ci_os, ci_os::UNKNOWN>;
+ #endif
+ #if defined(__x86_64__) || defined (_M_X64) || defined(__aarch64__) || defined(__mips64) || defined(_M_AMD64) || defined(_M_ARM64)
+ using m64 = std::integral_constant<bool, true>;
+ #else
+ using m64 = std::integral_constant<bool, false>;
+ #endif
+ #if defined(__x86_64__) || defined(__i386__)
+ using arch = std::integral_constant<ci_arch, ci_arch::X86>;
+ #elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(_M_ARM)
+ using arch = std::integral_constant<ci_arch, ci_arch::ARM>;
+ #elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
+ using arch = std::integral_constant<ci_arch, ci_arch::MIPS>;
+ #elif defined(__ia64__)
+ using arch = std::integral_constant<ci_arch, ci_arch::IA64>;
+ #else
+ using arch = std::integral_constant<ci_arch, ci_arch::UNKNOWN>;
+ #endif
+ #if defined(__MINGW32__)
+ using mingw = std::integral_constant<bool, true>;
+ #else
+ using mingw = std::integral_constant<bool, false>;
+ #endif
+ #if defined(__APPLE__)
+ using clang_noexcept_issue = std::integral_constant<bool, (type::value == ci_compiler::CLANG) && (version::full::value < 110003)>;
+ #else
+ using clang_noexcept_issue = std::integral_constant<bool, (type::value == ci_compiler::CLANG) && (version::vmajor::value < 9)>;
+ #endif
+ #if defined(__ia64__)
+ using abi_vtable_function_descriptors = std::integral_constant<bool, true>;
+ #else
+ using abi_vtable_function_descriptors = std::integral_constant<bool, false>;
+ #endif
+ #if defined(_MSC_VER)
+ using env = std::integral_constant<ci_env, ci_env::MSVC>;
+ using env_version = typed_version<_MSC_VER / 100, _MSC_VER % 100>;
+ #elif defined(__NVCC__) || defined(__CUDACC__)
+ using env = std::integral_constant<ci_env, ci_env::NVCC>;
+ using env_version = typed_version<__CUDA_API_VER_MAJOR__, __CUDA_API_VER_MINOR__, __CUDACC_VER_BUILD__>;
+ #if defined(__CUDA_ARCH__)
+ using cuda_arch = std::integral_constant<std::size_t, __CUDA_ARCH__>;
+ #else
+ using cuda_arch = std::integral_constant<std::size_t, 0>;
+ #endif
+ #else
+ using env = std::integral_constant<ci_env, ci_env::DEFAULT>;
+ using env_version = version;
+ #endif
+ };
+
+} // namespace plib
+
+using INT128 = plib::compile_info::int128_type;
+using UINT128 = plib::compile_info::uint128_type;
+
+namespace plib
+{
+
template<typename T> struct is_integral : public std::is_integral<T> { };
+ template<typename T> struct is_signed : public std::is_signed<T> { };
+ template<typename T> struct is_unsigned : public std::is_unsigned<T> { };
template<typename T> struct numeric_limits : public std::numeric_limits<T> { };
- /* 128 bit support at least on GCC is not fully supported */
-#if PHAS_INT128
+ // 128 bit support at least on GCC is not fully supported
+
template<> struct is_integral<UINT128> { static constexpr bool value = true; };
template<> struct is_integral<INT128> { static constexpr bool value = true; };
+
+ template<> struct is_signed<UINT128> { static constexpr bool value = false; };
+ template<> struct is_signed<INT128> { static constexpr bool value = true; };
+
+ template<> struct is_unsigned<UINT128> { static constexpr bool value = true; };
+ template<> struct is_unsigned<INT128> { static constexpr bool value = false; };
+
template<> struct numeric_limits<UINT128>
{
- static constexpr UINT128 max()
+ static constexpr UINT128 max() noexcept
{
- return ~((UINT128)0);
+ return compile_info::uint128_max();
}
};
template<> struct numeric_limits<INT128>
{
- static constexpr INT128 max()
+ static constexpr INT128 max() noexcept
{
- return (~((UINT128)0)) >> 1;
+ return compile_info::int128_max();
}
};
-#endif
- //============================================================
- // prevent implicit copying
- //============================================================
+ template<typename T> struct is_floating_point : public std::is_floating_point<T> { };
- struct nocopyassignmove
- {
- nocopyassignmove(const nocopyassignmove &) = delete;
- nocopyassignmove(nocopyassignmove &&) = delete;
- nocopyassignmove &operator=(const nocopyassignmove &) = delete;
- nocopyassignmove &operator=(nocopyassignmove &&) = delete;
- protected:
- nocopyassignmove() = default;
- ~nocopyassignmove() = default;
- };
+ template<class T>
+ struct is_arithmetic : std::integral_constant<bool,
+ plib::is_integral<T>::value || plib::is_floating_point<T>::value> {};
- struct nocopyassign
+#if PUSE_FLOAT128
+ template<> struct is_floating_point<FLOAT128> { static constexpr bool value = true; };
+ template<> struct numeric_limits<FLOAT128>
{
- nocopyassign(const nocopyassign &) = delete;
- nocopyassign &operator=(const nocopyassign &) = delete;
- protected:
- nocopyassign() = default;
- ~nocopyassign() = default;
- nocopyassign(nocopyassign &&) = default;
- nocopyassign &operator=(nocopyassign &&) = default;
+ static constexpr FLOAT128 max() noexcept
+ {
+ return FLT128_MAX;
+ }
+ static constexpr FLOAT128 lowest() noexcept
+ {
+ return -FLT128_MAX;
+ }
};
+#endif
- //============================================================
- // Avoid unused variable warnings
- //============================================================
- template<typename... Ts>
- inline void unused_var(Ts&&...) {}
-
- //============================================================
- // is_pow2
- //============================================================
- template <typename T>
- constexpr bool is_pow2(T v) noexcept
+ template<unsigned bits>
+ struct size_for_bits
{
- static_assert(is_integral<T>::value, "is_pow2 needs integer arguments");
- return !(v & (v-1));
- }
+ static_assert(bits <= 64 || (bits <= 128 && compile_info::has_int128::value), "not supported");
+ enum { value =
+ bits <= 8 ? 1 :
+ bits <= 16 ? 2 :
+ bits <= 32 ? 4 :
+ bits <= 64 ? 8 :
+ 16
+ };
+ };
+ template<unsigned N> struct least_type_for_size;
+ template<> struct least_type_for_size<1> { using type = uint_least8_t; };
+ template<> struct least_type_for_size<2> { using type = uint_least16_t; };
+ template<> struct least_type_for_size<4> { using type = uint_least32_t; };
+ template<> struct least_type_for_size<8> { using type = uint_least64_t; };
+ template<> struct least_type_for_size<16> { using type = UINT128; };
- //============================================================
- // abs, lcd, gcm
- //============================================================
+ // This is different to the standard library. Mappings provided in stdint
+ // are not always fastest.
+ template<unsigned N> struct fast_type_for_size;
+ template<> struct fast_type_for_size<1> { using type = uint32_t; };
+ template<> struct fast_type_for_size<2> { using type = uint32_t; };
+ template<> struct fast_type_for_size<4> { using type = uint32_t; };
+ template<> struct fast_type_for_size<8> { using type = uint_fast64_t; };
+ template<> struct fast_type_for_size<16> { using type = UINT128; };
- template<typename T>
- constexpr
- typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type
- abs(T v)
+ template<unsigned bits>
+ struct least_type_for_bits
{
- return v < 0 ? -v : v;
- }
+ static_assert(bits <= 64 || (bits <= 128 && compile_info::has_int128::value), "not supported");
+ using type = typename least_type_for_size<size_for_bits<bits>::value>::type;
+ };
- template<typename T>
- constexpr
- typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type
- abs(T v)
+ template<unsigned bits>
+ struct fast_type_for_bits
{
- return v;
- }
+ static_assert(bits <= 64 || (bits <= 128 && compile_info::has_int128::value), "not supported");
+ using type = typename fast_type_for_size<size_for_bits<bits>::value>::type;
+ };
- template<typename M, typename N>
- constexpr typename std::common_type<M, N>::type
- gcd(M m, N n)
- {
- static_assert(std::is_integral<M>::value, "gcd: M must be an integer");
- static_assert(std::is_integral<N>::value, "gcd: N must be an integer");
+ /// \brief mark arguments as not used for compiler
+ ///
+ /// \tparam Ts unused parameters
+ ///
+ template<typename... Ts>
+ inline void unused_var(Ts&&...) noexcept {} // NOLINT(readability-named-parameter) // FIXME: remove unused var completely
- return m == 0 ? plib::abs(n)
- : n == 0 ? plib::abs(m)
- : gcd(n, m % n);
+ /// \brief copy type S to type D byte by byte
+ ///
+ /// The purpose of this copy function is to suppress compiler warnings.
+ /// Use at your own risk. This is dangerous.
+ ///
+ /// \param s Source object
+ /// \param d Destination object
+ /// \tparam S Type of source object
+ /// \tparam D Type of destination object
+ template <typename S, typename D>
+ void reinterpret_copy(S &s, D &d)
+ {
+ static_assert(sizeof(D) >= sizeof(S), "size mismatch");
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto *dp = reinterpret_cast<std::uint8_t *>(&d);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ const auto *sp = reinterpret_cast<std::uint8_t *>(&s);
+ std::copy(sp, sp + sizeof(S), dp);
}
- template<typename M, typename N>
- constexpr typename std::common_type<M, N>::type
- lcm(M m, N n)
- {
- static_assert(std::is_integral<M>::value, "lcm: M must be an integer");
- static_assert(std::is_integral<N>::value, "lcm: N must be an integer");
+ /// \brief Test if type R has a stream operator << defined
+ ///
+ /// has_ostream_operator<std::ostream, int>:: value should be true
+ ///
+ /// \tparam LEFT Stream type
+ /// \tparam RIGHT Type to check for operator overload
- return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n) : 0;
- }
+ template<class LEFT, class RIGHT>
+ struct has_ostream_operator_impl {
+ template<class V> static auto test(V*) -> decltype(std::declval<LEFT &>() << std::declval<V>());
+ template<typename> static auto test(...) -> std::false_type;
+ //static constexpr const bool value = std::is_same<LEFT &, decltype(test<RIGHT>(0))>::value;
+ using type = typename std::is_same<LEFT &, decltype(test<RIGHT>(nullptr))>::type;
+ };
+ template<class LEFT, class RIGHT>
+ struct has_ostream_operator : has_ostream_operator_impl<LEFT, RIGHT>::type {};
} // namespace plib
@@ -144,9 +417,9 @@ namespace plib
template <typename T> class name \
{ \
template <typename U> static long test(decltype(&U:: member)); \
- template <typename U> static char test(...); \
+ template <typename U> static char test(...); \
public: \
static constexpr const bool value = sizeof(test<T>(nullptr)) == sizeof(long); \
}
-#endif /* PTYPES_H_ */
+#endif // PTYPES_H_
diff --git a/src/lib/netlist/plib/putil.cpp b/src/lib/netlist/plib/putil.cpp
index a9531006904..fb430f89a68 100644
--- a/src/lib/netlist/plib/putil.cpp
+++ b/src/lib/netlist/plib/putil.cpp
@@ -1,153 +1,84 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
#include "putil.h"
-#include "plists.h"
+#include "penum.h"
+#include "pstream.h"
+#include "pstrutil.h"
#include "ptypes.h"
#include <algorithm>
-//#include <cstdlib>
-//#include <cstring>
+// needed for getenv ...
#include <initializer_list>
namespace plib
{
namespace util
{
- const pstring buildpath(std::initializer_list<pstring> list )
+ static constexpr const char PATH_SEP = compile_info::win32::value ? '\\' : '/';
+ static constexpr const char *PATH_SEPS = compile_info::win32::value ? "\\/" :"/";
+
+ pstring basename(const pstring &filename, const pstring &suffix)
{
- pstring ret = "";
- for( const auto &elem : list )
- {
- if (ret == "")
- ret = elem;
- else
- #ifdef _WIN32
- ret = ret + '\\' + elem;
- #else
- ret += ('/' + elem);
- #endif
- }
+ auto p=find_last_of(filename, pstring(PATH_SEPS));
+ pstring ret = (p == pstring::npos) ? filename : filename.substr(p+1);
+ if (!suffix.empty() && endsWith(ret, suffix))
+ return ret.substr(0, ret.length() - suffix.length());
return ret;
}
- const pstring environment(const pstring &var, const pstring &default_val)
+ pstring path(const pstring &filename)
{
- if (std::getenv(var.c_str()) == nullptr)
- return default_val;
- else
- return pstring(std::getenv(var.c_str()));
- }
- } // namespace util
+ auto p=find_last_of(filename, pstring(1, PATH_SEP));
+ if (p == pstring::npos)
+ return "";
+ if (p == 0) // root case
+ return filename.substr(0, 1);
- std::vector<pstring> psplit(const pstring &str, const pstring &onstr, bool ignore_empty)
- {
- std::vector<pstring> ret;
-
- pstring::size_type p = 0;
- pstring::size_type pn = str.find(onstr, p);
-
- while (pn != pstring::npos)
- {
- pstring t = str.substr(p, pn - p);
- if (!ignore_empty || t.length() != 0)
- ret.push_back(t);
- p = pn + onstr.length();
- pn = str.find(onstr, p);
+ return filename.substr(0, p);
}
- if (p < str.length())
+
+ bool exists (const pstring &filename)
{
- pstring t = str.substr(p);
- if (!ignore_empty || t.length() != 0)
- ret.push_back(t);
+ plib::ifstream f(filename);
+ return f.good();
}
- return ret;
- }
- std::vector<std::string> psplit_r(const std::string &stri,
- const std::string &token,
- const std::size_t maxsplit)
- {
- std::string str(stri);
- std::vector<std::string> result;
- std::size_t splits = 0;
-
- while(str.size())
+ pstring build_path(std::initializer_list<pstring> list )
{
- std::size_t index = str.rfind(token);
- bool found = index!=std::string::npos;
- if (found)
- splits++;
- if ((splits <= maxsplit || maxsplit == 0) && found)
- {
- result.push_back(str.substr(index+token.size()));
- str = str.substr(0, index);
- if (str.size()==0)
- result.push_back(str);
- }
- else
+ pstring ret = "";
+ for( const auto &elem : list )
{
- result.push_back(str);
- str = "";
+ if (ret.empty())
+ ret = elem;
+ else
+ ret += (PATH_SEP + elem);
}
+ return ret;
}
- return result;
- }
-
- std::vector<pstring> psplit(const pstring &str, const std::vector<pstring> &onstrl)
- {
- pstring col = "";
- std::vector<pstring> ret;
- auto i = str.begin();
- while (i != str.end())
+ pstring environment(const pstring &var, const pstring &default_val)
{
- auto p = static_cast<std::size_t>(-1);
- for (std::size_t j=0; j < onstrl.size(); j++)
- {
- if (std::equal(onstrl[j].begin(), onstrl[j].end(), i))
- {
- p = j;
- break;
- }
- }
- if (p != static_cast<std::size_t>(-1))
- {
- if (col != "")
- ret.push_back(col);
-
- col = "";
- ret.push_back(onstrl[p]);
- i = std::next(i, static_cast<pstring::difference_type>(onstrl[p].length()));
- }
- else
- {
- pstring::value_type c = *i;
- col += c;
- i++;
- }
+ putf8string varu8(var);
+ return (std::getenv(varu8.c_str()) == nullptr) ? default_val
+ : pstring(std::getenv(varu8.c_str()));
}
- if (col != "")
- ret.push_back(col);
-
- return ret;
- }
-
+ } // namespace util
- int penum_base::from_string_int(const char *str, const char *x)
+ int penum_base::from_string_int(const pstring &str, const pstring &x)
{
int cnt = 0;
- for (auto &s : psplit(str, ",", false))
+ for (auto &s : psplit(str, ',', false))
{
- if (s == x)
+ if (trim(s) == x)
return cnt;
cnt++;
}
return -1;
}
- std::string penum_base::nthstr(int n, const char *str)
+ pstring penum_base::nthstr(std::size_t n, const pstring &str)
{
- return psplit(str, ",", false)[static_cast<std::size_t>(n)];
+ return psplit(str, ',', false)[n];
}
} // namespace plib
diff --git a/src/lib/netlist/plib/putil.h b/src/lib/netlist/plib/putil.h
index e6e6153d840..ae383aaf807 100644
--- a/src/lib/netlist/plib/putil.h
+++ b/src/lib/netlist/plib/putil.h
@@ -1,33 +1,121 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * putil.h
- *
- */
#ifndef PUTIL_H_
#define PUTIL_H_
+///
+/// \file putil.h
+///
+
+#include "palloc.h"
#include "pexception.h"
#include "pstring.h"
+
#include <algorithm>
#include <initializer_list>
-#include <locale>
#include <sstream>
#include <vector>
-#include <iostream>
-#define PSTRINGIFY_HELP(y) # y
+#define PSTRINGIFY_HELP(y) #y
#define PSTRINGIFY(x) PSTRINGIFY_HELP(x)
+// Discussion and background of this MSVC bug:
+// https://github.com/mamedev/mame/issues/6106
+///
+/// \brief Macro to work around a bug in MSVC treatment of __VA_ARGS__
+///
+#define PMSVC_VARARG_BUG(MACRO, ARGS) MACRO ARGS
+
+// clang-format off
+
+/// \brief Determine number of arguments in __VA_ARGS__
+///
+/// This macro works up to 16 arguments in __VA_ARGS__
+///
+/// \returns Number of arguments
+///
+#define PNARGS(...) PNARGS_1(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+#define PNARGS_2(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
+#define PNARGS_1(...) PMSVC_VARARG_BUG(PNARGS_2, (__VA_ARGS__))
+
+/// \brief Concatenate two arguments after expansion
+///
+/// \returns Concatenated expanded arguments
+///
+#define PCONCAT(a, b) PCONCAT_(a, b)
+
+#define PCONCAT_(a, b) a ## b
+
+#define PSTRINGIFY_1(x) #x
+#define PSTRINGIFY_2(x, x2) #x, #x2
+#define PSTRINGIFY_3(x, x2, x3) #x, #x2, #x3
+#define PSTRINGIFY_4(x, x2, x3, x4) #x, #x2, #x3, #x4
+#define PSTRINGIFY_5(x, x2, x3, x4, x5) #x, #x2, #x3, #x4, #x5
+#define PSTRINGIFY_6(x, x2, x3, x4, x5, x6) #x, #x2, #x3, #x4, #x5, #x6
+#define PSTRINGIFY_7(x, x2, x3, x4, x5, x6, x7) #x, #x2, #x3, #x4, #x5, #x6, #x7
+#define PSTRINGIFY_8(x, x2, x3, x4, x5, x6, x7, x8) #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8
+#define PSTRINGIFY_9(x, x2, x3, x4, x5, x6, x7, x8, x9) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9
+#define PSTRINGIFY_10(x, x2, x3, x4, x5, x6, x7, x8, x9, xa) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa
+#define PSTRINGIFY_11(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb
+#define PSTRINGIFY_12(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb, #xc
+#define PSTRINGIFY_13(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb, #xc, #xd
+#define PSTRINGIFY_14(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb, #xc, #xd, #xe
+#define PSTRINGIFY_15(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb, #xc, #xd, #xe, #xf
+#define PSTRINGIFY_16(x, x2, x3, x4, x5, x6, x7, x8, x9, xa, xb, xc, xd, xe, xf, x10) \
+ #x, #x2, #x3, #x4, #x5, #x6, #x7, #x8, #x9, #xa, #xb, #xc, #xd, #xe, #xf, #x10
+
+// clang-format on
+
+/// \brief Individually stringify up to 16 arguments
+///
+/// PSTRINGIFY_VA(a, b, c) will be expanded to "a", "b", "c"
+///
+/// \returns List of stringified individual arguments
+///
+#define PSTRINGIFY_VA(...) \
+ PMSVC_VARARG_BUG(PCONCAT, (PSTRINGIFY_, PNARGS(__VA_ARGS__)))(__VA_ARGS__)
+
+/// \brief Dispatch VARARG macro to specialized macros
+///
+/// ```
+/// #define LOCAL_LIB_ENTRY(...) PCALLVARARG(LOCAL_LIB_ENTRY_, __VA_ARGS__)
+/// ```
+///
+/// Will pass varargs depending on number of arguments to
+///
+/// ```
+/// LOCAL_LIB_ENTRY_1(a1)
+/// LOCAL_LIB_ENTRY_2(a1 , a2)
+/// ```
+///
+/// \returns result of specialized macro
+///
+#define PCALLVARARG(MAC, ...) \
+ PMSVC_VARARG_BUG(PCONCAT, (MAC, PNARGS(__VA_ARGS__)))(__VA_ARGS__)
+
+// FIXME:: __FUNCTION__ may be not be supported by all compilers.
+
+#define PSOURCELOC() plib::source_location(__FILE__, __LINE__)
namespace plib
{
namespace util
{
- const pstring buildpath(std::initializer_list<pstring> list );
- const pstring environment(const pstring &var, const pstring &default_val);
+ pstring basename(const pstring &filename, const pstring &suffix = "");
+ pstring path(const pstring &filename);
+ bool exists(const pstring &filename);
+ pstring build_path(std::initializer_list<pstring> list);
+ pstring environment(const pstring &var, const pstring &default_val);
} // namespace util
namespace container
@@ -40,18 +128,19 @@ namespace plib
static constexpr const std::size_t npos = static_cast<std::size_t>(-1);
template <class C>
- std::size_t indexof(C &con, const typename C::value_type &elem)
+ std::size_t index_of(C &con, const typename C::value_type &elem)
{
auto it = std::find(con.begin(), con.end(), elem);
if (it != con.end())
- return static_cast<std::size_t>(it - con.begin());
+ return narrow_cast<std::size_t>(it - con.begin());
return npos;
}
template <class C>
- void insert_at(C &con, const std::size_t index, const typename C::value_type &elem)
+ void insert_at(C &con, const std::size_t index,
+ const typename C::value_type &elem)
{
- con.insert(con.begin() + static_cast<std::ptrdiff_t>(index), elem);
+ con.insert(con.begin() + narrow_cast<std::ptrdiff_t>(index), elem);
}
template <class C>
@@ -61,198 +150,72 @@ namespace plib
}
} // namespace container
- /* May be further specialized .... This is the generic version */
- template <typename T>
- struct constants
- {
- static constexpr T zero() noexcept { return static_cast<T>(0); }
- static constexpr T one() noexcept { return static_cast<T>(1); }
- static constexpr T two() noexcept { return static_cast<T>(2); }
- static constexpr T sqrt2() noexcept { return static_cast<T>(1.414213562373095048801688724209); }
- static constexpr T pi() noexcept { return static_cast<T>(3.14159265358979323846264338327950); }
-
- /*!
- * \brief Electric constant of vacuum
- */
- static constexpr T eps_0() noexcept { return static_cast<T>(8.854187817e-12); }
- /*!
- * \brief Relative permittivity of Silicon dioxide
- */
- static constexpr T eps_SiO2() noexcept { return static_cast<T>(3.9); }
- /*!
- * \brief Relative permittivity of Silicon
- */
- static constexpr T eps_Si() noexcept { return static_cast<T>(11.7); }
- /*!
- * \brief Boltzmann constant
- */
- static constexpr T k_b() noexcept { return static_cast<T>(1.38064852e-23); }
- /*!
- * \brief room temperature (gives VT = 0.02585 at T=300)
- */
- static constexpr T T0() noexcept { return static_cast<T>(300); }
- /*!
- * \brief Elementary charge
- */
- static constexpr T Q_e() noexcept { return static_cast<T>(1.6021765314e-19); }
- /*!
- * \brief Intrinsic carrier concentration in 1/m^3 of Silicon
- */
- static constexpr T NiSi() noexcept { return static_cast<T>(1.45e16); }
-
- template <typename V>
- static constexpr const T cast(V &&v) noexcept { return static_cast<T>(v); }
- };
-
- static_assert(noexcept(constants<double>::one()) == true, "Not evaluated as constexpr");
-
-
- template <class C>
- struct indexed_compare
+ /// \brief Consistent hash implementation
+ ///
+ /// Hash implementations in c++ standard libraries may differ and deliver
+ /// different results. This hash can be used as a replacement hash in e.g.
+ /// std::map to deliver consistent results.
+ ///
+ /// \tparam V result type
+ /// \tparam T buffer element type
+ /// \param buf pointer to buffer for which hash should be calculated
+ /// \param size number of buffer elements
+ /// \return the hash of the buffer
+ ///
+ template <typename V, typename T>
+ constexpr V hash(const T *buf, std::size_t size) noexcept
{
- explicit indexed_compare(const C& target): m_target(target) {}
-
- bool operator()(int a, int b) const { return m_target[a] < m_target[b]; }
-
- const C& m_target;
- };
-
- // ----------------------------------------------------------------------------------------
- // string list
- // ----------------------------------------------------------------------------------------
-
- std::vector<pstring> psplit(const pstring &str, const pstring &onstr, bool ignore_empty = false);
- std::vector<pstring> psplit(const pstring &str, const std::vector<pstring> &onstrl);
- std::vector<std::string> psplit_r(const std::string &stri,
- const std::string &token,
- const std::size_t maxsplit);
-
- // ----------------------------------------------------------------------------------------
- // number conversions
- // ----------------------------------------------------------------------------------------
-
- template <typename T, typename S>
- T pstonum_locale(const std::locale &loc, const S &arg, std::size_t *idx)
- {
- std::stringstream ss;
- ss.imbue(loc);
- ss << arg;
- auto len(ss.tellp());
- T x(constants<T>::zero());
- if (ss >> x)
- {
- auto pos(ss.tellg());
- if (pos == static_cast<decltype(pos)>(-1))
- pos = len;
- *idx = static_cast<std::size_t>(pos);
- }
- else
- *idx = constants<std::size_t>::zero();
- //printf("%s, %f, %lu %ld\n", arg, (double)x, *idx, (long int) ss.tellg());
- return x;
+ V result = 5381; // NOLINT
+ for (const T *p = buf; p != buf + size; p++)
+ result = ((result << 5) + result) ^ (result >> (32 - 5))
+ ^ narrow_cast<std::size_t>(*p); // NOLINT
+ return result;
}
- template <typename T, typename E = void>
- struct pstonum_helper;
-
- template<typename T>
- struct pstonum_helper<T, typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value>::type>
+ /// \brief Execute code at end of block
+ ///
+ /// The class can be used to execute code at the end of a block. It follows
+ /// the same design as std::lock_guard.
+ ///
+ /// Since the functor is executed in the destructor of the class the
+ /// execution is protected by a try catch block. If an exception is raised,
+ /// \ref plib::terminate is called.
+ ///
+ /// \tparam F type of functor - will be derived by template deduction guide
+ ///
+ template <typename F>
+ struct functor_guard
{
- template <typename S>
- long long operator()(std::locale loc, const S &arg, std::size_t *idx)
+ /// \brief constructor
+ ///
+ /// \param f functor to execute
+ functor_guard(F &&f)
+ : m_f(std::move(f))
{
- //return std::stoll(arg, idx);
- return pstonum_locale<long long>(loc, arg, idx);
}
- };
-
- template<typename T>
- struct pstonum_helper<T, typename std::enable_if<std::is_integral<T>::value && !std::is_signed<T>::value>::type>
- {
- template <typename S>
- unsigned long long operator()(std::locale loc, const S &arg, std::size_t *idx)
+ /// \brief destructor
+ ///
+ ~functor_guard()
{
- //return std::stoll(arg, idx);
- return pstonum_locale<unsigned long long>(loc, arg, idx);
+ try
+ {
+ m_f();
+ }
+ catch (...)
+ {
+ plib::terminate("exception raised in lambda_guard");
+ }
}
- };
- template<typename T>
- struct pstonum_helper<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
- {
- template <typename S>
- long double operator()(std::locale loc, const S &arg, std::size_t *idx)
- {
- return pstonum_locale<long double>(loc, arg, idx);
- }
+ private:
+ F m_f;
};
- template<typename T, typename S>
- T pstonum(const S &arg, std::locale loc = std::locale::classic())
- {
- decltype(arg.c_str()) cstr = arg.c_str();
- std::size_t idx(0);
- auto ret = pstonum_helper<T>()(loc, cstr, &idx);
- using ret_type = decltype(ret);
- if (ret >= static_cast<ret_type>(std::numeric_limits<T>::lowest())
- && ret <= static_cast<ret_type>(std::numeric_limits<T>::max()))
- //&& (ret == T(0) || std::abs(ret) >= std::numeric_limits<T>::min() ))
- {
- if (cstr[idx] != 0)
- throw pexception(pstring("Continuation after numeric value ends: ") + cstr);
- }
- else
- {
- throw pexception(pstring("Out of range: ") + cstr);
- }
- return static_cast<T>(ret);
- }
-
- template<typename R, bool CLOCALE, typename T>
- R pstonum_ne(const T &str, bool &err, std::locale loc = std::locale::classic()) noexcept
- {
- try
- {
- err = false;
- return pstonum<R>(str, loc);
- }
- catch (...)
- {
- err = true;
- return R(0);
- }
- }
-
- //============================================================
- // penum - strongly typed enumeration
- //============================================================
-
- struct penum_base
- {
- protected:
- static int from_string_int(const char *str, const char *x);
- static std::string nthstr(int n, const char *str);
- };
+ /// \brief template deduction guide
+ ///
+ template <class F>
+ functor_guard(F f) -> functor_guard<F>;
} // namespace plib
-#define P_ENUM(ename, ...) \
- struct ename : public plib::penum_base { \
- enum E { __VA_ARGS__ }; \
- ename (E v) : m_v(v) { } \
- bool set_from_string (const std::string &s) { \
- static char const *const strings = # __VA_ARGS__; \
- int f = from_string_int(strings, s.c_str()); \
- if (f>=0) { m_v = static_cast<E>(f); return true; } else { return false; } \
- } \
- operator E() const {return m_v;} \
- bool operator==(const ename &rhs) const {return m_v == rhs.m_v;} \
- bool operator==(const E &rhs) const {return m_v == rhs;} \
- std::string name() const { \
- static char const *const strings = # __VA_ARGS__; \
- return nthstr(static_cast<int>(m_v), strings); \
- } \
- private: E m_v; };
-
-
-#endif /* PUTIL_H_ */
+#endif // PUTIL_H_
diff --git a/src/lib/netlist/plib/vector_ops.h b/src/lib/netlist/plib/vector_ops.h
index bdeb24ae502..f669f6a29d9 100644
--- a/src/lib/netlist/plib/vector_ops.h
+++ b/src/lib/netlist/plib/vector_ops.h
@@ -1,20 +1,21 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * vector_ops.h
- *
- * Base vector operations
- *
- */
#ifndef PLIB_VECTOR_OPS_H_
#define PLIB_VECTOR_OPS_H_
+///
+/// \file vector_ops.h
+///
+/// Base vector operations
+///
+///
#include "pconfig.h"
+#include "pgsl.h"
+#include "pmath.h"
#include <algorithm>
#include <array>
-#include <cmath>
#include <type_traits>
#if !defined(__clang__) && !defined(_MSC_VER) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
@@ -27,117 +28,123 @@
namespace plib
{
template<typename VT, typename T>
- void vec_set_scalar(const std::size_t n, VT &v, T && scalar)
+ void vec_set_scalar(VT &v, T && scalar) noexcept
{
+ const std::size_t n(v.size());
const typename std::remove_reference<decltype(v[0])>::type s(std::forward<T>(scalar));
for ( std::size_t i = 0; i < n; i++ )
v[i] = s;
}
template<typename VT, typename VS>
- void vec_set(const std::size_t n, VT &v, const VS & source)
+ void vec_set(VT &v, const VS & source) noexcept
{
+ const std::size_t n(v.size());
+ gsl_Expects(n <= source.size());
for ( std::size_t i = 0; i < n; i++ )
v[i] = source[i];
}
template<typename T, typename V1, typename V2>
- T vec_mult(const std::size_t n, const V1 & v1, const V2 & v2 )
+ T vec_mult(const V1 & v1, const V2 & v2 ) noexcept
{
- using b8 = std::array<T, 8>;
- PALIGNAS_VECTOROPT() b8 value = {0};
+ const std::size_t n(v1.size());
+ gsl_Expects(n <= v2.size());
+ T ret(plib::constants<T>::zero());
for (std::size_t i = 0; i < n ; i++ )
{
- value[i & 7] += v1[i] * v2[i];
+ ret += v1[i] * v2[i]; // NOLINT
}
- return value[0] + value[1] + value[2] + value[3] + value[4] + value[5] + value[6] + value[7];
+ return ret;
}
template<typename T, typename VT>
- T vec_mult2(const std::size_t n, const VT &v)
+ T vec_mult2(const VT &v) noexcept
{
- using b8 = std::array<T, 8>;
- PALIGNAS_VECTOROPT() b8 value = {0};
+ const std::size_t n(v.size());
+ T ret(plib::constants<T>::zero());
for (std::size_t i = 0; i < n ; i++ )
{
- value[i & 7] += v[i] * v[i];
+ ret += v[i] * v[i];
}
- return value[0] + value[1] + value[2] + value[3] + value[4] + value[5] + value[6] + value[7];
+ return ret;
}
template<typename T, typename VT>
- T vec_sum(const std::size_t n, const VT &v)
+ T vec_sum(const VT &v) noexcept
{
- if (n<8)
+ const std::size_t n(v.size());
+ T ret(plib::constants<T>::zero());
+ for (std::size_t i = 0; i < n ; i++ )
{
- T value(0);
- for (std::size_t i = 0; i < n ; i++ )
- value += v[i];
-
- return value;
+ ret += v[i];
}
- else
- {
- using b8 = std::array<T, 8>;
- PALIGNAS_VECTOROPT() b8 value = {0};
- for (std::size_t i = 0; i < n ; i++ )
- value[i & 7] += v[i];
- return ((value[0] + value[1]) + (value[2] + value[3])) + ((value[4] + value[5]) + (value[6] + value[7]));
- }
+ return ret;
}
template<typename VV, typename T, typename VR>
- void vec_mult_scalar(const std::size_t n, VR & result, const VV & v, T && scalar)
+ void vec_mult_scalar(VR & result, const VV & v, T && scalar) noexcept
{
+ const std::size_t n(result.size());
+ gsl_Expects(n <= v.size());
const typename std::remove_reference<decltype(v[0])>::type s(std::forward<T>(scalar));
for ( std::size_t i = 0; i < n; i++ )
result[i] = s * v[i];
}
template<typename VR, typename VV, typename T>
- void vec_add_mult_scalar(const std::size_t n, VR & result, const VV & v, T && scalar)
+ void vec_add_mult_scalar(VR & result, const VV & v, T && scalar) noexcept
{
+ const std::size_t n(result.size());
+ gsl_Expects(n <= v.size());
const typename std::remove_reference<decltype(v[0])>::type s(std::forward<T>(scalar));
for ( std::size_t i = 0; i < n; i++ )
result[i] += s * v[i];
}
template<typename T>
- void vec_add_mult_scalar_p(const std::size_t n, T * result, const T * v, T scalar)
+ void vec_add_mult_scalar_p(const std::size_t n, T * result, const T * v, T scalar) noexcept
{
for ( std::size_t i = 0; i < n; i++ )
result[i] += scalar * v[i];
}
template<typename R, typename V>
- void vec_add_ip(const std::size_t n, R & result, const V & v)
+ void vec_add_ip(R & result, const V & v) noexcept
{
+ const std::size_t n(result.size());
+ gsl_Expects(n <= v.size());
for ( std::size_t i = 0; i < n; i++ )
result[i] += v[i];
}
template<typename VR, typename V1, typename V2>
- void vec_sub(const std::size_t n, VR & result, const V1 &v1, const V2 & v2)
+ void vec_sub(VR & result, const V1 &v1, const V2 & v2) noexcept
{
+ const std::size_t n(result.size());
+ gsl_Expects(n <= v1.size());
+ gsl_Expects(n <= v2.size());
for ( std::size_t i = 0; i < n; i++ )
result[i] = v1[i] - v2[i];
}
template<typename V, typename T>
- void vec_scale(const std::size_t n, V & v, T &&scalar)
+ void vec_scale(V & v, T &&scalar) noexcept
{
+ const std::size_t n(v.size());
const typename std::remove_reference<decltype(v[0])>::type s(std::forward<T>(scalar));
for ( std::size_t i = 0; i < n; i++ )
v[i] *= s;
}
template<typename T, typename V>
- T vec_maxabs(const std::size_t n, const V & v)
+ T vec_max_abs(const V & v) noexcept
{
- T ret = 0.0;
+ const std::size_t n(v.size());
+ T ret(plib::constants<T>::zero());
for ( std::size_t i = 0; i < n; i++ )
- ret = std::max(ret, std::abs(v[i]));
+ ret = std::max(ret, plib::abs(v[i]));
return ret;
}
@@ -149,4 +156,4 @@ namespace plib
#endif
#endif
-#endif /* PLIB_VECTOR_OPS_H_ */
+#endif // PLIB_VECTOR_OPS_H_