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)
|
||||
project(instructed LANGUAGES CXX)
|
||||
|
||||
# Optionally build examples
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
# Optionally build examples & Tests
|
||||
option(BUILD_EXAMPLES "Build example programs" ON)
|
||||
option(BUILD_TESTS "Build tests" ON)
|
||||
|
||||
# Load third-party libraries
|
||||
add_subdirectory(third_party)
|
||||
|
||||
# Add util library
|
||||
add_subdirectory(util)
|
||||
|
||||
# Add tilemap library and examples
|
||||
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