File size: 6,581 Bytes
d8d559a | 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 216 | #ifndef UTIL_SIZED_ITERATOR_H
#define UTIL_SIZED_ITERATOR_H
#include "pool.hh"
#include "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
|