Merge 86239498bd
into fe7cfddd29
This commit is contained in:
commit
55bf25fc57
9 changed files with 101 additions and 9 deletions
|
@ -4,6 +4,7 @@ project(xmrig)
|
||||||
option(WITH_LIBCPUID "Use Libcpuid" ON)
|
option(WITH_LIBCPUID "Use Libcpuid" ON)
|
||||||
option(WITH_AEON "CryptoNight-Lite support" ON)
|
option(WITH_AEON "CryptoNight-Lite support" ON)
|
||||||
option(WITH_SUMO "CryptoNight-Heavy support" ON)
|
option(WITH_SUMO "CryptoNight-Heavy support" ON)
|
||||||
|
option(WITH_IPBC "CryptoNight-IPBC support" ON)
|
||||||
option(WITH_HTTPD "HTTP REST API" ON)
|
option(WITH_HTTPD "HTTP REST API" ON)
|
||||||
option(BUILD_STATIC "Build static binary" OFF)
|
option(BUILD_STATIC "Build static binary" OFF)
|
||||||
|
|
||||||
|
@ -205,6 +206,10 @@ if (NOT WITH_SUMO)
|
||||||
add_definitions(/DXMRIG_NO_SUMO)
|
add_definitions(/DXMRIG_NO_SUMO)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (NOT WITH_IPBC)
|
||||||
|
add_definitions(/DXMRIG_NO_IPBC)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WITH_HTTPD)
|
if (WITH_HTTPD)
|
||||||
find_package(MHD)
|
find_package(MHD)
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ enum Algo {
|
||||||
CRYPTONIGHT, /* CryptoNight (Monero) */
|
CRYPTONIGHT, /* CryptoNight (Monero) */
|
||||||
CRYPTONIGHT_LITE, /* CryptoNight-Lite (AEON) */
|
CRYPTONIGHT_LITE, /* CryptoNight-Lite (AEON) */
|
||||||
CRYPTONIGHT_HEAVY, /* CryptoNight-Heavy (SUMO) */
|
CRYPTONIGHT_HEAVY, /* CryptoNight-Heavy (SUMO) */
|
||||||
|
CRYPTONIGHT_IPBC, /* CryptoNight-Lite (IPBC) */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,10 @@ Options:\n\
|
||||||
"\
|
"\
|
||||||
cryptonight-heavy\n"
|
cryptonight-heavy\n"
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef XMRIG_NO_IPBC
|
||||||
|
"\
|
||||||
|
cryptonight-ipbc\n"
|
||||||
|
#endif
|
||||||
"\
|
"\
|
||||||
-o, --url=URL URL of mining server\n\
|
-o, --url=URL URL of mining server\n\
|
||||||
-O, --userpass=U:P username:password pair for mining server\n\
|
-O, --userpass=U:P username:password pair for mining server\n\
|
||||||
|
|
|
@ -47,11 +47,15 @@ constexpr const size_t CRYPTONIGHT_HEAVY_MEMORY = 4 * 1024 * 1024;
|
||||||
constexpr const uint32_t CRYPTONIGHT_HEAVY_MASK = 0x3FFFF0;
|
constexpr const uint32_t CRYPTONIGHT_HEAVY_MASK = 0x3FFFF0;
|
||||||
constexpr const uint32_t CRYPTONIGHT_HEAVY_ITER = 0x40000;
|
constexpr const uint32_t CRYPTONIGHT_HEAVY_ITER = 0x40000;
|
||||||
|
|
||||||
|
constexpr const size_t CRYPTONIGHT_IPBC_MEMORY = 1 * 1024 * 1024;
|
||||||
|
constexpr const uint32_t CRYPTONIGHT_IPBC_MASK = 0xFFFF0;
|
||||||
|
constexpr const uint32_t CRYPTONIGHT_IPBC_ITER = 0x40000;
|
||||||
|
|
||||||
template<Algo ALGO> inline constexpr size_t cn_select_memory() { return 0; }
|
template<Algo ALGO> inline constexpr size_t cn_select_memory() { return 0; }
|
||||||
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT>() { return CRYPTONIGHT_MEMORY; }
|
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT>() { return CRYPTONIGHT_MEMORY; }
|
||||||
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_MEMORY; }
|
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_MEMORY; }
|
||||||
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_MEMORY; }
|
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_MEMORY; }
|
||||||
|
template<> inline constexpr size_t cn_select_memory<CRYPTONIGHT_IPBC>() { return CRYPTONIGHT_IPBC_MEMORY; }
|
||||||
|
|
||||||
inline size_t cn_select_memory(Algo algorithm)
|
inline size_t cn_select_memory(Algo algorithm)
|
||||||
{
|
{
|
||||||
|
@ -66,6 +70,9 @@ inline size_t cn_select_memory(Algo algorithm)
|
||||||
case CRYPTONIGHT_HEAVY:
|
case CRYPTONIGHT_HEAVY:
|
||||||
return CRYPTONIGHT_HEAVY_MEMORY;
|
return CRYPTONIGHT_HEAVY_MEMORY;
|
||||||
|
|
||||||
|
case CRYPTONIGHT_IPBC:
|
||||||
|
return CRYPTONIGHT_IPBC_MEMORY;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +85,7 @@ template<Algo ALGO> inline constexpr uint32_t cn_select_mask() { retur
|
||||||
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT>() { return CRYPTONIGHT_MASK; }
|
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT>() { return CRYPTONIGHT_MASK; }
|
||||||
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_MASK; }
|
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_MASK; }
|
||||||
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_MASK; }
|
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_MASK; }
|
||||||
|
template<> inline constexpr uint32_t cn_select_mask<CRYPTONIGHT_IPBC>() { return CRYPTONIGHT_IPBC_MASK; }
|
||||||
|
|
||||||
inline uint32_t cn_select_mask(Algo algorithm)
|
inline uint32_t cn_select_mask(Algo algorithm)
|
||||||
{
|
{
|
||||||
|
@ -92,6 +100,9 @@ inline uint32_t cn_select_mask(Algo algorithm)
|
||||||
case CRYPTONIGHT_HEAVY:
|
case CRYPTONIGHT_HEAVY:
|
||||||
return CRYPTONIGHT_HEAVY_MASK;
|
return CRYPTONIGHT_HEAVY_MASK;
|
||||||
|
|
||||||
|
case CRYPTONIGHT_IPBC:
|
||||||
|
return CRYPTONIGHT_IPBC_MASK;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +115,7 @@ template<Algo ALGO> inline constexpr uint32_t cn_select_iter() { retur
|
||||||
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT>() { return CRYPTONIGHT_ITER; }
|
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT>() { return CRYPTONIGHT_ITER; }
|
||||||
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_ITER; }
|
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT_LITE>() { return CRYPTONIGHT_LITE_ITER; }
|
||||||
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_ITER; }
|
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT_HEAVY>() { return CRYPTONIGHT_HEAVY_ITER; }
|
||||||
|
template<> inline constexpr uint32_t cn_select_iter<CRYPTONIGHT_IPBC>() { return CRYPTONIGHT_IPBC_ITER; }
|
||||||
|
|
||||||
inline uint32_t cn_select_iter(Algo algorithm)
|
inline uint32_t cn_select_iter(Algo algorithm)
|
||||||
{
|
{
|
||||||
|
@ -118,6 +130,9 @@ inline uint32_t cn_select_iter(Algo algorithm)
|
||||||
case CRYPTONIGHT_HEAVY:
|
case CRYPTONIGHT_HEAVY:
|
||||||
return CRYPTONIGHT_HEAVY_ITER;
|
return CRYPTONIGHT_HEAVY_ITER;
|
||||||
|
|
||||||
|
case CRYPTONIGHT_IPBC:
|
||||||
|
return CRYPTONIGHT_IPBC_ITER;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,5 +130,19 @@ const static uint8_t test_output_heavy[160] = {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef XMRIG_NO_IPBC
|
||||||
|
const static uint8_t test_output_ipbc[160] = {
|
||||||
|
0xe4, 0x93, 0x8c, 0xaa, 0x59, 0x8d, 0x02, 0x8a, 0xb8, 0x6f, 0x25, 0xd2, 0xb1, 0x23, 0xd0, 0xd5,
|
||||||
|
0x33, 0xe3, 0x9f, 0x37, 0xac, 0xe5, 0xf8, 0xeb, 0x7a, 0xe8, 0x40, 0xeb, 0x5d, 0xb1, 0x35, 0x5f,
|
||||||
|
0xb2, 0x47, 0x86, 0xf0, 0x7f, 0x6f, 0x4b, 0x55, 0x3e, 0xa1, 0xbb, 0xe8, 0xa1, 0x75, 0x00, 0x2d,
|
||||||
|
0x07, 0x9a, 0x21, 0x0e, 0xbd, 0x06, 0x6a, 0xb0, 0xfd, 0x96, 0x9e, 0xe6, 0xe4, 0x69, 0x67, 0xbb,
|
||||||
|
0x88, 0x45, 0x0b, 0x91, 0x0b, 0x7b, 0xcb, 0x21, 0x3c, 0x3c, 0x09, 0x30, 0x07, 0x71, 0x07, 0xd5,
|
||||||
|
0xb8, 0x2d, 0x83, 0x09, 0xaf, 0x7e, 0xb2, 0xa8, 0xac, 0x25, 0xdc, 0x10, 0xf8, 0x63, 0x6a, 0xbc,
|
||||||
|
0x73, 0x01, 0x4e, 0xa8, 0x1c, 0xda, 0x9a, 0x86, 0x17, 0xec, 0xa8, 0xfb, 0xaa, 0x23, 0x23, 0x17,
|
||||||
|
0xe1, 0x32, 0x68, 0x9c, 0x4c, 0xf4, 0x08, 0xed, 0xb0, 0x15, 0xc3, 0xa9, 0x0f, 0xf0, 0xa2, 0x7e,
|
||||||
|
0xd9, 0xe4, 0x23, 0xa7, 0x9e, 0x91, 0xd8, 0x73, 0x94, 0xd6, 0x6c, 0x70, 0x9b, 0x8b, 0x72, 0x92,
|
||||||
|
0xa3, 0xa4, 0x0a, 0xe2, 0x3c, 0x0a, 0x34, 0x88, 0xa1, 0x6d, 0xfe, 0x02, 0x44, 0x60, 0x7b, 0x3d,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __CRYPTONIGHT_TEST_H__ */
|
#endif /* __CRYPTONIGHT_TEST_H__ */
|
||||||
|
|
|
@ -461,6 +461,9 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si
|
||||||
((uint64_t*)&l0[idx0 & MASK])[0] = al0;
|
((uint64_t*)&l0[idx0 & MASK])[0] = al0;
|
||||||
((uint64_t*)&l0[idx0 & MASK])[1] = ah0;
|
((uint64_t*)&l0[idx0 & MASK])[1] = ah0;
|
||||||
VARIANT1_2(ah0, 0);
|
VARIANT1_2(ah0, 0);
|
||||||
|
if (ALGO == xmrig::CRYPTONIGHT_IPBC) {
|
||||||
|
((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0];
|
||||||
|
}
|
||||||
|
|
||||||
ah0 ^= ch;
|
ah0 ^= ch;
|
||||||
al0 ^= cl;
|
al0 ^= cl;
|
||||||
|
@ -560,6 +563,9 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si
|
||||||
((uint64_t*) &l0[idx0 & MASK])[0] = al0;
|
((uint64_t*) &l0[idx0 & MASK])[0] = al0;
|
||||||
((uint64_t*) &l0[idx0 & MASK])[1] = ah0;
|
((uint64_t*) &l0[idx0 & MASK])[1] = ah0;
|
||||||
VARIANT1_2(ah0, 0);
|
VARIANT1_2(ah0, 0);
|
||||||
|
if (ALGO == xmrig::CRYPTONIGHT_IPBC) {
|
||||||
|
((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0];
|
||||||
|
}
|
||||||
|
|
||||||
ah0 ^= ch;
|
ah0 ^= ch;
|
||||||
al0 ^= cl;
|
al0 ^= cl;
|
||||||
|
@ -585,6 +591,9 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si
|
||||||
((uint64_t*) &l1[idx1 & MASK])[0] = al1;
|
((uint64_t*) &l1[idx1 & MASK])[0] = al1;
|
||||||
((uint64_t*) &l1[idx1 & MASK])[1] = ah1;
|
((uint64_t*) &l1[idx1 & MASK])[1] = ah1;
|
||||||
VARIANT1_2(ah1, 1);
|
VARIANT1_2(ah1, 1);
|
||||||
|
if (ALGO == xmrig::CRYPTONIGHT_IPBC) {
|
||||||
|
((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0];
|
||||||
|
}
|
||||||
|
|
||||||
ah1 ^= ch;
|
ah1 ^= ch;
|
||||||
al1 ^= cl;
|
al1 ^= cl;
|
||||||
|
@ -648,6 +657,10 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si
|
||||||
_mm_store_si128(ptr, a); \
|
_mm_store_si128(ptr, a); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
if (ALGO == xmrig::CRYPTONIGHT_IPBC) { \
|
||||||
|
((uint64_t*)&l[idx & MASK])[1] ^= ((uint64_t*)&l[idx & MASK])[0]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
a = _mm_xor_si128(a, b); \
|
a = _mm_xor_si128(a, b); \
|
||||||
idx = EXTRACT64(a); \
|
idx = EXTRACT64(a); \
|
||||||
\
|
\
|
||||||
|
|
|
@ -45,9 +45,14 @@ static const char *algoNames[] = {
|
||||||
nullptr,
|
nullptr,
|
||||||
# endif
|
# endif
|
||||||
# ifndef XMRIG_NO_SUMO
|
# ifndef XMRIG_NO_SUMO
|
||||||
"cryptonight-heavy"
|
"cryptonight-heavy",
|
||||||
# else
|
# else
|
||||||
nullptr
|
nullptr,
|
||||||
|
# endif
|
||||||
|
# ifndef XMRIG_NO_IPBC
|
||||||
|
"cryptonight-ipbc",
|
||||||
|
# else
|
||||||
|
nullptr,
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,9 +65,14 @@ static const char *algoNamesShort[] = {
|
||||||
nullptr,
|
nullptr,
|
||||||
# endif
|
# endif
|
||||||
# ifndef XMRIG_NO_SUMO
|
# ifndef XMRIG_NO_SUMO
|
||||||
"cn-heavy"
|
"cn-heavy",
|
||||||
# else
|
# else
|
||||||
nullptr
|
nullptr,
|
||||||
|
# endif
|
||||||
|
# ifndef XMRIG_NO_IPBC
|
||||||
|
"cn-ipbc",
|
||||||
|
# else
|
||||||
|
nullptr,
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ xmrig::CpuThread::cn_hash_fun xmrig::CpuThread::fn(Algo algorithm, AlgoVariant a
|
||||||
{
|
{
|
||||||
assert(variant == VARIANT_NONE || variant == VARIANT_V1);
|
assert(variant == VARIANT_NONE || variant == VARIANT_V1);
|
||||||
|
|
||||||
static const cn_hash_fun func_table[50] = {
|
static const cn_hash_fun func_table[80] = {
|
||||||
cryptonight_single_hash<CRYPTONIGHT, false, VARIANT_NONE>,
|
cryptonight_single_hash<CRYPTONIGHT, false, VARIANT_NONE>,
|
||||||
cryptonight_double_hash<CRYPTONIGHT, false, VARIANT_NONE>,
|
cryptonight_double_hash<CRYPTONIGHT, false, VARIANT_NONE>,
|
||||||
cryptonight_single_hash<CRYPTONIGHT, true, VARIANT_NONE>,
|
cryptonight_single_hash<CRYPTONIGHT, true, VARIANT_NONE>,
|
||||||
|
@ -125,9 +125,28 @@ xmrig::CpuThread::cn_hash_fun xmrig::CpuThread::fn(Algo algorithm, AlgoVariant a
|
||||||
cryptonight_triple_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
cryptonight_triple_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
||||||
cryptonight_quad_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
cryptonight_quad_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
||||||
cryptonight_penta_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
cryptonight_penta_hash<CRYPTONIGHT_HEAVY, true, VARIANT_NONE>,
|
||||||
# else
|
|
||||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
# else
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# ifndef XMRIG_NO_IPBC
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
cryptonight_single_hash<CRYPTONIGHT_IPBC, false, VARIANT_V1>,
|
||||||
|
cryptonight_double_hash<CRYPTONIGHT_IPBC, false, VARIANT_V1>,
|
||||||
|
cryptonight_single_hash<CRYPTONIGHT_IPBC, true, VARIANT_V1>,
|
||||||
|
cryptonight_double_hash<CRYPTONIGHT_IPBC, true, VARIANT_V1>,
|
||||||
|
cryptonight_triple_hash<CRYPTONIGHT_IPBC, false, VARIANT_V1>,
|
||||||
|
cryptonight_quad_hash<CRYPTONIGHT_IPBC, false, VARIANT_V1>,
|
||||||
|
cryptonight_penta_hash<CRYPTONIGHT_IPBC, false, VARIANT_V1>,
|
||||||
|
cryptonight_triple_hash<CRYPTONIGHT_IPBC, true, VARIANT_V1>,
|
||||||
|
cryptonight_quad_hash<CRYPTONIGHT_IPBC, true, VARIANT_V1>,
|
||||||
|
cryptonight_penta_hash<CRYPTONIGHT_IPBC, true, VARIANT_V1>,
|
||||||
|
# else
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
|
|
||||||
# ifndef XMRIG_NO_SUMO
|
# ifndef XMRIG_NO_SUMO
|
||||||
|
@ -135,6 +154,11 @@ xmrig::CpuThread::cn_hash_fun xmrig::CpuThread::fn(Algo algorithm, AlgoVariant a
|
||||||
variant = VARIANT_NONE;
|
variant = VARIANT_NONE;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
# ifndef XMRIG_NO_IPBC
|
||||||
|
if (algorithm == CRYPTONIGHT_IPBC) {
|
||||||
|
variant = VARIANT_V1;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
return func_table[20 * algorithm + 10 * variant + av - 1];
|
return func_table[20 * algorithm + 10 * variant + av - 1];
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,10 +71,16 @@ bool MultiWorker<N>::selfTest()
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifndef XMRIG_NO_SUMO
|
# ifndef XMRIG_NO_SUMO
|
||||||
return m_thread->algorithm() == xmrig::CRYPTONIGHT_HEAVY && memcmp(m_hash, test_output_heavy, sizeof m_hash) == 0;
|
if (m_thread->algorithm() == xmrig::CRYPTONIGHT_HEAVY && memcmp(m_hash, test_output_heavy, sizeof m_hash) == 0) {
|
||||||
# else
|
return true;
|
||||||
return false;
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# ifndef XMRIG_NO_IPBC
|
||||||
|
return m_thread->algorithm() == xmrig::CRYPTONIGHT_IPBC && memcmp(m_hash, test_output_ipbc, sizeof m_hash) == 0;
|
||||||
|
# else
|
||||||
|
return false;
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue