alpha
This commit is contained in:
@ -11,6 +11,9 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|||||||
|
|
||||||
project(IACore)
|
project(IACore)
|
||||||
|
|
||||||
|
# Default to ON if root, OFF if dependency
|
||||||
|
option(IACore_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL})
|
||||||
|
|
||||||
message(STATUS "Detected Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
|
message(STATUS "Detected Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
|
||||||
# Check if the compiler is MSVC (cl.exe), but allow Clang acting like MSVC (clang-cl)
|
# Check if the compiler is MSVC (cl.exe), but allow Clang acting like MSVC (clang-cl)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
@ -42,6 +45,6 @@ add_subdirectory(Vendor/)
|
|||||||
|
|
||||||
add_subdirectory(Src/)
|
add_subdirectory(Src/)
|
||||||
|
|
||||||
if(IA_BUILD_TESTS)
|
if(IACore_BUILD_TESTS)
|
||||||
add_subdirectory(Tests/)
|
add_subdirectory(Tests)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -37,11 +37,11 @@ namespace IACore {
|
|||||||
// Generic Primitive Reader (Read<UINT32>(), Read<FLOAT32>(), etc.)
|
// Generic Primitive Reader (Read<UINT32>(), Read<FLOAT32>(), etc.)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
NO_DISCARD("Check for EOF")
|
NO_DISCARD("Check for EOF")
|
||||||
tl::expected<T, String> Read() {
|
Expected<T, String> Read() {
|
||||||
constexpr SIZE_T size = sizeof(T);
|
constexpr SIZE_T size = sizeof(T);
|
||||||
|
|
||||||
if B_UNLIKELY((m_cursor + size > m_span.size())) {
|
if B_UNLIKELY((m_cursor + size > m_span.size())) {
|
||||||
return tl::make_unexpected(String("Unexpected EOF reading type"));
|
return MakeUnexpected(String("Unexpected EOF reading type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
T value;
|
T value;
|
||||||
|
|||||||
@ -220,8 +220,9 @@
|
|||||||
|
|
||||||
// Templates and Aliases
|
// Templates and Aliases
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
# define ALIAS_FUNCTION(alias, function) \
|
#define ALIAS_FUNCTION(alias, function) \
|
||||||
template<typename... Args> auto alias(Args &&...args) -> decltype(f(std::forward<Args>(args)...)) \
|
template<typename... Args> \
|
||||||
|
auto alias(Args &&...args) -> decltype(function(std::forward<Args>(args)...)) \
|
||||||
{ \
|
{ \
|
||||||
return function(std::forward<Args>(args)...); \
|
return function(std::forward<Args>(args)...); \
|
||||||
}
|
}
|
||||||
@ -513,6 +514,10 @@ template<typename _key_type> using UnorderedSet = ankerl::unordered_dense::set<_
|
|||||||
template<typename _key_type, typename _value_type>
|
template<typename _key_type, typename _value_type>
|
||||||
using UnorderedMap = ankerl::unordered_dense::map<_key_type, _value_type>;
|
using UnorderedMap = ankerl::unordered_dense::map<_key_type, _value_type>;
|
||||||
|
|
||||||
|
template<typename _expected_type, typename _unexpected_type>
|
||||||
|
using Expected = tl::expected<_expected_type, _unexpected_type>;
|
||||||
|
ALIAS_FUNCTION(MakeUnexpected, tl::make_unexpected);
|
||||||
|
|
||||||
using String = std::string;
|
using String = std::string;
|
||||||
using StringView = std::string_view;
|
using StringView = std::string_view;
|
||||||
using StringStream = std::stringstream;
|
using StringStream = std::stringstream;
|
||||||
|
|||||||
@ -38,7 +38,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
auto val1 = reader.Read<UINT8>();
|
auto val1 = reader.Read<UINT8>();
|
||||||
IAT_CHECK(val1.has_value());
|
IAT_CHECK(val1.has_value());
|
||||||
IAT_CHECK_EQ(*val1, 0xAA);
|
IAT_CHECK_EQ(*val1, 0xAA);
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 1);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)1);
|
||||||
|
|
||||||
// Read Second Byte
|
// Read Second Byte
|
||||||
auto val2 = reader.Read<UINT8>();
|
auto val2 = reader.Read<UINT8>();
|
||||||
@ -61,9 +61,9 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
|
|
||||||
// Assuming standard x86/ARM Little Endian for this test
|
// Assuming standard x86/ARM Little Endian for this test
|
||||||
// If your engine supports Big Endian, you'd check architecture here
|
// If your engine supports Big Endian, you'd check architecture here
|
||||||
IAT_CHECK_EQ(*val, 0x04030201);
|
IAT_CHECK_EQ(*val, (UINT32)0x04030201);
|
||||||
|
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 4);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)4);
|
||||||
IAT_CHECK(reader.IsEOF());
|
IAT_CHECK(reader.IsEOF());
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -100,7 +100,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
auto str = reader.ReadString(5);
|
auto str = reader.ReadString(5);
|
||||||
IAT_CHECK(str.has_value());
|
IAT_CHECK(str.has_value());
|
||||||
IAT_CHECK_EQ(*str, String("Hello"));
|
IAT_CHECK_EQ(*str, String("Hello"));
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 5);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)5);
|
||||||
|
|
||||||
// Read remaining "IA"
|
// Read remaining "IA"
|
||||||
auto str2 = reader.ReadString(2);
|
auto str2 = reader.ReadString(2);
|
||||||
@ -128,7 +128,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
IAT_CHECK_EQ(dst[2], 3);
|
IAT_CHECK_EQ(dst[2], 3);
|
||||||
|
|
||||||
// Verify cursor
|
// Verify cursor
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 3);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)3);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -141,22 +141,22 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
UINT8 data[10] = { 0 }; // Zero init
|
UINT8 data[10] = { 0 }; // Zero init
|
||||||
BinaryReader reader(data);
|
BinaryReader reader(data);
|
||||||
|
|
||||||
IAT_CHECK_EQ(reader.Remaining(), 10);
|
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)10);
|
||||||
|
|
||||||
// Skip
|
// Skip
|
||||||
reader.Skip(5);
|
reader.Skip(5);
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 5);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)5);
|
||||||
IAT_CHECK_EQ(reader.Remaining(), 5);
|
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)5);
|
||||||
|
|
||||||
// Skip clamping
|
// Skip clamping
|
||||||
reader.Skip(100); // Should clamp to 10
|
reader.Skip(100); // Should clamp to 10
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 10);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)10);
|
||||||
IAT_CHECK(reader.IsEOF());
|
IAT_CHECK(reader.IsEOF());
|
||||||
|
|
||||||
// Seek
|
// Seek
|
||||||
reader.Seek(2);
|
reader.Seek(2);
|
||||||
IAT_CHECK_EQ(reader.Cursor(), 2);
|
IAT_CHECK_EQ(reader.Cursor(), (SIZE_T)2);
|
||||||
IAT_CHECK_EQ(reader.Remaining(), 8);
|
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)8);
|
||||||
IAT_CHECK_NOT(reader.IsEOF());
|
IAT_CHECK_NOT(reader.IsEOF());
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|||||||
@ -37,14 +37,14 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
|
|
||||||
// Write 1 Byte
|
// Write 1 Byte
|
||||||
writer.Write<UINT8>(0xAA);
|
writer.Write<UINT8>(0xAA);
|
||||||
IAT_CHECK_EQ(buffer.size(), 1);
|
IAT_CHECK_EQ(buffer.size(), (SIZE_T)1);
|
||||||
IAT_CHECK_EQ(buffer[0], 0xAA);
|
IAT_CHECK_EQ(buffer[0], 0xAA);
|
||||||
|
|
||||||
// Write 4 Bytes (UINT32)
|
// Write 4 Bytes (UINT32)
|
||||||
// 0xDDCCBBAA -> Little Endian: AA BB CC DD
|
// 0xDDCCBBAA -> Little Endian: AA BB CC DD
|
||||||
writer.Write<UINT32>(0xDDCCBBAA);
|
writer.Write<UINT32>(0xDDCCBBAA);
|
||||||
|
|
||||||
IAT_CHECK_EQ(buffer.size(), 5);
|
IAT_CHECK_EQ(buffer.size(), (SIZE_T)5);
|
||||||
|
|
||||||
// Verify Memory Layout
|
// Verify Memory Layout
|
||||||
IAT_CHECK_EQ(buffer[1], 0xAA);
|
IAT_CHECK_EQ(buffer[1], 0xAA);
|
||||||
@ -67,7 +67,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
// Should append to end, not overwrite 0x01
|
// Should append to end, not overwrite 0x01
|
||||||
writer.Write<UINT8>(0x03);
|
writer.Write<UINT8>(0x03);
|
||||||
|
|
||||||
IAT_CHECK_EQ(buffer.size(), 3);
|
IAT_CHECK_EQ(buffer.size(), (SIZE_T)3);
|
||||||
IAT_CHECK_EQ(buffer[0], 0x01);
|
IAT_CHECK_EQ(buffer[0], 0x01);
|
||||||
IAT_CHECK_EQ(buffer[2], 0x03);
|
IAT_CHECK_EQ(buffer[2], 0x03);
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
writer.WriteBytes((PVOID)msg, 2);
|
writer.WriteBytes((PVOID)msg, 2);
|
||||||
writer.Write<UINT8>(0x00); // Null term
|
writer.Write<UINT8>(0x00); // Null term
|
||||||
|
|
||||||
IAT_CHECK_EQ(buffer.size(), 3);
|
IAT_CHECK_EQ(buffer.size(), (SIZE_T)3);
|
||||||
IAT_CHECK_EQ(buffer[0], 'I');
|
IAT_CHECK_EQ(buffer[0], 'I');
|
||||||
IAT_CHECK_EQ(buffer[1], 'A');
|
IAT_CHECK_EQ(buffer[1], 'A');
|
||||||
IAT_CHECK_EQ(buffer[2], 0x00);
|
IAT_CHECK_EQ(buffer[2], 0x00);
|
||||||
@ -157,7 +157,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
FLOAT32 val = 1.234f;
|
FLOAT32 val = 1.234f;
|
||||||
writer.Write<FLOAT32>(val);
|
writer.Write<FLOAT32>(val);
|
||||||
|
|
||||||
IAT_CHECK_EQ(buffer.size(), 4);
|
IAT_CHECK_EQ(buffer.size(), (SIZE_T)4);
|
||||||
|
|
||||||
// Read it back via memcpy to verify
|
// Read it back via memcpy to verify
|
||||||
FLOAT32 readBack;
|
FLOAT32 readBack;
|
||||||
|
|||||||
@ -1,62 +1,64 @@
|
|||||||
|
set(TEST_NAME_PREFIX "IACore_Test_Unit_")
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# C Compile Test
|
# C Compile Test
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
enable_language(C)
|
enable_language(C)
|
||||||
|
|
||||||
add_executable(Test_Unit_CCompile "CCompile.c")
|
add_executable(${TEST_NAME_PREFIX}CCompile "CCompile.c")
|
||||||
|
|
||||||
set_target_properties(Test_Unit_CCompile PROPERTIES
|
set_target_properties(${TEST_NAME_PREFIX}CCompile PROPERTIES
|
||||||
C_STANDARD 99
|
C_STANDARD 99
|
||||||
C_STANDARD_REQUIRED ON
|
C_STANDARD_REQUIRED ON
|
||||||
LINKER_LANGUAGE C
|
LINKER_LANGUAGE C
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(Test_Unit_CCompile PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}CCompile PRIVATE IACore)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: BinaryReader
|
# Unit: BinaryReader
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_BinaryReader "BinaryReader.cpp")
|
add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
|
||||||
target_link_libraries(Test_Unit_BinaryReader PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_BinaryReader PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_BinaryReader PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: BinaryWriter
|
# Unit: BinaryWriter
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_BinaryWriter "BinaryWriter.cpp")
|
add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
|
||||||
target_link_libraries(Test_Unit_BinaryWriter PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_BinaryWriter PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: Environment
|
# Unit: Environment
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_Environment "Environment.cpp")
|
add_executable(${TEST_NAME_PREFIX}Environment "Environment.cpp")
|
||||||
target_link_libraries(Test_Unit_Environment PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}Environment PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_Environment PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}Environment PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_Environment PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}Environment PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: File
|
# Unit: File
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_File "File.cpp")
|
add_executable(${TEST_NAME_PREFIX}File "File.cpp")
|
||||||
target_link_libraries(Test_Unit_File PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}File PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_File PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}File PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_File PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}File PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: Process
|
# Unit: Process
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_Process "Process.cpp")
|
add_executable(${TEST_NAME_PREFIX}Process "Process.cpp")
|
||||||
target_link_libraries(Test_Unit_Process PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}Process PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_Process PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}Process PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_Process PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}Process PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: Utils
|
# Unit: Utils
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
add_executable(Test_Unit_Utils "Utils.cpp")
|
add_executable(${TEST_NAME_PREFIX}Utils "Utils.cpp")
|
||||||
target_link_libraries(Test_Unit_Utils PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}Utils PRIVATE IACore)
|
||||||
target_compile_options(Test_Unit_Utils PRIVATE -fexceptions)
|
target_compile_options(${TEST_NAME_PREFIX}Utils PRIVATE -fexceptions)
|
||||||
set_target_properties(Test_Unit_Utils PROPERTIES USE_EXCEPTIONS ON)
|
set_target_properties(${TEST_NAME_PREFIX}Utils PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|||||||
@ -107,7 +107,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
|||||||
IAT_CHECK(result.has_value());
|
IAT_CHECK(result.has_value());
|
||||||
Vector<UINT8>& vec = *result;
|
Vector<UINT8>& vec = *result;
|
||||||
|
|
||||||
IAT_CHECK_EQ(vec.size(), 4);
|
IAT_CHECK_EQ(vec.size(), (SIZE_T)4);
|
||||||
IAT_CHECK_EQ(vec[0], 0xDE);
|
IAT_CHECK_EQ(vec[0], 0xDE);
|
||||||
IAT_CHECK_EQ(vec[3], 0xEF);
|
IAT_CHECK_EQ(vec[3], 0xEF);
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
|||||||
SIZE_T bytesRead = f.Read(&magicRead, sizeof(magicRead));
|
SIZE_T bytesRead = f.Read(&magicRead, sizeof(magicRead));
|
||||||
|
|
||||||
IAT_CHECK_EQ(bytesRead, sizeof(UINT32));
|
IAT_CHECK_EQ(bytesRead, sizeof(UINT32));
|
||||||
IAT_CHECK_EQ(magicRead, 12345);
|
IAT_CHECK_EQ(magicRead, (UINT32)12345);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::remove(path);
|
std::filesystem::remove(path);
|
||||||
|
|||||||
@ -96,7 +96,7 @@ IAT_BEGIN_BLOCK(Core, Utils)
|
|||||||
// Empty string is valid (empty vector)
|
// Empty string is valid (empty vector)
|
||||||
auto empty = IACore::Utils::HexStringToBinary("");
|
auto empty = IACore::Utils::HexStringToBinary("");
|
||||||
IAT_CHECK(empty.has_value());
|
IAT_CHECK(empty.has_value());
|
||||||
IAT_CHECK_EQ(empty->size(), 0);
|
IAT_CHECK_EQ(empty->size(), (SIZE_T)0);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
def main(args: list[str]):
|
def main(args: list[str]):
|
||||||
os.system("cmake -S. -B./Build/Linux -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DIA_BUILD_TESTS=ON")
|
os.system("cmake -S. -B./Build/Linux -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug")
|
||||||
os.system("cmake --build ./Build/Linux")
|
os.system("cmake --build ./Build/Linux")
|
||||||
|
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
|
|||||||
2
Vendor/CMakeLists.txt
vendored
2
Vendor/CMakeLists.txt
vendored
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
|
set(EXPECTED_BUILD_TESTS OFF)
|
||||||
add_subdirectory(expected/)
|
add_subdirectory(expected/)
|
||||||
|
|
||||||
|
set(MI_BUILD_TESTS OFF)
|
||||||
set(MI_BUILD_STATIC ON)
|
set(MI_BUILD_STATIC ON)
|
||||||
set(MI_BUILD_SHARED ON)
|
set(MI_BUILD_SHARED ON)
|
||||||
add_subdirectory(mimalloc/)
|
add_subdirectory(mimalloc/)
|
||||||
|
|||||||
Reference in New Issue
Block a user