diff options
Diffstat (limited to 'src/lib/netlist/plib')
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(¶ms); + })).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_ |