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());
+}