-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpostgres_utils.hpp
130 lines (108 loc) · 3.32 KB
/
postgres_utils.hpp
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
#ifndef POSTGRES_PROTOBUF_POSTGRES_UTILS_HPP_
#define POSTGRES_PROTOBUF_POSTGRES_UTILS_HPP_
#include <stdint.h>
#include <functional>
#include <limits>
#include <memory>
#include <new>
#include <string>
#include <utility>
#include <vector>
extern "C" {
// We don't want to include `postgres.h` here since it pollutes the namespace,
// causing problems with any protobuf includes that come after it.
extern void pfree(void* pointer);
}
namespace postgres_protobuf {
namespace postgres_utils {
// ===================================================================
// ==================== Memory allocation helpers ====================
// ===================================================================
void* palloc0_or_throw_bad_alloc(size_t size);
// Allocate and construct a C++ object in the current Postgres memory context.
// Throws std::bad_alloc on failure
template <typename T, typename... Args>
T* pnew(Args&&... args) {
void* block = palloc0_or_throw_bad_alloc(sizeof(T));
T* tp = nullptr;
try {
tp = new (block) T(std::forward<Args>(args)...);
} catch (...) {
pfree(block);
throw;
}
return tp;
}
// Like pfree but calls the destructor first.
template <typename T>
void pdelete(T* p) {
p->~T();
pfree(p);
}
std::string float_to_string(float x);
std::string double_to_string(double x);
// TODO: pass memory context explicitly?
template <typename T>
class PostgresAllocator : public std::allocator<T> {
public:
typedef T value_type;
PostgresAllocator() = default;
template <class U>
constexpr PostgresAllocator(const PostgresAllocator<U>&) noexcept {}
T* allocate(size_t n) {
if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) {
throw std::bad_alloc();
}
void* p = palloc0_or_throw_bad_alloc(n * sizeof(T));
#ifdef DEBUG_ALLOC
PGPROTO_DEBUG("ALLOC %lx IN %s", (intptr_t)p, CurrentMemoryContext->name);
#endif
if (p == nullptr) {
throw std::bad_alloc();
}
return static_cast<T*>(p);
}
void deallocate(T* p, std::size_t) noexcept {
#ifdef DEBUG_ALLOC
PGPROTO_DEBUG("FREE %lx", (intptr_t)p);
#endif
pfree(p);
}
template <typename U>
struct rebind {
typedef PostgresAllocator<U> other;
};
};
template <class T, class U>
bool operator==(const PostgresAllocator<T>&, const PostgresAllocator<U>&) {
return true;
}
template <class T, class U>
bool operator!=(const PostgresAllocator<T>&, const PostgresAllocator<U>&) {
return false;
}
using pstring =
std::basic_string<char, std::char_traits<char>, PostgresAllocator<char>>;
template <typename T>
using pvector = std::vector<T, PostgresAllocator<T>>;
template <typename T>
struct pfree_deleter {
void operator()(T* p) { pfree(p); }
};
template <typename T>
using punique_ptr = std::unique_ptr<T, pfree_deleter<T>>;
} // namespace postgres_utils
} // namespace postgres_protobuf
// ==============================================================
// ==================== std::hash extensions ====================
// ==============================================================
namespace std {
template <>
struct hash<postgres_protobuf::postgres_utils::pstring> {
std::size_t operator()(
postgres_protobuf::postgres_utils::pstring const& s) const noexcept {
return std::hash<std::string_view>{}(std::string_view(s));
}
};
} // namespace std
#endif // POSTGRES_PROTOBUF_POSTGRES_UTILS_HPP_