forked from xd9999/kenlm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsized_iterator.hh
215 lines (172 loc) · 6.44 KB
/
sized_iterator.hh
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#ifndef UTIL_SIZED_ITERATOR_H
#define UTIL_SIZED_ITERATOR_H
#include "util/pool.hh"
#include "util/proxy_iterator.hh"
#include <algorithm>
#include <functional>
#include <string>
#include <stdint.h>
#include <cstring>
#include <stdlib.h>
namespace util {
class SizedInnerIterator {
public:
SizedInnerIterator() {}
SizedInnerIterator(void *ptr, std::size_t size) : ptr_(static_cast<uint8_t*>(ptr)), size_(size) {}
bool operator==(const SizedInnerIterator &other) const {
return ptr_ == other.ptr_;
}
bool operator<(const SizedInnerIterator &other) const {
return ptr_ < other.ptr_;
}
SizedInnerIterator &operator+=(std::ptrdiff_t amount) {
ptr_ += amount * size_;
return *this;
}
std::ptrdiff_t operator-(const SizedInnerIterator &other) const {
return (ptr_ - other.ptr_) / size_;
}
const void *Data() const { return ptr_; }
void *Data() { return ptr_; }
std::size_t EntrySize() const { return size_; }
friend void swap(SizedInnerIterator &first, SizedInnerIterator &second);
private:
uint8_t *ptr_;
std::size_t size_;
};
inline void swap(SizedInnerIterator &first, SizedInnerIterator &second) {
using std::swap;
swap(first.ptr_, second.ptr_);
swap(first.size_, second.size_);
}
class ValueBlock {
public:
explicit ValueBlock(const void *from, FreePool &pool)
: ptr_(std::memcpy(pool.Allocate(), from, pool.ElementSize())),
pool_(pool) {}
ValueBlock(const ValueBlock &from)
: ptr_(std::memcpy(from.pool_.Allocate(), from.ptr_, from.pool_.ElementSize())),
pool_(from.pool_) {}
ValueBlock &operator=(const ValueBlock &from) {
std::memcpy(ptr_, from.ptr_, pool_.ElementSize());
return *this;
}
~ValueBlock() { pool_.Free(ptr_); }
const void *Data() const { return ptr_; }
void *Data() { return ptr_; }
private:
void *ptr_;
FreePool &pool_;
};
class SizedProxy {
public:
SizedProxy() {}
SizedProxy(void *ptr, FreePool &pool) : inner_(ptr, pool.ElementSize()), pool_(&pool) {}
operator ValueBlock() const {
return ValueBlock(inner_.Data(), *pool_);
}
SizedProxy &operator=(const SizedProxy &from) {
memcpy(inner_.Data(), from.inner_.Data(), inner_.EntrySize());
return *this;
}
SizedProxy &operator=(const ValueBlock &from) {
memcpy(inner_.Data(), from.Data(), inner_.EntrySize());
return *this;
}
const void *Data() const { return inner_.Data(); }
void *Data() { return inner_.Data(); }
friend void swap(SizedProxy first, SizedProxy second);
private:
friend class util::ProxyIterator<SizedProxy>;
typedef ValueBlock value_type;
typedef SizedInnerIterator InnerIterator;
InnerIterator &Inner() { return inner_; }
const InnerIterator &Inner() const { return inner_; }
InnerIterator inner_;
FreePool *pool_;
};
inline void swap(SizedProxy first, SizedProxy second) {
std::swap_ranges(
static_cast<char*>(first.inner_.Data()),
static_cast<char*>(first.inner_.Data()) + first.inner_.EntrySize(),
static_cast<char*>(second.inner_.Data()));
}
typedef ProxyIterator<SizedProxy> SizedIterator;
// Useful wrapper for a comparison function i.e. sort.
template <class Delegate, class Proxy = SizedProxy> class SizedCompare : public std::binary_function<const Proxy &, const Proxy &, bool> {
public:
explicit SizedCompare(const Delegate &delegate = Delegate()) : delegate_(delegate) {}
bool operator()(const Proxy &first, const Proxy &second) const {
return delegate_(first.Data(), second.Data());
}
bool operator()(const Proxy &first, const ValueBlock &second) const {
return delegate_(first.Data(), second.Data());
}
bool operator()(const ValueBlock &first, const Proxy &second) const {
return delegate_(first.Data(), second.Data());
}
bool operator()(const ValueBlock &first, const ValueBlock &second) const {
return delegate_(first.Data(), second.Data());
}
const Delegate &GetDelegate() const { return delegate_; }
private:
const Delegate delegate_;
};
template <unsigned Size> class JustPOD {
unsigned char data[Size];
};
template <class Delegate, unsigned Size> class JustPODDelegate : std::binary_function<const JustPOD<Size> &, const JustPOD<Size> &, bool> {
public:
explicit JustPODDelegate(const Delegate &compare) : delegate_(compare) {}
bool operator()(const JustPOD<Size> &first, const JustPOD<Size> &second) const {
return delegate_(&first, &second);
}
private:
Delegate delegate_;
};
#define UTIL_SORT_SPECIALIZE(Size) \
case Size: \
std::sort(static_cast<JustPOD<Size>*>(start), static_cast<JustPOD<Size>*>(end), JustPODDelegate<Compare, Size>(compare)); \
break;
template <class Compare> void SizedSort(void *start, void *end, std::size_t element_size, const Compare &compare) {
switch (element_size) {
// Benchmarking sort found it's about 2x faster with an explicitly sized type. So here goes :-(.
UTIL_SORT_SPECIALIZE(4);
UTIL_SORT_SPECIALIZE(8);
UTIL_SORT_SPECIALIZE(12);
UTIL_SORT_SPECIALIZE(16);
UTIL_SORT_SPECIALIZE(17); // This is used by interpolation.
UTIL_SORT_SPECIALIZE(20);
UTIL_SORT_SPECIALIZE(24);
UTIL_SORT_SPECIALIZE(28);
UTIL_SORT_SPECIALIZE(32);
default:
// Recent g++ versions create a temporary value_type then compare with it.
// Problem is that value_type in this case needs to be a runtime-sized array.
// Previously I had std::string serve this role. However, there were a lot
// of string new and delete calls.
//
// The temporary value is on the stack, so there will typically only be one
// at a time. But we can't guarantee that. So here is a pool optimized for
// the case where one element is allocated at any given time. It can
// allocate more, should the underlying C++ sort code change.
{
FreePool pool(element_size);
// TODO is this necessary anymore?
#if defined(_WIN32) || defined(_WIN64)
std::stable_sort
#else
std::sort
#endif
(SizedIterator(SizedProxy(start, pool)), SizedIterator(SizedProxy(end, pool)), SizedCompare<Compare>(compare));
}
}
}
} // namespace util
// Dirty hack because g++ 4.6 at least wants to do a bunch of copy operations.
namespace std {
inline void iter_swap(util::SizedIterator first, util::SizedIterator second) {
util::swap(*first, *second);
}
} // namespace std
#endif // UTIL_SIZED_ITERATOR_H