New salted id system
Conflicts: test/unit/CMakeLists.txt
This commit is contained in:
parent
163d4e0e13
commit
8488788040
5 changed files with 448 additions and 0 deletions
83
include/doorkeeper/implem/dktypes.hpp
Normal file
83
include/doorkeeper/implem/dktypes.hpp
Normal 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
|
170
include/doorkeeper/implem/idmanager.hpp
Normal file
170
include/doorkeeper/implem/idmanager.hpp
Normal 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
|
140
include/doorkeeper/implem/saltedid.hpp
Normal file
140
include/doorkeeper/implem/saltedid.hpp
Normal 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
|
|
@ -13,6 +13,7 @@ add_executable(${PROJECT_NAME}
|
|||
vector.cpp
|
||||
tileiterator.cpp
|
||||
asciimapsource.cpp
|
||||
saltedid.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
|
|
54
test/unit/saltedid.cpp
Normal file
54
test/unit/saltedid.cpp
Normal 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());
|
||||
}
|
Loading…
Reference in a new issue