ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
MMappedUUIDHashTable.cpp
Go to the documentation of this file.
1/**
2 * @file MMappedUUIDHashTable.cpp
3 * @brief Open-addressing hash table over a memory-mapped file: implementation.
4 *
5 * Implements all methods of @c MMappedUUIDHashTable declared in
6 * @c MMappedUUIDHashTable.h:
7 * - @c MMappedUUIDHashTable(): open/create the backing file and map it.
8 * - @c ~MMappedUUIDHashTable(): sync and unmap.
9 * - @c add(): insert a UUID and assign the next sequential integer.
10 * - @c operator[](): look up an integer by UUID.
11 * - @c sync(): flush the backing region (@c MappedRegion::sync()).
12 *
13 * Internal helpers:
14 * - @c grow(): double the table size and rehash.
15 * - @c find(): locate the slot index for a UUID (or @c NOTHING if absent).
16 * - @c set(): write a key-value pair into the table.
17 */
19
20#include <cassert>
21#include <cerrno>
22#include <cstdint>
23#include <cstring>
24#include <new>
25#include <stdexcept>
26#include <string>
27#include <vector>
28
29#include <fcntl.h>
30#include <unistd.h>
31
32#include <sys/mman.h>
33
34MMappedUUIDHashTable::MMappedUUIDHashTable(const char *filename, bool read_only, uint64_t magic_value)
35{
36 auto size = region.openFile(filename, read_only);
37 bool empty = (size == 0);
38
39 if(empty) {
41 region.resizeFile(size);
42 }
43
44 region.map(size);
45 table = reinterpret_cast<table_t *>(region.base());
46
47 if(empty) {
48 table->magic = magic_value;
49 table->version = 1;
50 table->elem_size = static_cast<uint16_t>(sizeof(value_t));
51 table->_reserved = 0;
52 table->log_size = table_t::logSizeForSize(size);
53 table->nb_elements = 0;
54 table->next_value = 0;
55 for(unsigned long i=0; i<table->capacity(); ++i) {
56 table->t[i].value = NOTHING;
57 }
58 } else {
59 if(table->magic != magic_value)
60 throw std::runtime_error("ProvSQL mmap: wrong file type (magic mismatch)");
61 if(table->version != 1)
62 throw std::runtime_error("ProvSQL mmap: unsupported format version "
63 + std::to_string(table->version));
64 if(table->elem_size != sizeof(value_t))
65 throw std::runtime_error("ProvSQL mmap: element size mismatch (recompile required)");
66 }
67}
68
70{
71 std::vector<value_t> elements;
72 elements.reserve(table->nb_elements);
73 for(unsigned long i=0; i<table->capacity(); ++i)
74 if(table->t[i].value != NOTHING)
75 elements.push_back(table->t[i]);
76
77 auto new_log_size = table->log_size+1;
78 region.remap(table_t::sizeForLogSize(new_log_size));
79 table = reinterpret_cast<table_t *>(region.base());
80
81 table->log_size = new_log_size;
82 for(unsigned long i=0; i<table->capacity(); ++i) {
83 table->t[i].value = NOTHING;
84 }
85 for(const auto &u: elements)
86 set(u.uuid, u.value);
87}
88
93
95{
96 auto k = hash(u);
97 while(table->t[k].value != NOTHING &&
98 std::memcmp(&table->t[k].uuid, &u, sizeof(pg_uuid_t))) {
99 k = (k+1) % table->capacity();
100 }
101
102 return k;
103}
104
106{
107 auto k = find(u);
108
109 return table->t[k].value;
110}
111
112std::pair<unsigned long,bool> MMappedUUIDHashTable::add(pg_uuid_t u)
113{
114 auto k = find(u);
115
116 if(table->t[k].value == NOTHING) {
117 if(table->nb_elements >= MAXIMUM_LOAD_FACTOR * table->capacity()) {
118 grow();
119 }
120 k = find(u);
121 ++table->nb_elements;
122 table->t[k].uuid = u;
123 return std::make_pair(table->t[k].value = table->next_value++, true);
124 } else
125 return std::make_pair(table->t[k].value, false);
126}
127
128// Only used when growing the table, so no need to check/update nb_elements
129void MMappedUUIDHashTable::set(pg_uuid_t u, unsigned long i)
130{
131 table->t[find(u)] = {u, i};
132}
133
135{
136 region.sync();
137}
Open-addressing hash table mapping UUIDs to integers, backed by an mmap file.
void set(pg_uuid_t u, unsigned long i)
Store the mapping u → i in the table.
unsigned long hash(pg_uuid_t u) const
Compute the starting slot index for UUID u.
void grow()
Double the table capacity and rehash all existing entries.
std::pair< unsigned long, bool > add(pg_uuid_t u)
Insert UUID u, assigning it the next available integer.
MMappedUUIDHashTable(const char *filename, bool read_only, uint64_t magic)
Open (or create) the mmap-backed hash table.
static constexpr unsigned STARTING_LOG_SIZE
Initial log2 capacity (65 536 slots).
unsigned long find(pg_uuid_t u) const
Find the slot index of u, or NOTHING if absent.
unsigned long operator[](pg_uuid_t u) const
Look up the integer index for UUID u.
~MMappedUUIDHashTable()
Sync and unmap the file.
MappedRegion region
Backing storage (shared mmap, or heap buffer).
void sync()
Flush the backing region to its file (MappedRegion::sync()).
static constexpr unsigned long NOTHING
Sentinel returned by operator[]() when the UUID is not present.
static constexpr double MAXIMUM_LOAD_FACTOR
Rehash when this fraction of slots is occupied.
table_t * table
Typed view of region.base().
On-disk layout of the hash table stored in the mmap file.
static constexpr unsigned logSizeForSize(std::size_t size)
Compute the log2 of the slot count from the file size.
static constexpr std::size_t sizeForLogSize(unsigned ls)
Compute the file size required for a table with 2^ls slots.
One slot in the hash table: a UUID key and its associated integer value.
UUID structure.