forked from osm2pgsql-dev/osm2pgsql
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathid-tracker.cpp
160 lines (129 loc) · 4.4 KB
/
id-tracker.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "id-tracker.hpp"
#include <map>
#include <limits>
#include <algorithm>
#include <boost/optional.hpp>
#define BLOCK_BITS (16)
#define BLOCK_SIZE (1 << BLOCK_BITS)
#define BLOCK_MASK (BLOCK_SIZE - 1)
namespace {
/* block used to be just a std::vector<bool> of fixed size. however,
* it seems there's significant overhead in exposing std::vector<bool>::iterator
* and so this is now a minimal re-implementation.
*
* each block is BLOCK_SIZE bits, stored as a vector of uint32_t elements.
*/
struct block {
block() : bits(BLOCK_SIZE >> 5, 0) {}
inline bool operator[](size_t i) const { return (bits[i >> 5] & (1 << (i & 0x1f))) > 0; }
inline void set(size_t i, bool value) {
uint32_t &bit = bits[i >> 5];
uint32_t mask = 1 << (i & 0x1f);
if (value) { bit |= mask; } else { bit &= ~mask; }
}
// find the next bit which is set, starting from an initial offset
// of start. this offset is a bit like an iterator, but not fully
// supporting iterator movement forwards and backwards.
//
// returns BLOCK_SIZE if a set bit isn't found
size_t next_set(size_t start) const {
uint32_t bit_i = start >> 5;
while ((bit_i < (BLOCK_SIZE >> 5)) && (bits[bit_i] == 0)) {
++bit_i;
}
if (bit_i >= (BLOCK_SIZE >> 5)) { return BLOCK_SIZE; }
uint32_t bit = bits[bit_i];
size_t idx = bit_i << 5;
while ((bit & 1) == 0) { ++idx; bit >>= 1; }
return idx;
}
private:
std::vector<uint32_t> bits;
};
} // anonymous namespace
struct id_tracker::pimpl {
pimpl();
~pimpl();
bool get(osmid_t id) const;
void set(osmid_t id, bool value);
osmid_t pop_min();
typedef std::map<osmid_t, block> map_t;
map_t pending;
osmid_t old_id;
// a cache of the next starting point to search for in the block.
// this significantly speeds up pop_min() because it doesn't need
// to repeatedly search the beginning of the block each time.
boost::optional<size_t> next_start;
};
bool id_tracker::pimpl::get(osmid_t id) const {
const osmid_t block = id >> BLOCK_BITS, offset = id & BLOCK_MASK;
map_t::const_iterator itr = pending.find(block);
bool result = false;
if (itr != pending.end()) {
result = itr->second[offset];
}
return result;
}
void id_tracker::pimpl::set(osmid_t id, bool value) {
const osmid_t block = id >> BLOCK_BITS, offset = id & BLOCK_MASK;
pending[block].set(offset, value);
// a set may potentially invalidate a next_start, as the bit
// set might be before the position of next_start.
if (next_start) { next_start = boost::none; }
}
// find the first element in a block set to true
osmid_t id_tracker::pimpl::pop_min() {
osmid_t id = std::numeric_limits<osmid_t>::max();
while (next_start || !pending.empty()) {
map_t::iterator itr = pending.begin();
block &b = itr->second;
size_t start = next_start.get_value_or(0);
size_t b_itr = b.next_set(start);
if (b_itr != BLOCK_SIZE) {
b.set(b_itr, false);
id = (itr->first << BLOCK_BITS) | b_itr;
next_start = b_itr;
break;
} else {
// no elements in this block - might as well delete
// the whole thing.
pending.erase(itr);
// since next_start is relative to the current
// block, which is ceasing to exist, then we need to
// reset it.
next_start = boost::none;
}
}
return id;
}
id_tracker::pimpl::pimpl()
: pending(), old_id(std::numeric_limits<osmid_t>::min()), next_start(boost::none) {
}
id_tracker::pimpl::~pimpl() {
}
id_tracker::id_tracker(): impl() {
impl.reset(new pimpl());
}
id_tracker::~id_tracker() {
}
void id_tracker::mark(osmid_t id) {
impl->set(id, true);
//we've marked something so we need to be able to pop it
//the assert below will fail though if we've already popped
//some that were > id so we have to essentially reset to
//allow for more pops to take place
impl->old_id = std::numeric_limits<osmid_t>::min();
}
bool id_tracker::is_marked(osmid_t id) {
return impl->get(id);
}
osmid_t id_tracker::pop_mark() {
osmid_t id = impl->pop_min();
assert((id > impl->old_id) || (id == std::numeric_limits<osmid_t>::max()));
impl->old_id = id;
return id;
}
void id_tracker::commit() {
}
void id_tracker::force_release() {
}