mirror of
https://github.com/stenzek/duckstation.git
synced 2026-07-08 17:46:19 +00:00
Common: Add additional unit tests
And fix several logical errors.
This commit is contained in:
@@ -1,3 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
|
||||
# SPDX-License-Identifier: CC-BY-NC-ND-4.0 + Packaging Restriction
|
||||
#
|
||||
# NOTE: In addition to the terms of CC-BY-NC-ND-4.0, you may not use this file to create
|
||||
# packages or build recipes without explicit permission from the copyright holder.
|
||||
#
|
||||
# Unless otherwise specified, other files supporting the build system are covered under
|
||||
# the same terms.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(duckstation C CXX)
|
||||
|
||||
|
||||
@@ -26,13 +26,23 @@ if(BUILD_TESTS)
|
||||
message(STATUS "Building unit tests.")
|
||||
endif()
|
||||
|
||||
# Refuse to build in Arch package environments. My license does not allow for packages, and I'm sick of
|
||||
# dealing with people complaining about things broken by packagers. This is why we can't have nice things.
|
||||
if(DEFINED ENV{DEBUGINFOD_URLS})
|
||||
if($ENV{DEBUGINFOD_URLS} MATCHES ".*archlinux.*")
|
||||
# Refuse to build in hostile package environments. The code and build script licenses do not allow for
|
||||
# packages, and I'm sick of dealing with people complaining about things broken by packagers, and then
|
||||
# being attacked by package maintainers who violate their distribution's codes of conduct. Attempts to
|
||||
# request removal of these packages have been unsuccessful, so we have to resort to this.
|
||||
# NOTE: You do NOT have permission to distribute build scripts or patches that modify the build system
|
||||
# without explicit permission from the copyright holder.
|
||||
# DuckStation's code is public so it can be audited and learned from. Not to repackage.
|
||||
# This is why we can't have nice things.
|
||||
if(EXISTS /etc/os-release)
|
||||
file(READ /etc/os-release OS_RELEASE_CONTENT)
|
||||
if(OS_RELEASE_CONTENT MATCHES "ID=arch" OR OS_RELEASE_CONTENT MATCHES "ID_LIKE=arch" OR OS_RELEASE_CONTENT MATCHES "ID=nixos")
|
||||
message(FATAL_ERROR "Unsupported environment.")
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED ENV{NIX_BUILD_TOP} OR DEFINED ENV{NIX_STORE} OR DEFINED ENV{IN_NIX_SHELL} OR EXISTS "/etc/NIXOS")
|
||||
message(FATAL_ERROR "Unsupported environment.")
|
||||
endif()
|
||||
|
||||
if(DEFINED HOST_MIN_PAGE_SIZE AND DEFINED HOST_MAX_PAGE_SIZE)
|
||||
message(STATUS "Building with a dynamic page size of ${HOST_MIN_PAGE_SIZE} - ${HOST_MAX_PAGE_SIZE} bytes.")
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
add_executable(common-tests
|
||||
binary_reader_writer_tests.cpp
|
||||
bitutils_tests.cpp
|
||||
file_system_tests.cpp
|
||||
gsvector_tests.cpp
|
||||
gsvector_yuvtorgb_test.cpp
|
||||
hash_tests.cpp
|
||||
heap_array_tests.cpp
|
||||
path_tests.cpp
|
||||
rectangle_tests.cpp
|
||||
small_string_tests.cpp
|
||||
string_tests.cpp
|
||||
)
|
||||
|
||||
|
||||
868
src/common-tests/binary_reader_writer_tests.cpp
Normal file
868
src/common-tests/binary_reader_writer_tests.cpp
Normal file
@@ -0,0 +1,868 @@
|
||||
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "common/binary_reader_writer.h"
|
||||
#include "common/small_string.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <gtest/gtest.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Global test data
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Buffer with various primitive types
|
||||
alignas(8) static constexpr std::array<u8, 32> g_primitive_buffer = {{
|
||||
0x01, // u8: 1
|
||||
0xFE, // s8: -2
|
||||
0x34, 0x12, // u16: 0x1234 (little endian)
|
||||
0xCD, 0xAB, // s16: -0x5433 (little endian)
|
||||
0x78, 0x56, 0x34, 0x12, // u32: 0x12345678
|
||||
0x88, 0xA9, 0xCB, 0xED, // s32: -0x12345678
|
||||
0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, // u64: 0x0123456789ABCDEF
|
||||
0x00, 0x00, 0x80, 0x3F, // float: 1.0f
|
||||
0x00, 0x00, 0x00, 0x00 // padding
|
||||
}};
|
||||
|
||||
// Buffer with C-strings (null-terminated)
|
||||
static constexpr std::array<u8, 16> g_cstring_buffer = {{
|
||||
'H', 'e', 'l', 'l', 'o', '\0', // "Hello"
|
||||
'W', 'o', 'r', 'l', 'd', '\0', // "World"
|
||||
'\0', // empty string
|
||||
'A', 'B', 'C' // "ABC" without null terminator (truncated)
|
||||
}};
|
||||
|
||||
// Buffer with size-prefixed strings (u32 length + data)
|
||||
static constexpr std::array<u8, 25> g_size_prefixed_buffer = {
|
||||
0x05, 0x00, 0x00, 0x00, // length: 5
|
||||
'H', 'e', 'l', 'l', 'o', // "Hello"
|
||||
0x00, 0x00, 0x00, 0x00, // length: 0 (empty string)
|
||||
0x04, 0x00, 0x00, 0x00, // length: 4
|
||||
'T', 'e', 's', 't', // "Test"
|
||||
0xFF, 0xFF, 0xFF, 0xFF // invalid length (overflow)
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// BinarySpanReader Tests
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST(BinarySpanReader, DefaultConstructor)
|
||||
{
|
||||
BinarySpanReader reader;
|
||||
EXPECT_FALSE(reader.IsValid());
|
||||
EXPECT_EQ(reader.GetBufferRemaining(), 0u);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, SpanConstructor)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
EXPECT_TRUE(reader.IsValid());
|
||||
EXPECT_EQ(reader.GetBufferRemaining(), g_primitive_buffer.size());
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, MoveConstructor)
|
||||
{
|
||||
BinarySpanReader original(g_primitive_buffer);
|
||||
original.ReadU8();
|
||||
original.ReadU8();
|
||||
|
||||
BinarySpanReader moved(std::move(original));
|
||||
EXPECT_TRUE(moved.IsValid());
|
||||
EXPECT_EQ(moved.GetBufferConsumed(), 2u);
|
||||
EXPECT_EQ(original.GetBufferConsumed(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, MoveAssignment)
|
||||
{
|
||||
BinarySpanReader original(g_primitive_buffer);
|
||||
original.ReadU8();
|
||||
|
||||
BinarySpanReader moved;
|
||||
moved = std::move(original);
|
||||
EXPECT_TRUE(moved.IsValid());
|
||||
EXPECT_EQ(moved.GetBufferConsumed(), 1u);
|
||||
EXPECT_EQ(original.GetBufferConsumed(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, GetSpan)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
auto span = reader.GetSpan();
|
||||
EXPECT_EQ(span.size(), g_primitive_buffer.size());
|
||||
EXPECT_EQ(span.data(), g_primitive_buffer.data());
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, CheckRemaining)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
EXPECT_TRUE(reader.CheckRemaining(g_primitive_buffer.size()));
|
||||
EXPECT_TRUE(reader.CheckRemaining(1));
|
||||
EXPECT_FALSE(reader.CheckRemaining(g_primitive_buffer.size() + 1));
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadU8)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
u8 val;
|
||||
EXPECT_TRUE(reader.ReadU8(&val));
|
||||
EXPECT_EQ(val, 0x01u);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 1u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadS8)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(1);
|
||||
s8 val;
|
||||
EXPECT_TRUE(reader.ReadS8(&val));
|
||||
EXPECT_EQ(val, static_cast<s8>(0xFE));
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 2u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadU16)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(2);
|
||||
u16 val;
|
||||
EXPECT_TRUE(reader.ReadU16(&val));
|
||||
EXPECT_EQ(val, 0x1234u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadS16)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(4);
|
||||
s16 val;
|
||||
EXPECT_TRUE(reader.ReadS16(&val));
|
||||
EXPECT_EQ(val, static_cast<s16>(0xABCD));
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadU32)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(6);
|
||||
u32 val;
|
||||
EXPECT_TRUE(reader.ReadU32(&val));
|
||||
EXPECT_EQ(val, 0x12345678u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadS32)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(10);
|
||||
s32 val;
|
||||
EXPECT_TRUE(reader.ReadS32(&val));
|
||||
EXPECT_EQ(val, static_cast<s32>(0xEDCBA988));
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadU64)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(14);
|
||||
u64 val;
|
||||
EXPECT_TRUE(reader.ReadU64(&val));
|
||||
EXPECT_EQ(val, 0x0123456789ABCDEFull);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadFloat)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(22);
|
||||
float val;
|
||||
EXPECT_TRUE(reader.ReadFloat(&val));
|
||||
EXPECT_FLOAT_EQ(val, 1.0f);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadBool)
|
||||
{
|
||||
std::array<u8, 2> buf = {0x00, 0x01};
|
||||
BinarySpanReader reader(buf);
|
||||
bool val;
|
||||
EXPECT_TRUE(reader.ReadBool(&val));
|
||||
EXPECT_FALSE(val);
|
||||
EXPECT_TRUE(reader.ReadBool(&val));
|
||||
EXPECT_TRUE(val);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadTReturningValue)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
EXPECT_EQ(reader.ReadU8(), 0x01u);
|
||||
EXPECT_EQ(reader.ReadS8(), static_cast<s8>(0xFE));
|
||||
EXPECT_EQ(reader.ReadU16(), 0x1234u);
|
||||
EXPECT_EQ(reader.ReadS16(), static_cast<s16>(0xABCD));
|
||||
EXPECT_EQ(reader.ReadU32(), 0x12345678u);
|
||||
EXPECT_EQ(reader.ReadS32(), static_cast<s32>(0xEDCBA988));
|
||||
EXPECT_EQ(reader.ReadU64(), 0x0123456789ABCDEFull);
|
||||
EXPECT_FLOAT_EQ(reader.ReadFloat(), 1.0f);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadBoolReturningValue)
|
||||
{
|
||||
std::array<u8, 3> buf = {0x00, 0x01, 0xFF};
|
||||
BinarySpanReader reader(buf);
|
||||
EXPECT_FALSE(reader.ReadBool());
|
||||
EXPECT_TRUE(reader.ReadBool());
|
||||
EXPECT_TRUE(reader.ReadBool()); // any non-zero is true
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadCStringToString)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
std::string val;
|
||||
EXPECT_TRUE(reader.ReadCString(&val));
|
||||
EXPECT_EQ(val, "Hello");
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 6u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadCStringToStringView)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
std::string_view val;
|
||||
EXPECT_TRUE(reader.ReadCString(&val));
|
||||
EXPECT_EQ(val, "Hello"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadCStringToSmallString)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
SmallString val;
|
||||
EXPECT_TRUE(reader.ReadCString(&val));
|
||||
EXPECT_STREQ(val.c_str(), "Hello");
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadCStringReturningValue)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
EXPECT_EQ(reader.ReadCString(), "Hello"sv);
|
||||
EXPECT_EQ(reader.ReadCString(), "World"sv);
|
||||
EXPECT_EQ(reader.ReadCString(), ""sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadCStringWithoutNullTerminator)
|
||||
{
|
||||
// Buffer ending without null terminator
|
||||
std::array<u8, 3> buf = {'A', 'B', 'C'};
|
||||
BinarySpanReader reader(buf);
|
||||
std::string val;
|
||||
EXPECT_FALSE(reader.ReadCString(&val));
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadSizePrefixedStringToString)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
std::string val;
|
||||
EXPECT_TRUE(reader.ReadSizePrefixedString(&val));
|
||||
EXPECT_EQ(val, "Hello");
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 9u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadSizePrefixedStringToStringView)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
std::string_view val;
|
||||
EXPECT_TRUE(reader.ReadSizePrefixedString(&val));
|
||||
EXPECT_EQ(val, "Hello"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadSizePrefixedStringToSmallString)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
SmallString val;
|
||||
EXPECT_TRUE(reader.ReadSizePrefixedString(&val));
|
||||
EXPECT_STREQ(val.c_str(), "Hello");
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadSizePrefixedStringReturningValue)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), "Hello"sv);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), ""sv);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), "Test"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadSizePrefixedStringEmpty)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
reader.IncrementPosition(9);
|
||||
std::string val;
|
||||
EXPECT_TRUE(reader.ReadSizePrefixedString(&val));
|
||||
EXPECT_TRUE(val.empty());
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekU8)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
u8 val;
|
||||
EXPECT_TRUE(reader.PeekU8(&val));
|
||||
EXPECT_EQ(val, 0x01u);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u); // position unchanged
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekU16)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(2);
|
||||
u16 val;
|
||||
EXPECT_TRUE(reader.PeekU16(&val));
|
||||
EXPECT_EQ(val, 0x1234u);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 2u); // position unchanged after peek
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekU32)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(6);
|
||||
u32 val;
|
||||
EXPECT_TRUE(reader.PeekU32(&val));
|
||||
EXPECT_EQ(val, 0x12345678u);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 6u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekU64)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(14);
|
||||
u64 val;
|
||||
EXPECT_TRUE(reader.PeekU64(&val));
|
||||
EXPECT_EQ(val, 0x0123456789ABCDEFull);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 14u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekFloat)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(22);
|
||||
float val;
|
||||
EXPECT_TRUE(reader.PeekFloat(&val));
|
||||
EXPECT_FLOAT_EQ(val, 1.0f);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 22u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekBool)
|
||||
{
|
||||
std::array<u8, 2> buf = {0x00, 0x01};
|
||||
BinarySpanReader reader(buf);
|
||||
bool val;
|
||||
EXPECT_TRUE(reader.PeekBool(&val));
|
||||
EXPECT_FALSE(val);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekCString)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
std::string_view val;
|
||||
EXPECT_TRUE(reader.PeekCString(&val));
|
||||
EXPECT_EQ(val, "Hello"sv);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u); // position unchanged
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekSizePrefixedString)
|
||||
{
|
||||
BinarySpanReader reader(g_size_prefixed_buffer);
|
||||
std::string_view val;
|
||||
EXPECT_TRUE(reader.PeekSizePrefixedString(&val));
|
||||
EXPECT_EQ(val, "Hello"sv);
|
||||
EXPECT_EQ(reader.GetBufferConsumed(), 0u); // position unchanged
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, StreamOperators)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
u8 u8val;
|
||||
s8 s8val;
|
||||
u16 u16val;
|
||||
s16 s16val;
|
||||
u32 u32val;
|
||||
s32 s32val;
|
||||
u64 u64val;
|
||||
float fval;
|
||||
|
||||
reader >> u8val >> s8val >> u16val >> s16val >> u32val >> s32val >> u64val >> fval;
|
||||
|
||||
EXPECT_EQ(u8val, 0x01u);
|
||||
EXPECT_EQ(s8val, static_cast<s8>(0xFE));
|
||||
EXPECT_EQ(u16val, 0x1234u);
|
||||
EXPECT_EQ(s16val, static_cast<s16>(0xABCD));
|
||||
EXPECT_EQ(u32val, 0x12345678u);
|
||||
EXPECT_EQ(s32val, static_cast<s32>(0xEDCBA988));
|
||||
EXPECT_EQ(u64val, 0x0123456789ABCDEFull);
|
||||
EXPECT_FLOAT_EQ(fval, 1.0f);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, StreamOperatorCString)
|
||||
{
|
||||
BinarySpanReader reader(g_cstring_buffer);
|
||||
std::string_view val;
|
||||
reader >> val;
|
||||
EXPECT_EQ(val, "Hello"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadOptionalTWithValue)
|
||||
{
|
||||
std::array<u8, 5> buf = {0x01, 0x78, 0x56, 0x34, 0x12}; // has_value=true, value=0x12345678
|
||||
BinarySpanReader reader(buf);
|
||||
std::optional<u32> val;
|
||||
EXPECT_TRUE(reader.ReadOptionalT(&val));
|
||||
EXPECT_TRUE(val.has_value());
|
||||
EXPECT_EQ(val.value(), 0x12345678u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadOptionalTWithoutValue)
|
||||
{
|
||||
std::array<u8, 5> buf = {0x00, 0x00, 0x00, 0x00, 0x00}; // has_value=false
|
||||
BinarySpanReader reader(buf);
|
||||
std::optional<u32> val = 123u; // preset to non-empty
|
||||
EXPECT_TRUE(reader.ReadOptionalT(&val));
|
||||
EXPECT_FALSE(val.has_value());
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, GetRemainingSpan)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(10);
|
||||
auto remaining = reader.GetRemainingSpan();
|
||||
EXPECT_EQ(remaining.size(), g_primitive_buffer.size() - 10);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, GetRemainingSpanWithSize)
|
||||
{
|
||||
BinarySpanReader reader(g_primitive_buffer);
|
||||
reader.IncrementPosition(10);
|
||||
auto remaining = reader.GetRemainingSpan(5);
|
||||
EXPECT_EQ(remaining.size(), 5u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, ReadBeyondBuffer)
|
||||
{
|
||||
std::array<u8, 2> buf = {0x01, 0x02};
|
||||
BinarySpanReader reader(buf);
|
||||
u32 val;
|
||||
EXPECT_FALSE(reader.ReadU32(&val));
|
||||
}
|
||||
|
||||
TEST(BinarySpanReader, PeekBeyondBuffer)
|
||||
{
|
||||
std::array<u8, 2> buf = {0x01, 0x02};
|
||||
BinarySpanReader reader(buf);
|
||||
u32 val;
|
||||
EXPECT_FALSE(reader.PeekU32(&val));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// BinarySpanWriter Tests
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST(BinarySpanWriter, DefaultConstructor)
|
||||
{
|
||||
BinarySpanWriter writer;
|
||||
EXPECT_FALSE(writer.IsValid());
|
||||
EXPECT_EQ(writer.GetBufferRemaining(), 0u);
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, SpanConstructor)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.IsValid());
|
||||
EXPECT_EQ(writer.GetBufferRemaining(), buf.size());
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, MoveConstructor)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter original(buf);
|
||||
original.WriteU8(0x01);
|
||||
original.WriteU8(0x02);
|
||||
|
||||
BinarySpanWriter moved(std::move(original));
|
||||
EXPECT_TRUE(moved.IsValid());
|
||||
EXPECT_EQ(moved.GetBufferWritten(), 2u);
|
||||
EXPECT_EQ(original.GetBufferWritten(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, MoveAssignment)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter original(buf);
|
||||
original.WriteU8(0x01);
|
||||
|
||||
BinarySpanWriter moved;
|
||||
moved = std::move(original);
|
||||
EXPECT_TRUE(moved.IsValid());
|
||||
EXPECT_EQ(moved.GetBufferWritten(), 1u);
|
||||
EXPECT_EQ(original.GetBufferWritten(), 0u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, GetSpan)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
auto span = writer.GetSpan();
|
||||
EXPECT_EQ(span.size(), buf.size());
|
||||
EXPECT_EQ(span.data(), buf.data());
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteU8)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteU8(0xAB));
|
||||
EXPECT_EQ(buf[0], 0xABu);
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 1u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteS8)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteS8(-5));
|
||||
EXPECT_EQ(static_cast<s8>(buf[0]), -5);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteU16)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteU16(0x1234));
|
||||
EXPECT_EQ(buf[0], 0x34u);
|
||||
EXPECT_EQ(buf[1], 0x12u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteS16)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteS16(-1000));
|
||||
s16 val;
|
||||
std::memcpy(&val, buf.data(), sizeof(val));
|
||||
EXPECT_EQ(val, -1000);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteU32)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteU32(0x12345678));
|
||||
EXPECT_EQ(buf[0], 0x78u);
|
||||
EXPECT_EQ(buf[1], 0x56u);
|
||||
EXPECT_EQ(buf[2], 0x34u);
|
||||
EXPECT_EQ(buf[3], 0x12u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteS32)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteS32(-123456789));
|
||||
s32 val;
|
||||
std::memcpy(&val, buf.data(), sizeof(val));
|
||||
EXPECT_EQ(val, -123456789);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteU64)
|
||||
{
|
||||
std::array<u8, 8> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteU64(0x0123456789ABCDEFull));
|
||||
EXPECT_EQ(buf[0], 0xEFu);
|
||||
EXPECT_EQ(buf[7], 0x01u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteS64)
|
||||
{
|
||||
std::array<u8, 8> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteS64(-1234567890123456789ll));
|
||||
s64 val;
|
||||
std::memcpy(&val, buf.data(), sizeof(val));
|
||||
EXPECT_EQ(val, -1234567890123456789ll);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteFloat)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteFloat(1.0f));
|
||||
float val;
|
||||
std::memcpy(&val, buf.data(), sizeof(val));
|
||||
EXPECT_FLOAT_EQ(val, 1.0f);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteBool)
|
||||
{
|
||||
std::array<u8, 2> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteBool(false));
|
||||
EXPECT_TRUE(writer.WriteBool(true));
|
||||
EXPECT_EQ(buf[0], 0x00u);
|
||||
EXPECT_EQ(buf[1], 0x01u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteCString)
|
||||
{
|
||||
std::array<u8, 16> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteCString("Hello"));
|
||||
EXPECT_EQ(buf[0], 'H');
|
||||
EXPECT_EQ(buf[4], 'o');
|
||||
EXPECT_EQ(buf[5], '\0');
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 6u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteCStringEmpty)
|
||||
{
|
||||
std::array<u8, 16> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteCString(""));
|
||||
EXPECT_EQ(buf[0], '\0');
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 1u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteSizePrefixedString)
|
||||
{
|
||||
std::array<u8, 16> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteSizePrefixedString("Test"));
|
||||
|
||||
u32 length;
|
||||
std::memcpy(&length, buf.data(), sizeof(length));
|
||||
EXPECT_EQ(length, 4u);
|
||||
EXPECT_EQ(buf[4], 'T');
|
||||
EXPECT_EQ(buf[7], 't');
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 8u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteSizePrefixedStringEmpty)
|
||||
{
|
||||
std::array<u8, 16> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_TRUE(writer.WriteSizePrefixedString(""));
|
||||
|
||||
u32 length;
|
||||
std::memcpy(&length, buf.data(), sizeof(length));
|
||||
EXPECT_EQ(length, 0u);
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 4u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, StreamOperators)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
writer << static_cast<u8>(0x01) << static_cast<s8>(0xFE) << static_cast<u16>(0x1234) << static_cast<s16>(0xABCD)
|
||||
<< static_cast<u32>(0x12345678) << static_cast<s32>(0xEDCBA988) << static_cast<u64>(0x0123456789ABCDEFull)
|
||||
<< 1.0f;
|
||||
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 26u);
|
||||
|
||||
// Verify by reading back
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_EQ(reader.ReadU8(), 0x01u);
|
||||
EXPECT_EQ(reader.ReadS8(), static_cast<s8>(0xFE));
|
||||
EXPECT_EQ(reader.ReadU16(), 0x1234u);
|
||||
EXPECT_EQ(reader.ReadS16(), static_cast<s16>(0xABCD));
|
||||
EXPECT_EQ(reader.ReadU32(), 0x12345678u);
|
||||
EXPECT_EQ(reader.ReadS32(), static_cast<s32>(0xEDCBA988));
|
||||
EXPECT_EQ(reader.ReadU64(), 0x0123456789ABCDEFull);
|
||||
EXPECT_FLOAT_EQ(reader.ReadFloat(), 1.0f);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, StreamOperatorCString)
|
||||
{
|
||||
std::array<u8, 16> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
writer << "Hello"sv;
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_EQ(reader.ReadCString(), "Hello"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteOptionalTWithValue)
|
||||
{
|
||||
std::array<u8, 8> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
std::optional<u32> val = 0x12345678u;
|
||||
EXPECT_TRUE(writer.WriteOptionalT(val));
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 5u);
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_TRUE(reader.ReadBool());
|
||||
EXPECT_EQ(reader.ReadU32(), 0x12345678u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteOptionalTWithoutValue)
|
||||
{
|
||||
std::array<u8, 8> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
std::optional<u32> val;
|
||||
EXPECT_TRUE(writer.WriteOptionalT(val));
|
||||
EXPECT_EQ(writer.GetBufferWritten(), 1u);
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_FALSE(reader.ReadBool());
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, GetRemainingSpan)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
writer.IncrementPosition(10);
|
||||
auto remaining = writer.GetRemainingSpan();
|
||||
EXPECT_EQ(remaining.size(), buf.size() - 10);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, GetRemainingSpanWithSize)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
writer.IncrementPosition(10);
|
||||
auto remaining = writer.GetRemainingSpan(5);
|
||||
EXPECT_EQ(remaining.size(), 5u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteBeyondBuffer)
|
||||
{
|
||||
std::array<u8, 2> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_FALSE(writer.WriteU32(0x12345678));
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteCStringBeyondBuffer)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_FALSE(writer.WriteCString("Hello")); // needs 6 bytes
|
||||
}
|
||||
|
||||
TEST(BinarySpanWriter, WriteSizePrefixedStringBeyondBuffer)
|
||||
{
|
||||
std::array<u8, 4> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
EXPECT_FALSE(writer.WriteSizePrefixedString("Test")); // needs 8 bytes
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Round-trip Tests (Writer -> Reader)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST(BinarySpanRoundTrip, AllPrimitiveTypes)
|
||||
{
|
||||
std::array<u8, 64> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
writer.WriteU8(0xAB);
|
||||
writer.WriteS8(-42);
|
||||
writer.WriteU16(0x1234);
|
||||
writer.WriteS16(-1000);
|
||||
writer.WriteU32(0xDEADBEEF);
|
||||
writer.WriteS32(-123456789);
|
||||
writer.WriteU64(0xFEDCBA9876543210ull);
|
||||
writer.WriteS64(-876543210123456789ll);
|
||||
writer.WriteFloat(3.14159f);
|
||||
writer.WriteBool(true);
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_EQ(reader.ReadU8(), 0xABu);
|
||||
EXPECT_EQ(reader.ReadS8(), -42);
|
||||
EXPECT_EQ(reader.ReadU16(), 0x1234u);
|
||||
EXPECT_EQ(reader.ReadS16(), -1000);
|
||||
EXPECT_EQ(reader.ReadU32(), 0xDEADBEEFu);
|
||||
EXPECT_EQ(reader.ReadS32(), -123456789);
|
||||
EXPECT_EQ(reader.ReadU64(), 0xFEDCBA9876543210ull);
|
||||
EXPECT_EQ(reader.ReadS64(), -876543210123456789ll);
|
||||
EXPECT_FLOAT_EQ(reader.ReadFloat(), 3.14159f);
|
||||
EXPECT_TRUE(reader.ReadBool());
|
||||
}
|
||||
|
||||
TEST(BinarySpanRoundTrip, CStrings)
|
||||
{
|
||||
std::array<u8, 64> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
writer.WriteCString("Hello");
|
||||
writer.WriteCString("");
|
||||
writer.WriteCString("World!");
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_EQ(reader.ReadCString(), "Hello"sv);
|
||||
EXPECT_EQ(reader.ReadCString(), ""sv);
|
||||
EXPECT_EQ(reader.ReadCString(), "World!"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanRoundTrip, SizePrefixedStrings)
|
||||
{
|
||||
std::array<u8, 64> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
writer.WriteSizePrefixedString("Testing");
|
||||
writer.WriteSizePrefixedString("");
|
||||
writer.WriteSizePrefixedString("123");
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), "Testing"sv);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), ""sv);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), "123"sv);
|
||||
}
|
||||
|
||||
TEST(BinarySpanRoundTrip, OptionalValues)
|
||||
{
|
||||
std::array<u8, 32> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
std::optional<u32> val1 = 12345u;
|
||||
std::optional<u32> val2;
|
||||
std::optional<u16> val3 = u16(9999);
|
||||
|
||||
writer.WriteOptionalT(val1);
|
||||
writer.WriteOptionalT(val2);
|
||||
writer.WriteOptionalT(val3);
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
|
||||
std::optional<u32> read1, read2;
|
||||
std::optional<u16> read3;
|
||||
|
||||
EXPECT_TRUE(reader.ReadOptionalT(&read1));
|
||||
EXPECT_TRUE(reader.ReadOptionalT(&read2));
|
||||
EXPECT_TRUE(reader.ReadOptionalT(&read3));
|
||||
|
||||
EXPECT_TRUE(read1.has_value());
|
||||
EXPECT_EQ(read1.value(), 12345u);
|
||||
EXPECT_FALSE(read2.has_value());
|
||||
EXPECT_TRUE(read3.has_value());
|
||||
EXPECT_EQ(read3.value(), 9999u);
|
||||
}
|
||||
|
||||
TEST(BinarySpanRoundTrip, MixedContent)
|
||||
{
|
||||
std::array<u8, 128> buf = {};
|
||||
BinarySpanWriter writer(buf);
|
||||
|
||||
writer.WriteU32(0x12345678);
|
||||
writer.WriteCString("Header");
|
||||
writer.WriteU16(100);
|
||||
writer.WriteSizePrefixedString("Payload");
|
||||
writer.WriteFloat(2.5f);
|
||||
writer.WriteBool(false);
|
||||
|
||||
BinarySpanReader reader(std::span<const u8>(buf.data(), writer.GetBufferWritten()));
|
||||
|
||||
EXPECT_EQ(reader.ReadU32(), 0x12345678u);
|
||||
EXPECT_EQ(reader.ReadCString(), "Header"sv);
|
||||
EXPECT_EQ(reader.ReadU16(), 100u);
|
||||
EXPECT_EQ(reader.ReadSizePrefixedString(), "Payload"sv);
|
||||
EXPECT_FLOAT_EQ(reader.ReadFloat(), 2.5f);
|
||||
EXPECT_FALSE(reader.ReadBool());
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\dep\googletest\src\gtest_main.cc" />
|
||||
<ClCompile Include="binary_reader_writer_tests.cpp" />
|
||||
<ClCompile Include="bitutils_tests.cpp" />
|
||||
<ClCompile Include="file_system_tests.cpp" />
|
||||
<ClCompile Include="gsvector_tests.cpp" />
|
||||
<ClCompile Include="heap_array_tests.cpp" />
|
||||
<ClCompile Include="path_tests.cpp" />
|
||||
<ClCompile Include="rectangle_tests.cpp" />
|
||||
<ClCompile Include="hash_tests.cpp" />
|
||||
<ClCompile Include="small_string_tests.cpp" />
|
||||
<ClCompile Include="string_tests.cpp" />
|
||||
<ClCompile Include="gsvector_yuvtorgb_test.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -10,5 +10,8 @@
|
||||
<ClCompile Include="gsvector_yuvtorgb_test.cpp" />
|
||||
<ClCompile Include="hash_tests.cpp" />
|
||||
<ClCompile Include="gsvector_tests.cpp" />
|
||||
<ClCompile Include="small_string_tests.cpp" />
|
||||
<ClCompile Include="binary_reader_writer_tests.cpp" />
|
||||
<ClCompile Include="heap_array_tests.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
827
src/common-tests/heap_array_tests.cpp
Normal file
827
src/common-tests/heap_array_tests.cpp
Normal file
@@ -0,0 +1,827 @@
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "common/heap_array.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// ============================================================================
|
||||
// FixedHeapArray Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(FixedHeapArray, DefaultConstruction)
|
||||
{
|
||||
FixedHeapArray<int, 10> arr;
|
||||
EXPECT_EQ(arr.size(), 10u);
|
||||
EXPECT_EQ(arr.capacity(), 10u);
|
||||
EXPECT_EQ(arr.size_bytes(), 10u * sizeof(int));
|
||||
EXPECT_FALSE(arr.empty());
|
||||
EXPECT_NE(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, CopyConstruction)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr1;
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 10);
|
||||
|
||||
FixedHeapArray<int, 5> arr2(arr1);
|
||||
EXPECT_EQ(arr2.size(), arr1.size());
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], arr1[i]);
|
||||
|
||||
// Ensure they have separate storage
|
||||
EXPECT_NE(arr1.data(), arr2.data());
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, MoveConstruction)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr1;
|
||||
int* original_data = arr1.data();
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 10);
|
||||
|
||||
FixedHeapArray<int, 5> arr2(std::move(arr1));
|
||||
EXPECT_EQ(arr2.data(), original_data);
|
||||
EXPECT_EQ(arr2.size(), 5u);
|
||||
|
||||
for (size_t i = 0; i < arr2.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], static_cast<int>(i * 10));
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, ElementAccess)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr;
|
||||
arr[0] = 100;
|
||||
arr[1] = 200;
|
||||
arr[2] = 300;
|
||||
arr[3] = 400;
|
||||
arr[4] = 500;
|
||||
|
||||
EXPECT_EQ(arr[0], 100);
|
||||
EXPECT_EQ(arr[1], 200);
|
||||
EXPECT_EQ(arr[2], 300);
|
||||
EXPECT_EQ(arr[3], 400);
|
||||
EXPECT_EQ(arr[4], 500);
|
||||
|
||||
const auto& carr = arr;
|
||||
EXPECT_EQ(carr[0], 100);
|
||||
EXPECT_EQ(carr[4], 500);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, FrontBack)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr;
|
||||
arr[0] = 10;
|
||||
arr[4] = 50;
|
||||
|
||||
EXPECT_EQ(arr.front(), 10);
|
||||
EXPECT_EQ(arr.back(), 50);
|
||||
|
||||
arr.front() = 15;
|
||||
arr.back() = 55;
|
||||
EXPECT_EQ(arr[0], 15);
|
||||
EXPECT_EQ(arr[4], 55);
|
||||
|
||||
const auto& carr = arr;
|
||||
EXPECT_EQ(carr.front(), 15);
|
||||
EXPECT_EQ(carr.back(), 55);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, Iterators)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr;
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
int expected = 0;
|
||||
for (auto it = arr.begin(); it != arr.end(); ++it)
|
||||
{
|
||||
EXPECT_EQ(*it, expected);
|
||||
++expected;
|
||||
}
|
||||
EXPECT_EQ(expected, 5);
|
||||
|
||||
expected = 0;
|
||||
for (auto it = arr.cbegin(); it != arr.cend(); ++it)
|
||||
{
|
||||
EXPECT_EQ(*it, expected);
|
||||
++expected;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, Fill)
|
||||
{
|
||||
FixedHeapArray<int, 10> arr;
|
||||
arr.fill(42);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], 42);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, Swap)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr1;
|
||||
FixedHeapArray<int, 5> arr2;
|
||||
arr1.fill(10);
|
||||
arr2.fill(20);
|
||||
|
||||
int* arr1_data = arr1.data();
|
||||
int* arr2_data = arr2.data();
|
||||
|
||||
arr1.swap(arr2);
|
||||
|
||||
EXPECT_EQ(arr1.data(), arr2_data);
|
||||
EXPECT_EQ(arr2.data(), arr1_data);
|
||||
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(arr1[i], 20);
|
||||
EXPECT_EQ(arr2[i], 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, Span)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr;
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i * 2);
|
||||
|
||||
std::span<int, 5> sp = arr.span();
|
||||
EXPECT_EQ(sp.size(), 5u);
|
||||
EXPECT_EQ(sp.data(), arr.data());
|
||||
|
||||
for (size_t i = 0; i < sp.size(); ++i)
|
||||
EXPECT_EQ(sp[i], static_cast<int>(i * 2));
|
||||
|
||||
const auto& carr = arr;
|
||||
std::span<const int, 5> csp = carr.cspan();
|
||||
EXPECT_EQ(csp.size(), 5u);
|
||||
EXPECT_EQ(csp.data(), carr.data());
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, MoveAssignment)
|
||||
{
|
||||
FixedHeapArray<int, 5> arr1;
|
||||
arr1.fill(42);
|
||||
int* original_data = arr1.data();
|
||||
|
||||
FixedHeapArray<int, 5> arr2;
|
||||
arr2.fill(0);
|
||||
|
||||
arr2 = std::move(arr1);
|
||||
EXPECT_EQ(arr2.data(), original_data);
|
||||
for (size_t i = 0; i < arr2.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], 42);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, AlignedAllocation)
|
||||
{
|
||||
constexpr size_t alignment = 64;
|
||||
FixedHeapArray<int, 16, alignment> arr;
|
||||
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(arr.data());
|
||||
EXPECT_EQ(addr % alignment, 0u);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, DifferentTypes)
|
||||
{
|
||||
FixedHeapArray<double, 3> arr;
|
||||
arr[0] = 1.5;
|
||||
arr[1] = 2.5;
|
||||
arr[2] = 3.5;
|
||||
|
||||
EXPECT_DOUBLE_EQ(arr[0], 1.5);
|
||||
EXPECT_DOUBLE_EQ(arr[1], 2.5);
|
||||
EXPECT_DOUBLE_EQ(arr[2], 3.5);
|
||||
EXPECT_EQ(arr.size_bytes(), 3u * sizeof(double));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DynamicHeapArray Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(DynamicHeapArray, DefaultConstruction)
|
||||
{
|
||||
DynamicHeapArray<int> arr;
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
EXPECT_EQ(arr.capacity(), 0u);
|
||||
EXPECT_EQ(arr.size_bytes(), 0u);
|
||||
EXPECT_TRUE(arr.empty());
|
||||
EXPECT_EQ(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SizeConstruction)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
EXPECT_EQ(arr.size(), 10u);
|
||||
EXPECT_EQ(arr.capacity(), 10u);
|
||||
EXPECT_EQ(arr.size_bytes(), 10u * sizeof(int));
|
||||
EXPECT_FALSE(arr.empty());
|
||||
EXPECT_NE(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, RangeConstructionBeginEnd)
|
||||
{
|
||||
int source[] = {1, 2, 3, 4, 5};
|
||||
DynamicHeapArray<int> arr(std::begin(source), std::end(source));
|
||||
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, RangeConstructionBeginCount)
|
||||
{
|
||||
int source[] = {10, 20, 30, 40, 50};
|
||||
DynamicHeapArray<int> arr(source, 5);
|
||||
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SpanConstruction)
|
||||
{
|
||||
int source[] = {1, 2, 3, 4, 5};
|
||||
std::span<const int> sp(source);
|
||||
DynamicHeapArray<int> arr(sp);
|
||||
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SpanConstructionEmpty)
|
||||
{
|
||||
std::span<const int> sp;
|
||||
DynamicHeapArray<int> arr(sp);
|
||||
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
EXPECT_TRUE(arr.empty());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, CopyConstruction)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 10);
|
||||
|
||||
DynamicHeapArray<int> arr2(arr1);
|
||||
EXPECT_EQ(arr2.size(), arr1.size());
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], arr1[i]);
|
||||
|
||||
// Ensure they have separate storage
|
||||
EXPECT_NE(arr1.data(), arr2.data());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, CopyConstructionEmpty)
|
||||
{
|
||||
DynamicHeapArray<int> arr1;
|
||||
DynamicHeapArray<int> arr2(arr1);
|
||||
|
||||
EXPECT_EQ(arr2.size(), 0u);
|
||||
EXPECT_TRUE(arr2.empty());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, MoveConstruction)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
int* original_data = arr1.data();
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 10);
|
||||
|
||||
DynamicHeapArray<int> arr2(std::move(arr1));
|
||||
|
||||
EXPECT_EQ(arr2.data(), original_data);
|
||||
EXPECT_EQ(arr2.size(), 5u);
|
||||
EXPECT_EQ(arr1.size(), 0u);
|
||||
EXPECT_EQ(arr1.data(), nullptr);
|
||||
|
||||
for (size_t i = 0; i < arr2.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], static_cast<int>(i * 10));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, ElementAccess)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
arr[0] = 100;
|
||||
arr[1] = 200;
|
||||
arr[2] = 300;
|
||||
arr[3] = 400;
|
||||
arr[4] = 500;
|
||||
|
||||
EXPECT_EQ(arr[0], 100);
|
||||
EXPECT_EQ(arr[1], 200);
|
||||
EXPECT_EQ(arr[2], 300);
|
||||
EXPECT_EQ(arr[3], 400);
|
||||
EXPECT_EQ(arr[4], 500);
|
||||
|
||||
const auto& carr = arr;
|
||||
EXPECT_EQ(carr[0], 100);
|
||||
EXPECT_EQ(carr[4], 500);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, FrontBack)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
arr[0] = 10;
|
||||
arr[4] = 50;
|
||||
|
||||
EXPECT_EQ(arr.front(), 10);
|
||||
EXPECT_EQ(arr.back(), 50);
|
||||
|
||||
arr.front() = 15;
|
||||
arr.back() = 55;
|
||||
EXPECT_EQ(arr[0], 15);
|
||||
EXPECT_EQ(arr[4], 55);
|
||||
|
||||
const auto& carr = arr;
|
||||
EXPECT_EQ(carr.front(), 15);
|
||||
EXPECT_EQ(carr.back(), 55);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, Iterators)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
int expected = 0;
|
||||
for (auto it = arr.begin(); it != arr.end(); ++it)
|
||||
{
|
||||
EXPECT_EQ(*it, expected);
|
||||
++expected;
|
||||
}
|
||||
EXPECT_EQ(expected, 5);
|
||||
|
||||
expected = 0;
|
||||
for (auto it = arr.cbegin(); it != arr.cend(); ++it)
|
||||
{
|
||||
EXPECT_EQ(*it, expected);
|
||||
++expected;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, Fill)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
arr.fill(42);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], 42);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, Swap)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
DynamicHeapArray<int> arr2(3);
|
||||
arr1.fill(10);
|
||||
arr2.fill(20);
|
||||
|
||||
int* arr1_data = arr1.data();
|
||||
int* arr2_data = arr2.data();
|
||||
|
||||
arr1.swap(arr2);
|
||||
|
||||
EXPECT_EQ(arr1.data(), arr2_data);
|
||||
EXPECT_EQ(arr2.data(), arr1_data);
|
||||
EXPECT_EQ(arr1.size(), 3u);
|
||||
EXPECT_EQ(arr2.size(), 5u);
|
||||
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
EXPECT_EQ(arr1[i], 20);
|
||||
for (size_t i = 0; i < arr2.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], 10);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, ResizeGrow)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
arr.resize(10);
|
||||
EXPECT_EQ(arr.size(), 10u);
|
||||
|
||||
// Original data should be preserved
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
EXPECT_EQ(arr[i], static_cast<int>(i));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, ResizeShrink)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
arr.resize(5);
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], static_cast<int>(i));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, ResizeSameSize)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
int* original_data = arr.data();
|
||||
arr.fill(42);
|
||||
|
||||
arr.resize(5);
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
EXPECT_EQ(arr.data(), original_data);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], 42);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, Deallocate)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
EXPECT_FALSE(arr.empty());
|
||||
|
||||
arr.deallocate();
|
||||
EXPECT_TRUE(arr.empty());
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
EXPECT_EQ(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignSpan)
|
||||
{
|
||||
DynamicHeapArray<int> arr;
|
||||
int source[] = {1, 2, 3, 4, 5};
|
||||
std::span<const int> sp(source);
|
||||
|
||||
arr.assign(sp);
|
||||
EXPECT_EQ(arr.size(), 5u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignBeginEnd)
|
||||
{
|
||||
DynamicHeapArray<int> arr;
|
||||
int source[] = {10, 20, 30};
|
||||
|
||||
arr.assign(std::begin(source), std::end(source));
|
||||
EXPECT_EQ(arr.size(), 3u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignBeginCount)
|
||||
{
|
||||
DynamicHeapArray<int> arr;
|
||||
int source[] = {100, 200, 300, 400};
|
||||
|
||||
arr.assign(source, 4);
|
||||
EXPECT_EQ(arr.size(), 4u);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignCopy)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 3);
|
||||
|
||||
DynamicHeapArray<int> arr2;
|
||||
arr2.assign(arr1);
|
||||
|
||||
EXPECT_EQ(arr2.size(), arr1.size());
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], arr1[i]);
|
||||
EXPECT_NE(arr1.data(), arr2.data());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignMove)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
int* original_data = arr1.data();
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 3);
|
||||
|
||||
DynamicHeapArray<int> arr2;
|
||||
arr2.assign(std::move(arr1));
|
||||
|
||||
EXPECT_EQ(arr2.size(), 5u);
|
||||
EXPECT_EQ(arr2.data(), original_data);
|
||||
EXPECT_EQ(arr1.size(), 0u);
|
||||
EXPECT_EQ(arr1.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignEmpty)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
arr.fill(42);
|
||||
|
||||
int* empty = nullptr;
|
||||
arr.assign(empty, static_cast<size_t>(0));
|
||||
|
||||
EXPECT_TRUE(arr.empty());
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AssignSameSize)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
arr.fill(10);
|
||||
int* original_data = arr.data();
|
||||
|
||||
int source[] = {1, 2, 3, 4, 5};
|
||||
arr.assign(source, 5);
|
||||
|
||||
// Should reuse existing buffer
|
||||
EXPECT_EQ(arr.data(), original_data);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], source[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, Span)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i * 2);
|
||||
|
||||
std::span<int> sp = arr.span();
|
||||
EXPECT_EQ(sp.size(), 5u);
|
||||
EXPECT_EQ(sp.data(), arr.data());
|
||||
|
||||
for (size_t i = 0; i < sp.size(); ++i)
|
||||
EXPECT_EQ(sp[i], static_cast<int>(i * 2));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SpanWithOffset)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
std::span<int> sp = arr.span(3, 4);
|
||||
EXPECT_EQ(sp.size(), 4u);
|
||||
EXPECT_EQ(sp.data(), arr.data() + 3);
|
||||
EXPECT_EQ(sp[0], 3);
|
||||
EXPECT_EQ(sp[3], 6);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SpanWithOffsetClamp)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
// Request more than available
|
||||
std::span<int> sp = arr.span(7, 100);
|
||||
EXPECT_EQ(sp.size(), 3u);
|
||||
EXPECT_EQ(sp[0], 7);
|
||||
EXPECT_EQ(sp[2], 9);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, SpanWithOffsetOutOfRange)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
std::span<int> sp = arr.span(10);
|
||||
EXPECT_TRUE(sp.empty());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, CSpan)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i * 2);
|
||||
|
||||
const auto& carr = arr;
|
||||
std::span<const int> csp = carr.cspan();
|
||||
EXPECT_EQ(csp.size(), 5u);
|
||||
EXPECT_EQ(csp.data(), carr.data());
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, CSpanWithOffset)
|
||||
{
|
||||
DynamicHeapArray<int> arr(10);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<int>(i);
|
||||
|
||||
const auto& carr = arr;
|
||||
std::span<const int> csp = carr.cspan(2, 3);
|
||||
EXPECT_EQ(csp.size(), 3u);
|
||||
EXPECT_EQ(csp[0], 2);
|
||||
EXPECT_EQ(csp[2], 4);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, CopyAssignment)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
arr1[i] = static_cast<int>(i * 2);
|
||||
|
||||
DynamicHeapArray<int> arr2;
|
||||
arr2 = arr1;
|
||||
|
||||
EXPECT_EQ(arr2.size(), arr1.size());
|
||||
EXPECT_NE(arr2.data(), arr1.data());
|
||||
for (size_t i = 0; i < arr1.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], arr1[i]);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, MoveAssignment)
|
||||
{
|
||||
DynamicHeapArray<int> arr1(5);
|
||||
int* original_data = arr1.data();
|
||||
arr1.fill(42);
|
||||
|
||||
DynamicHeapArray<int> arr2;
|
||||
arr2 = std::move(arr1);
|
||||
|
||||
EXPECT_EQ(arr2.data(), original_data);
|
||||
EXPECT_EQ(arr2.size(), 5u);
|
||||
EXPECT_EQ(arr1.size(), 0u);
|
||||
EXPECT_EQ(arr1.data(), nullptr);
|
||||
|
||||
for (size_t i = 0; i < arr2.size(); ++i)
|
||||
EXPECT_EQ(arr2[i], 42);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AlignedAllocation)
|
||||
{
|
||||
constexpr size_t alignment = 64;
|
||||
DynamicHeapArray<int, alignment> arr(16);
|
||||
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(arr.data());
|
||||
EXPECT_EQ(addr % alignment, 0u);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, AlignedResize)
|
||||
{
|
||||
constexpr size_t alignment = 64;
|
||||
DynamicHeapArray<int, alignment> arr(8);
|
||||
arr.fill(42);
|
||||
|
||||
arr.resize(32);
|
||||
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(arr.data());
|
||||
EXPECT_EQ(addr % alignment, 0u);
|
||||
|
||||
// Original data preserved
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
EXPECT_EQ(arr[i], 42);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, DifferentTypes)
|
||||
{
|
||||
DynamicHeapArray<double> arr(3);
|
||||
arr[0] = 1.5;
|
||||
arr[1] = 2.5;
|
||||
arr[2] = 3.5;
|
||||
|
||||
EXPECT_DOUBLE_EQ(arr[0], 1.5);
|
||||
EXPECT_DOUBLE_EQ(arr[1], 2.5);
|
||||
EXPECT_DOUBLE_EQ(arr[2], 3.5);
|
||||
EXPECT_EQ(arr.size_bytes(), 3u * sizeof(double));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, RangeBasedFor)
|
||||
{
|
||||
DynamicHeapArray<int> arr(5);
|
||||
int val = 0;
|
||||
for (auto& elem : arr)
|
||||
elem = val++;
|
||||
|
||||
val = 0;
|
||||
for (const auto& elem : arr)
|
||||
{
|
||||
EXPECT_EQ(elem, val);
|
||||
++val;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, ByteArray)
|
||||
{
|
||||
DynamicHeapArray<std::byte> arr(16);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
arr[i] = static_cast<std::byte>(i);
|
||||
|
||||
EXPECT_EQ(arr.size(), 16u);
|
||||
EXPECT_EQ(arr.size_bytes(), 16u);
|
||||
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
EXPECT_EQ(arr[i], static_cast<std::byte>(i));
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, EmptyRangeConstruction)
|
||||
{
|
||||
int* empty = nullptr;
|
||||
DynamicHeapArray<int> arr(empty, empty);
|
||||
|
||||
EXPECT_TRUE(arr.empty());
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
EXPECT_EQ(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, EmptyCountConstruction)
|
||||
{
|
||||
int* ptr = nullptr;
|
||||
DynamicHeapArray<int> arr(ptr, static_cast<size_t>(0));
|
||||
|
||||
EXPECT_TRUE(arr.empty());
|
||||
EXPECT_EQ(arr.size(), 0u);
|
||||
EXPECT_EQ(arr.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, CopyAssignmentCopiesWrongDirection)
|
||||
{
|
||||
FixedHeapArray<int, 4> src;
|
||||
FixedHeapArray<int, 4> dst;
|
||||
|
||||
src[0] = 1;
|
||||
src[1] = 2;
|
||||
src[2] = 3;
|
||||
src[3] = 4;
|
||||
dst[0] = 0;
|
||||
dst[1] = 0;
|
||||
dst[2] = 0;
|
||||
dst[3] = 0;
|
||||
|
||||
dst = src; // Should copy src -> dst
|
||||
|
||||
// After assignment, dst should have src's values
|
||||
EXPECT_EQ(dst[0], 1);
|
||||
EXPECT_EQ(dst[1], 2);
|
||||
EXPECT_EQ(dst[2], 3);
|
||||
EXPECT_EQ(dst[3], 4);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, EqualityOperatorMissingReturnTrue)
|
||||
{
|
||||
FixedHeapArray<int, 4> a;
|
||||
FixedHeapArray<int, 4> b;
|
||||
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
a[2] = 3;
|
||||
a[3] = 4;
|
||||
b[0] = 1;
|
||||
b[1] = 2;
|
||||
b[2] = 3;
|
||||
b[3] = 4;
|
||||
|
||||
// Both arrays have identical content, should return true
|
||||
EXPECT_TRUE(a == b);
|
||||
}
|
||||
|
||||
TEST(FixedHeapArray, InequalityOperatorReturnTrue)
|
||||
{
|
||||
FixedHeapArray<int, 4> a;
|
||||
FixedHeapArray<int, 4> b;
|
||||
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
a[2] = 3;
|
||||
a[3] = 4;
|
||||
b[0] = 1;
|
||||
b[1] = 2;
|
||||
b[2] = 3;
|
||||
b[3] = 5; // Last element differs
|
||||
|
||||
// Arrays differ, inequality should return true
|
||||
EXPECT_TRUE(a != b);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, EqualityOperatorReturnTrue)
|
||||
{
|
||||
DynamicHeapArray<int> a(4);
|
||||
DynamicHeapArray<int> b(4);
|
||||
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
a[2] = 3;
|
||||
a[3] = 4;
|
||||
b[0] = 1;
|
||||
b[1] = 2;
|
||||
b[2] = 3;
|
||||
b[3] = 4;
|
||||
|
||||
// Both arrays have identical content, should return true
|
||||
EXPECT_TRUE(a == b);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, EqualityOperatorWhenSizesDiffer)
|
||||
{
|
||||
DynamicHeapArray<int> a(4);
|
||||
DynamicHeapArray<int> b(8);
|
||||
|
||||
// Different sizes should NOT be equal
|
||||
EXPECT_FALSE(a == b);
|
||||
}
|
||||
|
||||
TEST(DynamicHeapArray, InequalityOperatorWhenSizesDiffer)
|
||||
{
|
||||
DynamicHeapArray<int> a(4);
|
||||
DynamicHeapArray<int> b(8);
|
||||
|
||||
// Different sizes should be not-equal
|
||||
EXPECT_TRUE(a != b);
|
||||
}
|
||||
1529
src/common-tests/small_string_tests.cpp
Normal file
1529
src/common-tests/small_string_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,9 @@
|
||||
template<typename T, std::size_t SIZE, std::size_t ALIGNMENT = 0>
|
||||
class FixedHeapArray
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T is trivially copyable");
|
||||
static_assert(std::is_standard_layout_v<T>, "T is standard layout");
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
@@ -73,12 +76,12 @@ public:
|
||||
|
||||
void swap(this_type& move) { std::swap(m_data, move.m_data); }
|
||||
|
||||
std::span<T, SIZE> span() { return std::span<T, SIZE>(m_data); }
|
||||
std::span<const T, SIZE> cspan() const { return std::span<const T, SIZE>(m_data); }
|
||||
std::span<T, SIZE> span() { return std::span<T, SIZE>(m_data, m_data + SIZE); }
|
||||
std::span<const T, SIZE> cspan() const { return std::span<const T, SIZE>(m_data, m_data + SIZE); }
|
||||
|
||||
this_type& operator=(const this_type& rhs)
|
||||
{
|
||||
std::copy(begin(), end(), rhs.cbegin());
|
||||
std::copy(rhs.cbegin(), rhs.cend(), begin());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -90,24 +93,12 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
#define RELATIONAL_OPERATOR(op) \
|
||||
bool operator op(const this_type& rhs) const \
|
||||
{ \
|
||||
for (size_type i = 0; i < SIZE; i++) \
|
||||
{ \
|
||||
if (!(m_data[i] op rhs.m_data[i])) \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
|
||||
RELATIONAL_OPERATOR(==);
|
||||
RELATIONAL_OPERATOR(!=);
|
||||
RELATIONAL_OPERATOR(<);
|
||||
RELATIONAL_OPERATOR(<=);
|
||||
RELATIONAL_OPERATOR(>);
|
||||
RELATIONAL_OPERATOR(>=);
|
||||
|
||||
#undef RELATIONAL_OPERATOR
|
||||
bool operator==(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) == 0); }
|
||||
bool operator!=(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) != 0); }
|
||||
bool operator<(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) < 0); }
|
||||
bool operator<=(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) <= 0); }
|
||||
bool operator>(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) < 0); }
|
||||
bool operator>=(const this_type& rhs) const { return (std::memcmp(m_data, rhs.m_data, SIZE * sizeof(T)) >= 0); }
|
||||
|
||||
private:
|
||||
void allocate()
|
||||
@@ -372,26 +363,87 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
#define RELATIONAL_OPERATOR(op, size_op) \
|
||||
bool operator op(const this_type& rhs) const \
|
||||
{ \
|
||||
if (m_size != rhs.m_size) \
|
||||
return m_size size_op rhs.m_size; \
|
||||
for (size_type i = 0; i < m_size; i++) \
|
||||
{ \
|
||||
if (!(m_data[i] op rhs.m_data[i])) \
|
||||
return false; \
|
||||
} \
|
||||
bool operator==(const this_type& rhs) const
|
||||
{
|
||||
if (m_size != rhs.m_size)
|
||||
return false;
|
||||
|
||||
if (m_size == 0)
|
||||
return true;
|
||||
|
||||
return (std::memcmp(m_data, rhs.m_data, m_size * sizeof(T)) == 0);
|
||||
}
|
||||
|
||||
RELATIONAL_OPERATOR(==, !=);
|
||||
RELATIONAL_OPERATOR(!=, ==);
|
||||
RELATIONAL_OPERATOR(<, <);
|
||||
RELATIONAL_OPERATOR(<=, <=);
|
||||
RELATIONAL_OPERATOR(>, >);
|
||||
RELATIONAL_OPERATOR(>=, >=);
|
||||
bool operator!=(const this_type& rhs) const
|
||||
{
|
||||
if (m_size != rhs.m_size)
|
||||
return true;
|
||||
|
||||
#undef RELATIONAL_OPERATOR
|
||||
if (m_size == 0)
|
||||
return false;
|
||||
|
||||
return (std::memcmp(m_data, rhs.m_data, m_size * sizeof(T)) != 0);
|
||||
}
|
||||
|
||||
bool operator<(const this_type& rhs) const
|
||||
{
|
||||
const size_type min_size = std::min(m_size, rhs.m_size);
|
||||
for (size_type i = 0; i < min_size; i++)
|
||||
{
|
||||
if (!(m_data[i] < rhs.m_data[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_size != rhs.m_size)
|
||||
return m_size < rhs.m_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator<=(const this_type& rhs) const
|
||||
{
|
||||
const size_type min_size = std::min(m_size, rhs.m_size);
|
||||
for (size_type i = 0; i < min_size; i++)
|
||||
{
|
||||
if (!(m_data[i] <= rhs.m_data[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_size != rhs.m_size)
|
||||
return m_size <= rhs.m_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator>(const this_type& rhs) const
|
||||
{
|
||||
const size_type min_size = std::min(m_size, rhs.m_size);
|
||||
for (size_type i = 0; i < min_size; i++)
|
||||
{
|
||||
if (!(m_data[i] > rhs.m_data[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_size != rhs.m_size)
|
||||
return m_size > rhs.m_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator>=(const this_type& rhs) const
|
||||
{
|
||||
const size_type min_size = std::min(m_size, rhs.m_size);
|
||||
for (size_type i = 0; i < min_size; i++)
|
||||
{
|
||||
if (!(m_data[i] >= rhs.m_data[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_size != rhs.m_size)
|
||||
return m_size >= rhs.m_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void internal_resize(size_t size, T* prev_ptr, [[maybe_unused]] size_t prev_size)
|
||||
|
||||
@@ -86,7 +86,7 @@ void SmallStringBase::reserve(u32 new_reserve)
|
||||
m_on_heap = true;
|
||||
}
|
||||
|
||||
m_buffer_size = new_reserve;
|
||||
m_buffer_size = real_reserve;
|
||||
}
|
||||
|
||||
void SmallStringBase::shrink_to_fit()
|
||||
@@ -100,6 +100,7 @@ void SmallStringBase::shrink_to_fit()
|
||||
std::free(m_buffer);
|
||||
m_buffer = nullptr;
|
||||
m_buffer_size = 0;
|
||||
m_on_heap = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ std::string_view SmallStringBase::view() const
|
||||
|
||||
SmallStringBase& SmallStringBase::operator=(SmallStringBase&& move)
|
||||
{
|
||||
assign(move);
|
||||
assign(std::move(move));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -160,15 +161,16 @@ SmallStringBase& SmallStringBase::operator=(const SmallStringBase& copy)
|
||||
|
||||
void SmallStringBase::make_room_for(u32 space)
|
||||
{
|
||||
const u32 required_size = m_length + space + 1;
|
||||
if (m_buffer_size >= required_size)
|
||||
const u32 required_length = m_length + space;
|
||||
if (m_buffer_size > required_length)
|
||||
return;
|
||||
|
||||
reserve(std::max(required_size, m_buffer_size * 2));
|
||||
reserve(std::max(required_length, m_buffer_size * 2));
|
||||
}
|
||||
|
||||
void SmallStringBase::append(const char* str, u32 length)
|
||||
{
|
||||
DebugAssert(str != m_buffer); // appending self is not allowed
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
@@ -215,6 +217,8 @@ void SmallStringBase::append_hex(const void* data, size_t len, bool comma_separa
|
||||
m_buffer[m_length++] = hex_char(bytes[i] & 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer[m_length] = '\0';
|
||||
}
|
||||
|
||||
void SmallStringBase::prepend(const char* str, u32 length)
|
||||
@@ -222,6 +226,7 @@ void SmallStringBase::prepend(const char* str, u32 length)
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
DebugAssert(str != m_buffer); // appending self is not allowed
|
||||
make_room_for(length);
|
||||
|
||||
DebugAssert((length + m_length) < m_buffer_size);
|
||||
@@ -239,11 +244,13 @@ void SmallStringBase::append(char c)
|
||||
|
||||
void SmallStringBase::append(const SmallStringBase& str)
|
||||
{
|
||||
DebugAssert(&str != this); // appending self is not allowed
|
||||
append(str.m_buffer, str.m_length);
|
||||
}
|
||||
|
||||
void SmallStringBase::append(const char* str)
|
||||
{
|
||||
DebugAssert(str != m_buffer); // appending self is not allowed
|
||||
append(str, static_cast<u32>(std::strlen(str)));
|
||||
}
|
||||
|
||||
@@ -254,6 +261,7 @@ void SmallStringBase::append(const std::string& str)
|
||||
|
||||
void SmallStringBase::append(const std::string_view str)
|
||||
{
|
||||
DebugAssert(str.data() != m_buffer); // appending self is not allowed
|
||||
append(str.data(), static_cast<u32>(str.length()));
|
||||
}
|
||||
|
||||
@@ -307,11 +315,13 @@ void SmallStringBase::prepend(char c)
|
||||
|
||||
void SmallStringBase::prepend(const SmallStringBase& str)
|
||||
{
|
||||
DebugAssert(&str != this); // prepending self is not allowed
|
||||
prepend(str.m_buffer, str.m_length);
|
||||
}
|
||||
|
||||
void SmallStringBase::prepend(const char* str)
|
||||
{
|
||||
DebugAssert(str != m_buffer); // prepending self is not allowed
|
||||
prepend(str, static_cast<u32>(std::strlen(str)));
|
||||
}
|
||||
|
||||
@@ -322,6 +332,7 @@ void SmallStringBase::prepend(const std::string& str)
|
||||
|
||||
void SmallStringBase::prepend(const std::string_view str)
|
||||
{
|
||||
DebugAssert(str.data() != m_buffer); // prepending self is not allowed
|
||||
prepend(str.data(), static_cast<u32>(str.length()));
|
||||
}
|
||||
|
||||
@@ -345,7 +356,10 @@ void SmallStringBase::prepend_vsprintf(const char* format, va_list ArgPtr)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int ret = std::vsnprintf(buffer, buffer_size, format, ArgPtr);
|
||||
std::va_list ap_copy;
|
||||
va_copy(ap_copy, ArgPtr);
|
||||
int ret = std::vsnprintf(buffer, buffer_size, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
if (ret < 0 || (static_cast<u32>(ret) >= (buffer_size - 1)))
|
||||
{
|
||||
buffer_size *= 2;
|
||||
@@ -367,11 +381,13 @@ void SmallStringBase::prepend_vsprintf(const char* format, va_list ArgPtr)
|
||||
|
||||
void SmallStringBase::insert(s32 offset, const char* str)
|
||||
{
|
||||
DebugAssert(str != m_buffer); // inserting self is not allowed
|
||||
insert(offset, str, static_cast<u32>(std::strlen(str)));
|
||||
}
|
||||
|
||||
void SmallStringBase::insert(s32 offset, const SmallStringBase& str)
|
||||
{
|
||||
DebugAssert(&str != this); // inserting self is not allowed
|
||||
insert(offset, str, str.m_length);
|
||||
}
|
||||
|
||||
@@ -393,7 +409,7 @@ void SmallStringBase::insert(s32 offset, const char* str, u32 length)
|
||||
DebugAssert(real_offset <= m_length);
|
||||
const u32 chars_after_offset = m_length - real_offset;
|
||||
if (chars_after_offset > 0)
|
||||
std::memmove(m_buffer + offset + length, m_buffer + offset, chars_after_offset);
|
||||
std::memmove(m_buffer + real_offset + length, m_buffer + real_offset, chars_after_offset);
|
||||
|
||||
// insert the string
|
||||
std::memcpy(m_buffer + real_offset, str, length);
|
||||
@@ -410,6 +426,7 @@ void SmallStringBase::insert(s32 offset, const std::string& str)
|
||||
|
||||
void SmallStringBase::insert(s32 offset, const std::string_view str)
|
||||
{
|
||||
DebugAssert(str.data() != m_buffer); // inserting self is not allowed
|
||||
insert(offset, str.data(), static_cast<u32>(str.size()));
|
||||
}
|
||||
|
||||
@@ -497,6 +514,7 @@ void SmallStringBase::assign(const std::wstring_view wstr)
|
||||
}
|
||||
|
||||
m_length = static_cast<u32>(mblen);
|
||||
m_buffer[m_length] = '\0';
|
||||
}
|
||||
|
||||
std::wstring SmallStringBase::wstring() const
|
||||
@@ -572,7 +590,8 @@ bool SmallStringBase::iequals(const char* otherText) const
|
||||
|
||||
bool SmallStringBase::iequals(const SmallStringBase& str) const
|
||||
{
|
||||
return (m_length == str.m_length && (m_length == 0 || std::strcmp(m_buffer, str.m_buffer) == 0));
|
||||
return (m_length == str.m_length &&
|
||||
(m_length == 0 || StringUtil::Strncasecmp(m_buffer, str.m_buffer, m_length) == 0));
|
||||
}
|
||||
|
||||
bool SmallStringBase::iequals(const std::string_view str) const
|
||||
@@ -771,6 +790,9 @@ bool SmallStringBase::ends_with(const std::string& str, bool case_sensitive) con
|
||||
|
||||
void SmallStringBase::clear()
|
||||
{
|
||||
if (m_buffer_size == 0)
|
||||
return;
|
||||
|
||||
// in debug, zero whole string, in release, zero only the first character
|
||||
#if _DEBUG
|
||||
std::memset(m_buffer, 0, m_buffer_size);
|
||||
@@ -823,6 +845,9 @@ u32 SmallStringBase::count(char ch) const
|
||||
u32 SmallStringBase::replace(const char* search, const char* replacement)
|
||||
{
|
||||
const u32 search_length = static_cast<u32>(std::strlen(search));
|
||||
if (search_length == 0)
|
||||
return 0;
|
||||
|
||||
const u32 replacement_length = static_cast<u32>(std::strlen(replacement));
|
||||
|
||||
s32 offset = 0;
|
||||
@@ -833,17 +858,19 @@ u32 SmallStringBase::replace(const char* search, const char* replacement)
|
||||
if (offset < 0)
|
||||
break;
|
||||
|
||||
const u32 chars_after_offset = (m_length - static_cast<u32>(offset));
|
||||
DebugAssert(chars_after_offset >= search_length);
|
||||
|
||||
const u32 new_length = m_length - search_length + replacement_length;
|
||||
reserve(new_length);
|
||||
m_length = new_length;
|
||||
|
||||
const u32 chars_after_offset = (m_length - static_cast<u32>(offset));
|
||||
DebugAssert(chars_after_offset >= search_length);
|
||||
if (chars_after_offset > search_length)
|
||||
{
|
||||
std::memmove(&m_buffer[static_cast<u32>(offset) + replacement_length],
|
||||
&m_buffer[static_cast<u32>(offset) + search_length], chars_after_offset - search_length);
|
||||
std::memcpy(&m_buffer[static_cast<u32>(offset)], replacement, replacement_length);
|
||||
m_buffer[m_length] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -861,22 +888,26 @@ u32 SmallStringBase::replace(const char* search, const char* replacement)
|
||||
|
||||
void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller)
|
||||
{
|
||||
// if going larger, or we don't own the buffer, realloc
|
||||
if (new_size >= m_buffer_size)
|
||||
if (new_size > m_length)
|
||||
{
|
||||
// expanding - ensure we have space
|
||||
reserve(new_size);
|
||||
|
||||
if (m_length < new_size)
|
||||
{
|
||||
std::memset(m_buffer + m_length, fill, m_buffer_size - m_length - 1);
|
||||
}
|
||||
|
||||
// fill the expanded area with the fill character
|
||||
std::memset(m_buffer + m_length, fill, new_size - m_length);
|
||||
m_length = new_size;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// zero remaining unused buffer in debug
|
||||
std::memset(m_buffer + m_length, 0, m_buffer_size - new_size);
|
||||
#else
|
||||
m_buffer[m_length] = 0;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// update length and terminator
|
||||
#if _DEBUG
|
||||
// shrinking or same size - update length and terminator
|
||||
#ifdef _DEBUG
|
||||
std::memset(m_buffer + new_size, 0, m_buffer_size - new_size);
|
||||
#else
|
||||
m_buffer[new_size] = 0;
|
||||
@@ -975,7 +1006,7 @@ void SmallStringBase::erase(s32 offset, s32 count)
|
||||
const u32 after_erase_block = m_length - real_offset - real_count;
|
||||
DebugAssert(after_erase_block > 0);
|
||||
|
||||
std::memmove(m_buffer + offset, m_buffer + real_offset + real_count, after_erase_block);
|
||||
std::memmove(m_buffer + real_offset, m_buffer + real_offset + real_count, after_erase_block);
|
||||
m_length = m_length - real_count;
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
@@ -184,6 +184,9 @@ public:
|
||||
// returns the end of the string (pointer is past the last character)
|
||||
ALWAYS_INLINE const char* end_ptr() const { return m_buffer + m_length; }
|
||||
|
||||
// returns true if the string is heap-allocated
|
||||
ALWAYS_INLINE bool is_heap_allocated() const { return m_on_heap; }
|
||||
|
||||
// STL adapters
|
||||
ALWAYS_INLINE char& front() { return m_buffer[0]; }
|
||||
ALWAYS_INLINE const char& front() const { return m_buffer[0]; }
|
||||
@@ -287,7 +290,7 @@ public:
|
||||
ALWAYS_INLINE SmallStackString(SmallStringBase&& move)
|
||||
{
|
||||
init();
|
||||
assign(move);
|
||||
move_assign(std::move(move));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit SmallStackString(const SmallStackString& copy)
|
||||
@@ -299,7 +302,7 @@ public:
|
||||
ALWAYS_INLINE explicit SmallStackString(SmallStackString&& move)
|
||||
{
|
||||
init();
|
||||
assign(move);
|
||||
move_assign(std::move(move));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE explicit SmallStackString(const std::string& str)
|
||||
@@ -322,7 +325,7 @@ public:
|
||||
|
||||
ALWAYS_INLINE SmallStackString& operator=(SmallStringBase&& move)
|
||||
{
|
||||
assign(move);
|
||||
move_assign(std::move(move));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -334,7 +337,7 @@ public:
|
||||
|
||||
ALWAYS_INLINE SmallStackString& operator=(SmallStackString&& move)
|
||||
{
|
||||
assign(move);
|
||||
move_assign(std::move(move));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -378,6 +381,15 @@ private:
|
||||
m_stack_buffer[0] = '\0';
|
||||
#endif
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void move_assign(SmallStringBase&& move)
|
||||
{
|
||||
// only move if on the heap, otherwise copy
|
||||
if (move.is_heap_allocated())
|
||||
SmallStringBase::assign(std::move(move));
|
||||
else
|
||||
assign(move.data(), move.length());
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
Reference in New Issue
Block a user