New salted id system

This commit is contained in:
King_DuckZ 2015-08-28 17:24:27 +02:00
parent bf612bc7fd
commit eecd1d1649
5 changed files with 448 additions and 0 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef idA7B80E7E36484499AD624450C00B1DCA
#define idA7B80E7E36484499AD624450C00B1DCA
#include <climits>
namespace dk {
namespace implem {
class ERROR_inttype_not_available {
ERROR_inttype_not_available();
};
template<bool FIRST, typename A, typename B>
struct select {
typedef A type;
};
template<typename A, typename B>
struct select<false, A, B> {
typedef B type;
};
} // namespace implem
// Retrieves a signed/unsigned integer type with sizeof( T ) == BYTES
template<unsigned BYTES, bool SIGNED>
struct int_t {
typedef typename implem::select<sizeof(signed char) * CHAR_BIT == CHAR_BIT * BYTES, signed char,
typename implem::select<sizeof(signed short) * CHAR_BIT == CHAR_BIT * BYTES, signed short,
typename implem::select<sizeof(signed int) * CHAR_BIT == CHAR_BIT * BYTES, signed int,
typename implem::select<sizeof(signed long) * CHAR_BIT == CHAR_BIT * BYTES, signed long,
typename implem::select<sizeof(signed long long) * CHAR_BIT == CHAR_BIT * BYTES, signed long long,
implem::ERROR_inttype_not_available>::type>::type>::type>::type>::type type;
};
template<unsigned BYTES>
struct int_t<BYTES, false> {
typedef typename implem::select<sizeof(unsigned char) * CHAR_BIT == CHAR_BIT * BYTES, unsigned char,
typename implem::select<sizeof(unsigned short) * CHAR_BIT == CHAR_BIT * BYTES, unsigned short,
typename implem::select<sizeof(unsigned int) * CHAR_BIT == CHAR_BIT * BYTES, unsigned int,
typename implem::select<sizeof(unsigned long) * CHAR_BIT == CHAR_BIT * BYTES, unsigned long,
typename implem::select<sizeof(unsigned long long) * CHAR_BIT == CHAR_BIT * BYTES, unsigned long long,
implem::ERROR_inttype_not_available>::type>::type>::type>::type>::type type;
};
// Retrieves the smallest unsigned integer type with sizeof( T ) >= BYTES
template<unsigned BYTES>
struct uint_t_min {
typedef typename implem::select<sizeof(unsigned char) * CHAR_BIT >= CHAR_BIT * BYTES, unsigned char,
typename implem::select<sizeof(unsigned short) * CHAR_BIT >= CHAR_BIT * BYTES, unsigned short,
typename implem::select<sizeof(unsigned int) * CHAR_BIT >= CHAR_BIT * BYTES, unsigned int,
typename implem::select<sizeof(unsigned long) * CHAR_BIT >= CHAR_BIT * BYTES, unsigned long,
typename implem::select<sizeof(unsigned long long) * CHAR_BIT >= 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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef idBC27D8C2304944D0856C2C2B012AE57B
#define idBC27D8C2304944D0856C2C2B012AE57B
#include "doorkeeper/primitivetypes.hpp"
#include "doorkeeper/implem/saltedid.hpp"
#include <boost/numeric/interval.hpp>
#include <set>
#include <vector>
#include <algorithm>
namespace dk {
namespace implem {
template <typename I>
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<I> m_interval;
};
} //namespace implem
template <unsigned int Salt, unsigned int Indx>
class IDManager {
public:
typedef SaltedID<Salt, Indx> 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<index_type>;
using interval_set = std::set<id_interval>;
interval_set m_free_intervals;
std::vector<salt_type> m_salts;
};
namespace implem {
template <typename I>
bool IDInterval<I>::operator< ( const IDInterval& parOther) const {
return (m_interval.lower() < parOther.m_interval.lower()) and
(m_interval.upper() < parOther.m_interval.upper());
}
} //namespace implem
template <unsigned int Salt, unsigned int Indx>
IDManager<Salt, Indx>::IDManager() {
m_free_intervals.insert(id_interval(1, value_type::max_index));
}
template <unsigned int Salt, unsigned int Indx>
auto IDManager<Salt, Indx>::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<index_type>(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 <unsigned int Salt, unsigned int Indx>
void IDManager<Salt, Indx>::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 <unsigned int Salt, unsigned int Indx>
void IDManager<Salt, Indx>::reset() {
m_free_intervals.clear();
m_free_intervals.insert(id_interval(1, value_type::max_index));
m_salts.clear();
}
} //namespace dk
#endif

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef id440C878EF94C4468948A57610C7C9B5A
#define id440C878EF94C4468948A57610C7C9B5A
#include "doorkeeper/primitivetypes.hpp"
#include "doorkeeper/implem/dktypes.hpp"
#include <ciso646>
namespace dk {
namespace implem {
template <unsigned int S>
struct uint_t {
typedef typename int_t<S / CHAR_BIT, false>::type type;
};
template <unsigned int N, unsigned int M=8*CHAR_BIT, bool=(N>=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 <unsigned int B>
struct FillBits;
template <unsigned int B>
struct FillBits {
enum { value = 1 bitor (FillBits<B - 1>::value << 1) };
};
template <>
struct FillBits<0> {
enum { value = 0 };
};
} //namespace implem
template <unsigned int Salt, unsigned int Indx>
class SaltedID {
static_assert(Salt + Indx <= 64, "Only indices up to 64 bits are currently supported");
public:
enum {
bit_size = implem::RoundToInt<Salt + Indx>::value,
max_index = implem::FillBits<Indx>::value,
max_salt = implem::FillBits<Salt>::value
};
private:
typedef typename implem::uint_t<bit_size>::type compound_type;
public:
typedef typename implem::uint_t<implem::RoundToInt<Salt>::value>::type salt_type;
typedef typename implem::uint_t<implem::RoundToInt<Indx>::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 <unsigned int Salt, unsigned int Indx>
auto SaltedID<Salt, Indx>::salt() const -> salt_type {
return static_cast<salt_type>(m_data bitand implem::FillBits<Salt>::value);
}
template <unsigned int Salt, unsigned int Indx>
auto SaltedID<Salt, Indx>::index() const -> index_type {
return static_cast<index_type>(m_data >> Salt);
}
template <unsigned int Salt, unsigned int Indx>
auto SaltedID<Salt, Indx>::pack_index_salt (index_type parIndex, salt_type parSalt) -> compound_type {
DK_ASSERT(parIndex <= max_index);
DK_ASSERT(parSalt <= max_salt);
return (static_cast<compound_type>(parIndex) << Salt) bitor parSalt;
}
template <unsigned int Salt, unsigned int Indx>
inline bool operator< (const SaltedID<Salt, Indx>& parLeft, const SaltedID<Salt, Indx>& parRight) {
return parLeft.index() < parRight.index();
}
template <unsigned int Salt, unsigned int Indx>
inline SaltedID<Salt, Indx> operator+ (SaltedID<Salt, Indx> parLeft, typename SaltedID<Salt, Indx>::index_type parRight) {
return (parLeft += parRight);
}
template <unsigned int Salt, unsigned int Indx>
inline SaltedID<Salt, Indx> operator+ (typename SaltedID<Salt, Indx>::index_type parLeft, SaltedID<Salt, Indx> parRight) {
return (parRight += parLeft);
}
template <unsigned int Salt, unsigned int Indx>
inline SaltedID<Salt, Indx> operator- (SaltedID<Salt, Indx> parLeft, typename SaltedID<Salt, Indx>::index_type parRight) {
return (parLeft -= parRight);
}
template <unsigned int Salt, unsigned int Indx>
inline SaltedID<Salt, Indx> operator- (typename SaltedID<Salt, Indx>::index_type parLeft, SaltedID<Salt, Indx> parRight) {
return (parRight -= parLeft);
}
} //namespace dk
#endif

View file

@ -14,6 +14,7 @@ add_executable(${PROJECT_NAME}
tileiterator.cpp tileiterator.cpp
asciimapsource.cpp asciimapsource.cpp
layeredviewport.cpp layeredviewport.cpp
saltedid.cpp
) )
target_link_libraries(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME}

54
test/unit/saltedid.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#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());
}