// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated symbol inspection header
///
/// This file is for inclusion by internal files that need to inspect
/// specific symbols.  Applications typically use the VPI instead.
///
/// User wrapper code wanting to inspect the symbol table should use
/// verilated_syms.h instead.
///
//*************************************************************************
// These classes are thread safe, and read only.

#ifndef VERILATOR_VERILATED_SYM_PROPS_H_
#define VERILATOR_VERILATED_SYM_PROPS_H_

#include "verilatedos.h"

#include <vector>

//===========================================================================
// Verilator range
// Thread safety: Assume is constructed only with model, then any number of readers

// See also V3Ast::VNumRange
class VerilatedRange final {
    int m_left = 0;
    int m_right = 0;

protected:
    friend class VerilatedVarProps;
    friend class VerilatedScope;
    VerilatedRange() = default;
    void init(int left, int right) {
        m_left = left;
        m_right = right;
    }

public:
    VerilatedRange(int left, int right)
        : m_left{left}
        , m_right{right} {}
    ~VerilatedRange() = default;
    int left() const { return m_left; }
    int right() const { return m_right; }
    int low() const { return (m_left < m_right) ? m_left : m_right; }
    int high() const { return (m_left > m_right) ? m_left : m_right; }
    int elements() const {
        return (VL_LIKELY(m_left >= m_right) ? (m_left - m_right + 1) : (m_right - m_left + 1));
    }
    int increment() const { return (m_left >= m_right) ? 1 : -1; }
};

//===========================================================================
// Verilator variable
// Thread safety: Assume is constructed only with model, then any number of readers

class VerilatedVarProps VL_NOT_FINAL {
    // TYPES
    static constexpr vluint32_t MAGIC = 0xddc4f829UL;
    // MEMBERS
    const vluint32_t m_magic;  // Magic number
    const VerilatedVarType m_vltype;  // Data type
    const VerilatedVarFlags m_vlflags;  // Direction
    const int m_pdims;  // Packed dimensions, 0 = none
    const int m_udims;  // Unpacked dimensions, 0 = none
    VerilatedRange m_packed;  // Packed array range
    std::vector<VerilatedRange> m_unpacked;  // Unpacked array ranges
    void initUnpacked(const int* ulims) {
        for (int i = 0; i < m_udims; ++i) {
            const int uleft = ulims ? ulims[2 * i + 0] : 0;
            const int uright = ulims ? ulims[2 * i + 1] : 0;
            m_unpacked.emplace_back(uleft, uright);
        }
    }
    // CONSTRUCTORS
protected:
    friend class VerilatedScope;
    VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int pdims, int udims)
        : m_magic{MAGIC}
        , m_vltype{vltype}
        , m_vlflags{vlflags}
        , m_pdims{pdims}
        , m_udims{udims} {
        initUnpacked(nullptr);
    }

public:
    class Unpacked {};
    // Without packed
    VerilatedVarProps(VerilatedVarType vltype, int vlflags)
        : m_magic{MAGIC}
        , m_vltype{vltype}
        , m_vlflags(VerilatedVarFlags(vlflags))  // Need () or GCC 4.8 false warning
        , m_pdims{0}
        , m_udims{0} {}
    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
        : m_magic{MAGIC}
        , m_vltype{vltype}
        , m_vlflags(VerilatedVarFlags(vlflags))  // Need () or GCC 4.8 false warning
        , m_pdims{0}
        , m_udims{udims} {
        initUnpacked(ulims);
    }
    // With packed
    class Packed {};
    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr)
        : m_magic{MAGIC}
        , m_vltype{vltype}
        , m_vlflags(VerilatedVarFlags(vlflags))  // Need () or GCC 4.8 false warning
        , m_pdims{1}
        , m_udims{0}
        , m_packed{pl, pr} {}
    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked,
                      int udims, const int* ulims)
        : m_magic{MAGIC}
        , m_vltype{vltype}
        , m_vlflags(VerilatedVarFlags(vlflags))  // Need () or GCC 4.8 false warning
        , m_pdims{1}
        , m_udims{udims}
        , m_packed{pl, pr} {
        initUnpacked(ulims);
    }

