feat: add SmallMap implementation and associated tests
Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
280aff5465
commit
b40c19ddc7
@ -1,11 +1,18 @@
|
|||||||
cmake_minimum_required(VERSION 3.27)
|
cmake_minimum_required(VERSION 3.27)
|
||||||
project(instructed LANGUAGES CXX)
|
project(instructed LANGUAGES CXX)
|
||||||
|
|
||||||
# Optionally build examples
|
include(CTest)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
# Optionally build examples & Tests
|
||||||
option(BUILD_EXAMPLES "Build example programs" ON)
|
option(BUILD_EXAMPLES "Build example programs" ON)
|
||||||
|
option(BUILD_TESTS "Build tests" ON)
|
||||||
|
|
||||||
# Load third-party libraries
|
# Load third-party libraries
|
||||||
add_subdirectory(third_party)
|
add_subdirectory(third_party)
|
||||||
|
|
||||||
|
# Add util library
|
||||||
|
add_subdirectory(util)
|
||||||
|
|
||||||
# Add tilemap library and examples
|
# Add tilemap library and examples
|
||||||
add_subdirectory(tilemap)
|
add_subdirectory(tilemap)
|
||||||
|
10
util/CMakeLists.txt
Normal file
10
util/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
|
||||||
|
# Define util library sources (header-only for now)
|
||||||
|
add_library(istd_util INTERFACE)
|
||||||
|
target_include_directories(istd_util INTERFACE include)
|
||||||
|
target_compile_features(istd_util INTERFACE cxx_std_23)
|
||||||
|
|
||||||
|
if (BUILD_TESTS)
|
||||||
|
add_subdirectory("test/")
|
||||||
|
endif()
|
160
util/include/istd_util/small_map.h
Normal file
160
util/include/istd_util/small_map.h
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* @file small_map.h
|
||||||
|
* @brief Provides a simple sorted map implementation for small key-value sets.
|
||||||
|
*/
|
||||||
|
#ifndef ISTD_UTIL_SMALL_MAP_H
|
||||||
|
#define ISTD_UTIL_SMALL_MAP_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace istd {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A simple sorted map for small key-value sets.
|
||||||
|
*
|
||||||
|
* Stores entries in a sorted vector and provides basic map operations.
|
||||||
|
*
|
||||||
|
* @tparam T_Key Type of the key.
|
||||||
|
* @tparam T_Value Type of the value.
|
||||||
|
*/
|
||||||
|
template<typename T_Key, typename T_Value>
|
||||||
|
class SmallMap {
|
||||||
|
/**
|
||||||
|
* @brief Internal entry structure for key-value pairs.
|
||||||
|
*/
|
||||||
|
struct Entry {
|
||||||
|
T_Key key; ///< Key of the entry
|
||||||
|
T_Value value; ///< Value of the entry
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Comparison operator for sorting entries by key.
|
||||||
|
* @param other_key Key to compare with.
|
||||||
|
* @return True if this entry's key is less than other_key.
|
||||||
|
*/
|
||||||
|
bool operator<(const T_Key &other_key) const {
|
||||||
|
return key < other_key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Entry> entries_; ///< Container for all entries
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Default constructor.
|
||||||
|
*/
|
||||||
|
SmallMap() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inserts a new key-value pair.
|
||||||
|
* @param key Key to insert.
|
||||||
|
* @param value Value to associate with the key.
|
||||||
|
* @throws std::invalid_argument if the key already exists.
|
||||||
|
*/
|
||||||
|
void insert(const T_Key &key, const T_Value &value) {
|
||||||
|
// Binary search for existing key
|
||||||
|
auto it = std::lower_bound(entries_.begin(), entries_.end(), key);
|
||||||
|
|
||||||
|
if (it != entries_.end() && it->key == key) {
|
||||||
|
// Key exists, but it shouldn't
|
||||||
|
throw std::invalid_argument("Key already exists in SmallMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new entry in sorted order
|
||||||
|
entries_.insert(it, {key, value});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Accesses the value associated with a key.
|
||||||
|
* @tparam Self Type of the SmallMap instance.
|
||||||
|
* @param key Key to look up.
|
||||||
|
* @return Reference to the value associated with the key.
|
||||||
|
* @throws std::out_of_range if the key does not exist.
|
||||||
|
*/
|
||||||
|
template<typename Self>
|
||||||
|
auto &&operator[](this Self &&self, const T_Key &key) {
|
||||||
|
auto it = std::lower_bound(
|
||||||
|
self.entries_.begin(), self.entries_.end(), key
|
||||||
|
);
|
||||||
|
|
||||||
|
if (it != self.entries_.end() && it->key == key) {
|
||||||
|
return it->value; // Return existing value
|
||||||
|
}
|
||||||
|
throw std::out_of_range("Key not found in SmallMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes all entries from the map.
|
||||||
|
*/
|
||||||
|
void clear() noexcept {
|
||||||
|
entries_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of entries in the map.
|
||||||
|
* @return Number of key-value pairs.
|
||||||
|
*/
|
||||||
|
size_t size() const noexcept {
|
||||||
|
return entries_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the map is empty.
|
||||||
|
* @return True if the map contains no entries.
|
||||||
|
*/
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return entries_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the entry with the specified key.
|
||||||
|
* @param key Key to remove.
|
||||||
|
* @throws std::out_of_range if the key does not exist.
|
||||||
|
*/
|
||||||
|
void erase(const T_Key &key) {
|
||||||
|
auto it = std::lower_bound(entries_.begin(), entries_.end(), key);
|
||||||
|
if (it != entries_.end() && it->key == key) {
|
||||||
|
entries_.erase(it); // Remove the entry
|
||||||
|
} else {
|
||||||
|
throw std::out_of_range("Key not found in SmallMap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning of the entries.
|
||||||
|
* @return Iterator to the first entry.
|
||||||
|
*/
|
||||||
|
auto begin() noexcept {
|
||||||
|
return entries_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end of the entries.
|
||||||
|
* @return Iterator to one past the last entry.
|
||||||
|
*/
|
||||||
|
auto end() noexcept {
|
||||||
|
return entries_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a const iterator to the beginning of the entries.
|
||||||
|
* @return Const iterator to the first entry.
|
||||||
|
*/
|
||||||
|
auto cbegin() const noexcept {
|
||||||
|
return entries_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a const iterator to the end of the entries.
|
||||||
|
* @return Const iterator to one past the last entry.
|
||||||
|
*/
|
||||||
|
auto cend() const noexcept {
|
||||||
|
return entries_.cend();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace istd
|
||||||
|
|
||||||
|
#endif
|
1
util/include/util.h
Normal file
1
util/include/util.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "istd_util/small_map.h"
|
9
util/test/CMakeLists.txt
Normal file
9
util/test/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
|
||||||
|
add_executable(test_small_map small_map.cpp)
|
||||||
|
target_link_libraries(test_small_map PRIVATE istd_util)
|
||||||
|
target_compile_features(test_small_map PRIVATE cxx_std_23)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
enable_testing()
|
||||||
|
add_test(NAME util_small_map COMMAND test_small_map)
|
77
util/test/small_map.cpp
Normal file
77
util/test/small_map.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include "istd_util/small_map.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using namespace istd;
|
||||||
|
// Test insert and size
|
||||||
|
SmallMap<int, int> map;
|
||||||
|
assert(map.empty());
|
||||||
|
map.insert(1, 10);
|
||||||
|
map.insert(2, 20);
|
||||||
|
map.insert(3, 30);
|
||||||
|
assert(map.size() == 3);
|
||||||
|
assert(!map.empty());
|
||||||
|
|
||||||
|
// Test operator[]
|
||||||
|
assert(map[1] == 10);
|
||||||
|
assert(map[2] == 20);
|
||||||
|
assert(map[3] == 30);
|
||||||
|
|
||||||
|
// Test erase
|
||||||
|
map.erase(2);
|
||||||
|
assert(map.size() == 2);
|
||||||
|
assert(map[1] == 10);
|
||||||
|
assert(map[3] == 30);
|
||||||
|
|
||||||
|
// Test clear
|
||||||
|
map.clear();
|
||||||
|
assert(map.empty());
|
||||||
|
|
||||||
|
// Test duplicate insert throws
|
||||||
|
SmallMap<int, int> map2;
|
||||||
|
map2.insert(5, 50);
|
||||||
|
bool thrown = false;
|
||||||
|
try {
|
||||||
|
map2.insert(5, 60);
|
||||||
|
} catch (const std::invalid_argument &) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
assert(thrown);
|
||||||
|
|
||||||
|
// Test out_of_range on operator[]
|
||||||
|
thrown = false;
|
||||||
|
try {
|
||||||
|
(void)map2[99];
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
assert(thrown);
|
||||||
|
|
||||||
|
// Test erase throws on missing key
|
||||||
|
thrown = false;
|
||||||
|
try {
|
||||||
|
map2.erase(99);
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
thrown = true;
|
||||||
|
}
|
||||||
|
assert(thrown);
|
||||||
|
|
||||||
|
// Test iterator
|
||||||
|
map2.insert(6, 60);
|
||||||
|
map2.insert(7, 70);
|
||||||
|
int sum = 0;
|
||||||
|
for (auto it = map2.begin(); it != map2.end(); ++it) {
|
||||||
|
sum += it->value;
|
||||||
|
}
|
||||||
|
assert(sum == 180); // 50 + 60 + 70
|
||||||
|
|
||||||
|
// Test const iterators
|
||||||
|
const auto &cmap = map2;
|
||||||
|
sum = 0;
|
||||||
|
for (auto it = cmap.cbegin(); it != cmap.cend(); ++it) {
|
||||||
|
sum += it->value;
|
||||||
|
}
|
||||||
|
assert(sum == 180);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user