diff --git a/include/doorkeeper/implem/dktypes.hpp b/include/doorkeeper/implem/dktypes.hpp new file mode 100644 index 0000000..693dfde --- /dev/null +++ b/include/doorkeeper/implem/dktypes.hpp @@ -0,0 +1,83 @@ +/* Copyright 2015, Michele Santullo + * This file is part of DoorKeeper. + * + * DoorKeeper is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DoorKeeper is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DoorKeeper. If not, see . + */ + +#ifndef idA7B80E7E36484499AD624450C00B1DCA +#define idA7B80E7E36484499AD624450C00B1DCA + +#include + +namespace dk { + namespace implem { + class ERROR_inttype_not_available { + ERROR_inttype_not_available(); + }; + + template + struct select { + typedef A type; + }; + + template + struct select { + typedef B type; + }; + } // namespace implem + + // Retrieves a signed/unsigned integer type with sizeof( T ) == BYTES + template + struct int_t { + typedef typename implem::select::type>::type>::type>::type>::type type; + }; + + template + struct int_t { + typedef typename implem::select::type>::type>::type>::type>::type type; + }; + + // Retrieves the smallest unsigned integer type with sizeof( T ) >= BYTES + template + struct uint_t_min { + typedef typename implem::select= CHAR_BIT * BYTES, unsigned char, + typename implem::select= CHAR_BIT * BYTES, unsigned short, + typename implem::select= CHAR_BIT * BYTES, unsigned int, + typename implem::select= CHAR_BIT * BYTES, unsigned long, + typename implem::select= CHAR_BIT * BYTES, unsigned long long, + implem::ERROR_inttype_not_available >::type >::type >::type >::type >::type type; + }; + + // Machine independent definition of sized integer types + typedef int_t<1, true>::type i8; + typedef int_t<2, true>::type i16; + typedef int_t<4, true>::type i32; + typedef int_t<8, true>::type i64; + typedef int_t<1, false>::type u8; + typedef int_t<2, false>::type u16; + typedef int_t<4, false>::type u32; + typedef int_t<8, false>::type u64; +} //namespace dk + +#endif diff --git a/include/doorkeeper/implem/idmanager.hpp b/include/doorkeeper/implem/idmanager.hpp new file mode 100644 index 0000000..3e1daf6 --- /dev/null +++ b/include/doorkeeper/implem/idmanager.hpp @@ -0,0 +1,170 @@ +/* Copyright 2015, Michele Santullo + * This file is part of DoorKeeper. + * + * DoorKeeper is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DoorKeeper is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DoorKeeper. If not, see . + */ + +#ifndef idBC27D8C2304944D0856C2C2B012AE57B +#define idBC27D8C2304944D0856C2C2B012AE57B + +#include "doorkeeper/primitivetypes.hpp" +#include "doorkeeper/implem/saltedid.hpp" +#include +#include +#include +#include + +namespace dk { + namespace implem { + template + class IDInterval { + public: + IDInterval ( I parFrom, I parTo ) : + m_interval(parFrom, parTo) + { + } + + bool operator< ( const IDInterval& parOther ) const; + I lower ( void ) const { return m_interval.lower(); } + I upper ( void ) const { return m_interval.upper(); } + + private: + boost::numeric::interval m_interval; + }; + } //namespace implem + + template + class IDManager { + public: + typedef SaltedID value_type; + typedef typename value_type::index_type index_type; + + enum { + bit_count = sizeof(value_type) * CHAR_BIT + }; + + IDManager ( void ); + + value_type allocate ( void ); + void free ( value_type parID ); + void reset ( void ); + + private: + typedef typename value_type::salt_type salt_type; + using id_interval = implem::IDInterval; + using interval_set = std::set; + + interval_set m_free_intervals; + std::vector m_salts; + }; + + namespace implem { + template + bool IDInterval::operator< ( const IDInterval& parOther) const { + return (m_interval.lower() < parOther.m_interval.lower()) and + (m_interval.upper() < parOther.m_interval.upper()); + } + } //namespace implem + + template + IDManager::IDManager() { + m_free_intervals.insert(id_interval(1, value_type::max_index)); + } + + template + auto IDManager::allocate() -> value_type { + DK_ASSERT(not m_free_intervals.empty()); + auto first = *m_free_intervals.begin(); + auto free_id = first.lower(); + m_free_intervals.erase(m_free_intervals.begin()); + if (first.lower() + 1 <= first.upper()) { + m_free_intervals.insert(typename interval_set::value_type(first.lower() + 1, first.upper())); + } + + if (static_cast(m_salts.size()) < free_id) { + const auto old_size = m_salts.size(); + m_salts.resize(free_id); + std::fill(m_salts.begin() + old_size, m_salts.end(), 0); + } + + DK_ASSERT(free_id > 0); + const auto salt_index = free_id - 1; + if (m_salts[salt_index] < value_type::max_salt) { + return value_type(free_id, m_salts[salt_index]++); + } + else { + return value_type(free_id, m_salts[salt_index]); + } + } + + template + void IDManager::free (value_type parID) { + if (parID.salt() == value_type::max_salt) + return; + + const auto id = parID.index(); + DK_ASSERT(id > 0); + if (m_salts.size() < id or 0 == id) + return; + DK_ASSERT(parID.salt() == m_salts[id - 1] - 1); + if (parID.salt() != m_salts[id - 1] - 1) + return; + + auto it = m_free_intervals.find(id_interval(id, id)); + if (it != m_free_intervals.end() and it->lower() <= id and it->upper() > id) { + return; + } + + it = m_free_intervals.upper_bound(id_interval(id, id)); + if (m_free_intervals.end() == it) { + return; + } + else { + auto free_interval = *it; + + if (id + 1 != free_interval.lower()) { + m_free_intervals.insert(id_interval(id, id)); + } + else { + if (m_free_intervals.begin() != it) { + auto it_2 = it; + --it_2; + if (it_2->lower() + 1 == id) { + auto free_interval_2 = *it_2; + m_free_intervals.erase(it); + m_free_intervals.erase(it_2); + m_free_intervals.insert(id_interval(free_interval_2.lower(), free_interval.upper())); + } + else { + m_free_intervals.erase(it); + m_free_intervals.insert(id_interval(id, free_interval.lower())); + } + } + else { + m_free_intervals.erase(it); + m_free_intervals.insert(id_interval(id, free_interval.upper())); + } + } + } + } + + template + void IDManager::reset() { + m_free_intervals.clear(); + m_free_intervals.insert(id_interval(1, value_type::max_index)); + m_salts.clear(); + } +} //namespace dk + +#endif diff --git a/include/doorkeeper/implem/saltedid.hpp b/include/doorkeeper/implem/saltedid.hpp new file mode 100644 index 0000000..a6af02d --- /dev/null +++ b/include/doorkeeper/implem/saltedid.hpp @@ -0,0 +1,140 @@ +/* Copyright 2015, Michele Santullo + * This file is part of DoorKeeper. + * + * DoorKeeper is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DoorKeeper is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DoorKeeper. If not, see . + */ + +#ifndef id440C878EF94C4468948A57610C7C9B5A +#define id440C878EF94C4468948A57610C7C9B5A + +#include "doorkeeper/primitivetypes.hpp" +#include "doorkeeper/implem/dktypes.hpp" +#include + +namespace dk { + namespace implem { + template + struct uint_t { + typedef typename int_t::type type; + }; + + template =M) or (N%CHAR_BIT==0)> + struct RoundToInt { + private: + enum { + v = (N < M ? (N < CHAR_BIT ? CHAR_BIT : N) : M) + }; + static_assert(v > 0, "v must be greater than 0"); + + public: + enum { + value = ((((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) | ((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) >> 4)) | (((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) | ((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) >> 4)) >> 8)) | ((((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) | ((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) >> 4)) | (((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) | ((((v - 1) | ((v - 1) >> 1)) | (((v - 1) | ((v - 1) >> 1)) >> 2)) >> 4)) >> 8)) >> 16)) + 1 + }; + static_assert(value <= M, "value must be smaller than M"); + static_assert(value and not (value bitand (value - 1)), "value must be a power of two"); + }; + + template + struct FillBits; + template + struct FillBits { + enum { value = 1 bitor (FillBits::value << 1) }; + }; + template <> + struct FillBits<0> { + enum { value = 0 }; + }; + + } //namespace implem + + template + class SaltedID { + static_assert(Salt + Indx <= 64, "Only indices up to 64 bits are currently supported"); + public: + enum { + bit_size = implem::RoundToInt::value, + max_index = implem::FillBits::value, + max_salt = implem::FillBits::value + }; + private: + + typedef typename implem::uint_t::type compound_type; + public: + typedef typename implem::uint_t::value>::type salt_type; + typedef typename implem::uint_t::value>::type index_type; + + SaltedID ( void ) { } + SaltedID ( index_type parIndex, salt_type parSalt ) : + m_data(pack_index_salt(parIndex, parSalt)) + { + DK_ASSERT(index() == parIndex); + DK_ASSERT(salt() == parSalt); + } + + salt_type salt ( void ) const; + index_type index ( void ) const; + + SaltedID& operator+= ( index_type parOther ) { m_data = pack_index_salt(index() + parOther, salt()); return *this; } + SaltedID& operator-= ( index_type parOther ) { m_data = pack_index_salt(index() - parOther, salt()); return *this; } + + private: + static compound_type pack_index_salt ( index_type parIndex, salt_type parSalt ); + + compound_type m_data; + }; + + template + auto SaltedID::salt() const -> salt_type { + return static_cast(m_data bitand implem::FillBits::value); + } + + template + auto SaltedID::index() const -> index_type { + return static_cast(m_data >> Salt); + } + + template + auto SaltedID::pack_index_salt (index_type parIndex, salt_type parSalt) -> compound_type { + DK_ASSERT(parIndex <= max_index); + DK_ASSERT(parSalt <= max_salt); + return (static_cast(parIndex) << Salt) bitor parSalt; + } + + template + inline bool operator< (const SaltedID& parLeft, const SaltedID& parRight) { + return parLeft.index() < parRight.index(); + } + + template + inline SaltedID operator+ (SaltedID parLeft, typename SaltedID::index_type parRight) { + return (parLeft += parRight); + } + + template + inline SaltedID operator+ (typename SaltedID::index_type parLeft, SaltedID parRight) { + return (parRight += parLeft); + } + + template + inline SaltedID operator- (SaltedID parLeft, typename SaltedID::index_type parRight) { + return (parLeft -= parRight); + } + + template + inline SaltedID operator- (typename SaltedID::index_type parLeft, SaltedID parRight) { + return (parRight -= parLeft); + } +} //namespace dk + +#endif diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f06b20e..39ba742 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${PROJECT_NAME} tileiterator.cpp asciimapsource.cpp layeredviewport.cpp + saltedid.cpp ) target_link_libraries(${PROJECT_NAME} diff --git a/test/unit/saltedid.cpp b/test/unit/saltedid.cpp new file mode 100644 index 0000000..3a0f6cd --- /dev/null +++ b/test/unit/saltedid.cpp @@ -0,0 +1,54 @@ +/* Copyright 2015, Michele Santullo + * This file is part of DoorKeeper. + * + * DoorKeeper is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DoorKeeper is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DoorKeeper. If not, see . + */ + +#include +#include "doorkeeper/implem/idmanager.hpp" + +TEST(components, saltedid) { + typedef dk::IDManager<5, 11> id_manager_t; + id_manager_t idman; + + EXPECT_EQ(2, sizeof(idman.allocate())); + EXPECT_EQ(1, sizeof(id_manager_t::value_type::salt_type)); + EXPECT_EQ(2, sizeof(id_manager_t::value_type::index_type)); + EXPECT_EQ(16, id_manager_t::bit_count); + + for (unsigned int z = 0; z < 20; ++z) { + auto index = idman.allocate(); + EXPECT_EQ(0, index.salt()); + EXPECT_EQ(z + 1, index.index()); + } + auto free_index = idman.allocate(); + EXPECT_EQ(0, free_index.salt()); + for (unsigned int z = 0; z < 20; ++z) { + auto index = idman.allocate(); + EXPECT_EQ(0, index.salt()); + EXPECT_EQ(z + 22, index.index()); + } + + for (unsigned int z = 0; z < (1 << 5) - 1; ++z) { + idman.free(free_index); + free_index = idman.allocate(); + EXPECT_EQ(z + 1, free_index.salt()); + EXPECT_EQ(21, free_index.index()); + } + + idman.free(free_index); + free_index = idman.allocate(); + EXPECT_EQ(0, free_index.salt()); + EXPECT_EQ(42, free_index.index()); +}