public:
    ~VerilatedVarProps() = default;
    // METHODS
    bool magicOk() const { return m_magic == MAGIC; }
    VerilatedVarType vltype() const { return m_vltype; }
    VerilatedVarFlags vldir() const {
        return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
    }
    vluint32_t entSize() const;
    bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
    // DPI compatible C standard layout
    bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
    int udims() const { return m_udims; }
    int dims() const { return m_pdims + m_udims; }
    const VerilatedRange& packed() const { return m_packed; }
    const VerilatedRange& unpacked() const { return m_unpacked[0]; }
    // DPI accessors
    int left(int dim) const {
        return dim == 0 ? m_packed.left()
                        : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0;
    }
    int right(int dim) const {
        return dim == 0 ? m_packed.right()
                        : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0;
    }
    int low(int dim) const {
        return dim == 0 ? m_packed.low()
                        : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0;
    }
    int high(int dim) const {
        return dim == 0 ? m_packed.high()
                        : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0;
    }
    int increment(int dim) const {
        return dim == 0
                   ? m_packed.increment()
                   : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0;
    }
    int elements(int dim) const {
        return dim == 0
                   ? m_packed.elements()
                   : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0;
    }
    // Total size in bytes (note DPI limited to 4GB)
    size_t totalSize() const;
    // Adjust a data pointer to access a given array element, NuLL if something goes bad
    void* datapAdjustIndex(void* datap, int dim, int indx) const;
};

//===========================================================================
// Verilator DPI open array variable

class VerilatedDpiOpenVar final {
    // MEMBERS
    const VerilatedVarProps* m_propsp;  // Variable properties
    void* m_datap;  // Location of data (local to thread always, so safe)
public:
    // CONSTRUCTORS
    VerilatedDpiOpenVar(const VerilatedVarProps* propsp, void* datap)
        : m_propsp{propsp}
        , m_datap{datap} {}
    VerilatedDpiOpenVar(const VerilatedVarProps* propsp, const void* datap)
        : m_propsp{propsp}
        , m_datap{const_cast<void*>(datap)} {}
    ~VerilatedDpiOpenVar() = default;
    // METHODS
    void* datap() const { return m_datap; }
    // METHODS - from VerilatedVarProps
    bool magicOk() const { return m_propsp->magicOk(); }
    VerilatedVarType vltype() const { return m_propsp->vltype(); }
    bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); }
    const VerilatedRange& packed() const { return m_propsp->packed(); }
    const VerilatedRange& unpacked() const { return m_propsp->unpacked(); }
    int udims() const { return m_propsp->udims(); }
    int left(int dim) const { return m_propsp->left(dim); }
    int right(int dim) const { return m_propsp->right(dim); }
    int low(int dim) const { return m_propsp->low(dim); }
    int high(int dim) const { return m_propsp->high(dim); }
    int increment(int dim) const { return m_propsp->increment(dim); }
    int elements(int dim) const { return m_propsp->elements(dim); }
    size_t totalSize() const { return m_propsp->totalSize(); }
    void* datapAdjustIndex(void* datap, int dim, int indx) const {
        return m_propsp->datapAdjustIndex(datap, dim, indx);
    }
};

//===========================================================================
// Verilator variable
// Thread safety: Assume is constructed only with model, then any number of readers

class VerilatedVar final : public VerilatedVarProps {
    // MEMBERS
    void* m_datap;  // Location of data
    const char* m_namep;  // Name - slowpath
protected:
    bool m_isParam;
    friend class VerilatedScope;
    // CONSTRUCTORS
    VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
                 VerilatedVarFlags vlflags, int dims, bool isParam)
        : VerilatedVarProps{vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0)}
        , m_datap{datap}
        , m_namep{namep}
        , m_isParam{isParam} {}

public:
    ~VerilatedVar() = default;
    // ACCESSORS
    void* datap() const { return m_datap; }
    const VerilatedRange& range() const { return packed(); }  // Deprecated
    const VerilatedRange& array() const { return unpacked(); }  // Deprecated
    const char* name() const { return m_namep; }
    bool isParam() const { return m_isParam; }
};

#endif  // Guard
