maph 1.0.0 High-Performance Memory-Mapped Key-Value Database with O(1) Lookups

Loading...
Searching...
No Matches
maph.hpp
Go to the documentation of this file.
1
27#pragma once
28
29#include <sys/mman.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <cstring>
34#include <atomic>
35#include <string_view>
36#include <optional>
37#include <memory>
38#include <vector>
39#include <thread>
40#include <future>
41#include <algorithm>
42#include <execution>
43#include <chrono>
44#include <immintrin.h> // For SIMD
45
46 namespace maph {
47
48 using JsonView = std::string_view;
49
50// ===== CONSTANTS =====
51
52 static constexpr uint32_t MAGIC_NUMBER = 0x4D415048; // "MAPH"
53 static constexpr uint32_t CURRENT_VERSION = 1;
54 static constexpr size_t SLOT_SIZE = 512;
55 static constexpr size_t HEADER_SIZE = 512;
56 static constexpr size_t SLOT_DATA_SIZE = 496; // SLOT_SIZE - metadata
57 static constexpr size_t MAX_PROBE_DISTANCE = 10;
58 static constexpr double DEFAULT_STATIC_RATIO = 0.8; // 80% static, 20% dynamic
59
60// ===== ERROR CODES =====
61
66 enum class ErrorCode {
67 SUCCESS = 0,
68 FILE_OPEN_FAILED,
69 FILE_TRUNCATE_FAILED,
70 MMAP_FAILED,
71 INVALID_MAGIC,
72 VERSION_MISMATCH,
73 VALUE_TOO_LARGE,
74 TABLE_FULL,
75 KEY_NOT_FOUND,
76 READONLY_STORE
77};
78
83 struct Result {
84 ErrorCode code;
85 std::string message;
86
91 bool ok() const { return code == ErrorCode::SUCCESS; }
92
97 operator bool() const { return ok(); }
98};
99
100// ===== CORE STRUCTURES =====
101
115 struct alignas(64) Slot {
116 std::atomic<uint64_t> hash_version{0};
117 uint32_t size{0};
118 uint32_t reserved{0};
119 char data[SLOT_DATA_SIZE]{};
120
121 static constexpr size_t MAX_SIZE = SLOT_DATA_SIZE;
122
127 uint32_t hash() const {
128 return static_cast<uint32_t>(hash_version.load(std::memory_order_acquire) >> 32);
129 }
130
135 uint32_t version() const {
136 return static_cast<uint32_t>(hash_version.load(std::memory_order_acquire));
137 }
138
143 bool empty() const {
144 return hash() == 0;
145 }
146
151 JsonView view() const {
152 return JsonView(data, size);
153 }
154
166 void set(uint32_t h, JsonView value) {
167 uint32_t v = version();
168 hash_version.store((uint64_t(h) << 32) | (v + 1), std::memory_order_release);
169 size = static_cast<uint32_t>(value.size());
170 std::memcpy(data, value.data(), value.size());
171 hash_version.store((uint64_t(h) << 32) | (v + 2), std::memory_order_release);
172 }
173
180 void clear() {
181 uint32_t v = version();
182 hash_version.store(v + 2, std::memory_order_release);
183 size = 0;
184 }
185};
186
187 static_assert(sizeof(Slot) == 512);
188
196 struct Header {
197 uint32_t magic{MAGIC_NUMBER};
198 uint32_t version{CURRENT_VERSION};
199 uint64_t total_slots{0};
200 uint64_t static_slots{0};
201 std::atomic<uint64_t> generation{0};
202 char reserved[480]{};
203};
204
205 static_assert(sizeof(Header) == 512);
206
207// ===== HASH FUNCTION =====
208
216 struct Hash {
221 struct Result {
222 uint32_t hash;
223 uint32_t index;
224 };
225
238 static Result compute(JsonView key, uint64_t num_slots) {
239 // FNV-1a hash
240 uint32_t h = 2166136261u;
241 for (unsigned char c : key) {
242 h ^= c;
243 h *= 16777619u;
244 }
245 if (h == 0) h = 1; // Never return 0 (means empty)
246
247 // Simple modulo for index (perfect hash would go here)
248 uint32_t idx = h % static_cast<uint32_t>(num_slots);
249
250 return {h, idx};
251 }
252
253#ifdef __AVX2__
254 // SIMD batch hash for up to 8 keys
255 static void compute_batch_avx2(const JsonView* keys, size_t count,
256 uint64_t num_slots, Result* results) {
257 // Process 8 hashes in parallel using AVX2
258 const __m256i fnv_prime = _mm256_set1_epi32(16777619u);
259 const __m256i fnv_offset = _mm256_set1_epi32(2166136261u);
260
261 size_t i = 0;
262 for (; i + 8 <= count; i += 8) {
263 __m256i hashes = fnv_offset;
264
265 // Find minimum length for this batch
266 size_t min_len = keys[i].size();
267 for (size_t j = 1; j < 8; ++j) {
268 min_len = std::min(min_len, keys[i + j].size());
269 }
270
271 // Process common prefix with SIMD
272 for (size_t pos = 0; pos < min_len; ++pos) {
273 __m256i chars = _mm256_setr_epi32(
274 keys[i + 0][pos], keys[i + 1][pos],
275 keys[i + 2][pos], keys[i + 3][pos],
276 keys[i + 4][pos], keys[i + 5][pos],
277 keys[i + 6][pos], keys[i + 7][pos]
278 );
279
280 hashes = _mm256_xor_si256(hashes, chars);
281 hashes = _mm256_mullo_epi32(hashes, fnv_prime);
282 }
283
284 // Extract results and finish tails
285 alignas(32) uint32_t hash_array[8];
286 _mm256_store_si256((__m256i*)hash_array, hashes);
287
288 for (size_t j = 0; j < 8; ++j) {
289 uint32_t h = hash_array[j];
290 // Finish the tail for keys longer than min_len
291 for (size_t pos = min_len; pos < keys[i + j].size(); ++pos) {
292 h ^= static_cast<unsigned char>(keys[i + j][pos]);
293 h *= 16777619u;
294 }
295 if (h == 0) h = 1;
296 results[i + j] = {h, h % static_cast<uint32_t>(num_slots)};
297 }
298 }
299
300 // Process remaining keys
301 for (; i < count; ++i) {
302 results[i] = compute(keys[i], num_slots);
303 }
304 }
305#endif
306
307 // Batch hash with automatic SIMD detection
308 static void compute_batch(const std::vector<JsonView>& keys,
309 uint64_t num_slots,
310 std::vector<Result>& results) {
311 results.resize(keys.size());
312
313#ifdef __AVX2__
314 if (__builtin_cpu_supports("avx2")) {
315 compute_batch_avx2(keys.data(), keys.size(), num_slots, results.data());
316 return;
317 }
318#endif
319
320 // Fallback to scalar
321 for (size_t i = 0; i < keys.size(); ++i) {
322 results[i] = compute(keys[i], num_slots);
323 }
324 }
325};
326
327// ===== MAIN CLASS =====
328
351 class Maph {
352 private:
353 int fd_{-1};
354 void* mapped_{nullptr};
355 size_t file_size_{0};
356 Header* header_{nullptr};
357 Slot* slots_{nullptr};
358 bool readonly_{false};
359
360 public:
361 Maph() = default;
362 ~Maph() { close(); }
363
364 // Move-only
365 Maph(const Maph&) = delete;
366 Maph& operator=(const Maph&) = delete;
367 Maph(Maph&& other) noexcept { swap(other); }
368 Maph& operator=(Maph&& other) noexcept { swap(other); return *this; }
369
370 // ===== CREATE/OPEN =====
371
387 static std::unique_ptr<Maph> create(const std::string& path,
388 uint64_t total_slots,
389 uint64_t static_slots = 0) {
390 auto m = std::make_unique<Maph>();
391
392 size_t file_size = sizeof(Header) + (total_slots * sizeof(Slot));
393
394 int fd = ::open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
395 if (fd < 0) return nullptr;
396
397 if (::ftruncate(fd, file_size) < 0) {
398 ::close(fd);
399 return nullptr;
400 }
401
402 void* mapped = ::mmap(nullptr, file_size, PROT_READ | PROT_WRITE,
403 MAP_SHARED, fd, 0);
404 if (mapped == MAP_FAILED) {
405 ::close(fd);
406 return nullptr;
407 }
408
409 // Initialize
410 auto* header = new (mapped) Header();
411 header->total_slots = total_slots;
412 // Default: 80% static, 20% dynamic for collision handling
413 header->static_slots = (static_slots > 0) ? static_slots :
414 static_cast<uint64_t>(total_slots * DEFAULT_STATIC_RATIO);
415
416 auto* slots = reinterpret_cast<Slot*>(
417 static_cast<char*>(mapped) + sizeof(Header));
418 std::memset(slots, 0, total_slots * sizeof(Slot));
419
420 m->fd_ = fd;
421 m->mapped_ = mapped;
422 m->file_size_ = file_size;
423 m->header_ = header;
424 m->slots_ = slots;
425
426 return m;
427 }
428
442 static std::unique_ptr<Maph> open(const std::string& path, bool readonly = false) {
443 auto m = std::make_unique<Maph>();
444
445 int flags = readonly ? O_RDONLY : O_RDWR;
446 int fd = ::open(path.c_str(), flags);
447 if (fd < 0) return nullptr;
448
449 struct stat st;
450 if (::fstat(fd, &st) < 0) {
451 ::close(fd);
452 return nullptr;
453 }
454
455 int prot = readonly ? PROT_READ : (PROT_READ | PROT_WRITE);
456 void* mapped = ::mmap(nullptr, st.st_size, prot, MAP_SHARED, fd, 0);
457 if (mapped == MAP_FAILED) {
458 ::close(fd);
459 return nullptr;
460 }
461
462 auto* header = static_cast<Header*>(mapped);
463 if (header->magic != 0x4D415048) {
464 ::munmap(mapped, st.st_size);
465 ::close(fd);
466 return nullptr;
467 }
468
469 m->fd_ = fd;
470 m->mapped_ = mapped;
471 m->file_size_ = st.st_size;
472 m->header_ = header;
473 m->slots_ = reinterpret_cast<Slot*>(
474 static_cast<char*>(mapped) + sizeof(Header));
475 m->readonly_ = readonly;
476
477 return m;
478 }
479
480 // ===== CORE OPERATIONS (ZERO-COPY) =====
481
498 std::optional<JsonView> get(JsonView key) const {
499 auto [hash, index] = Hash::compute(key, header_->total_slots);
500
501 // For static range, only check direct slot
502 if (index < header_->static_slots) {
503 const Slot& slot = slots_[index];
504 if (slot.hash() == hash) {
505 return slot.view();
506 }
507 return std::nullopt;
508 }
509
510 // For dynamic range, check with linear probing
511 for (size_t i = 0; i < MAX_PROBE_DISTANCE; ++i) {
512 uint32_t idx = (index + i) % header_->total_slots;
513 if (idx < header_->static_slots) continue;
514
515 const Slot& s = slots_[idx];
516 if (s.empty()) break;
517 if (s.hash() == hash) {
518 return s.view();
519 }
520 }
521
522 return std::nullopt;
523 }
524
539 bool set(JsonView key, JsonView value) {
540 if (readonly_ || value.size() > Slot::MAX_SIZE) return false;
541
542 auto [hash, index] = Hash::compute(key, header_->total_slots);
543
544 // Static range - direct set (always overwrites)
545 if (index < header_->static_slots) {
546 slots_[index].set(hash, value);
547 header_->generation.fetch_add(1, std::memory_order_relaxed);
548 return true;
549 }
550
551 // Dynamic range - linear probe for existing key or empty slot
552 for (size_t i = 0; i < MAX_PROBE_DISTANCE; ++i) {
553 uint32_t idx = (index + i) % header_->total_slots;
554 if (idx < header_->static_slots) continue;
555
556 Slot& slot = slots_[idx];
557 // Set if empty or if it's the same key (update)
558 if (slot.empty() || slot.hash() == hash) {
559 slot.set(hash, value);
560 header_->generation.fetch_add(1, std::memory_order_relaxed);
561 return true;
562 }
563 }
564
565 return false; // Table full in probe region
566 }
567
579 bool remove(JsonView key) {
580 if (readonly_) return false;
581
582 auto [hash, index] = Hash::compute(key, header_->total_slots);
583
584 // For static range, only check direct slot
585 if (index < header_->static_slots) {
586 if (slots_[index].hash() == hash) {
587 slots_[index].clear();
588 header_->generation.fetch_add(1, std::memory_order_relaxed);
589 return true;
590 }
591 return false;
592 }
593
594 // For dynamic range, check with linear probing
595 for (size_t i = 0; i < MAX_PROBE_DISTANCE; ++i) {
596 uint32_t idx = (index + i) % header_->total_slots;
597 if (idx < header_->static_slots) continue;
598
599 Slot& slot = slots_[idx];
600 if (slot.empty()) break; // Key not found
601 if (slot.hash() == hash) {
602 slot.clear();
603 header_->generation.fetch_add(1, std::memory_order_relaxed);
604 return true;
605 }
606 }
607
608 return false;
609 }
610
617 bool exists(JsonView key) const {
618 return get(key).has_value();
619 }
620
621 // ===== BATCH OPERATIONS =====
622
635 template<typename Callback>
636 void mget(const std::vector<JsonView>& keys, Callback&& cb) const {
637 // Prefetch
638 for (auto key : keys) {
639 auto [_, index] = Hash::compute(key, header_->total_slots);
640 __builtin_prefetch(&slots_[index], 0, 3);
641 }
642
643 // Process
644 for (auto key : keys) {
645 if (auto value = get(key)) {
646 cb(key, *value);
647 }
648 }
649 }
650
659 size_t mset(const std::vector<std::pair<JsonView, JsonView>>& kvs) {
660 if (readonly_) return 0;
661
662 size_t count = 0;
663 for (const auto& [key, value] : kvs) {
664 if (set(key, value)) ++count;
665 }
666 return count;
667 }
668
669 // ===== SCANNING =====
670
681 template<typename Visitor>
682 void scan(Visitor&& visit) const {
683 for (uint64_t i = 0; i < header_->total_slots; ++i) {
684 const Slot& slot = slots_[i];
685 if (!slot.empty()) {
686 visit(i, slot.hash(), slot.view());
687 }
688 }
689 }
690
691 // ===== UTILITIES =====
692
701 void sync() {
702 if (!readonly_ && mapped_) {
703 ::msync(mapped_, file_size_, MS_ASYNC);
704 }
705 }
706
715 void close() {
716 if (mapped_) {
717 ::munmap(mapped_, file_size_);
718 mapped_ = nullptr;
719 }
720 if (fd_ >= 0) {
721 ::close(fd_);
722 fd_ = -1;
723 }
724 }
725
730 struct Stats {
731 uint64_t total_slots;
732 uint64_t static_slots;
733 uint64_t used_slots;
734 uint64_t generation;
735 size_t memory_bytes;
736 double load_factor;
737 };
738
747 Stats stats() const {
748 uint64_t used = 0;
749 for (uint64_t i = 0; i < header_->total_slots; ++i) {
750 if (!slots_[i].empty()) ++used;
751 }
752
753 return {
754 header_->total_slots,
755 header_->static_slots,
756 used,
757 header_->generation.load(std::memory_order_relaxed),
758 file_size_,
759 static_cast<double>(used) / header_->total_slots
760 };
761 }
762
763 // ===== PARALLEL BATCH OPERATIONS =====
764
765 template<typename Callback>
766 void parallel_mget(const std::vector<JsonView>& keys,
767 Callback&& cb,
768 size_t thread_count = 0) const {
769 if (thread_count == 0) {
770 thread_count = std::thread::hardware_concurrency();
771 }
772
773 if (keys.size() < thread_count * 10) {
774 // Not worth parallelizing
775 mget(keys, std::forward<Callback>(cb));
776 return;
777 }
778
779 const size_t chunk_size = (keys.size() + thread_count - 1) / thread_count;
780 std::vector<std::future<void>> futures;
781
782 for (size_t t = 0; t < thread_count; ++t) {
783 size_t start = t * chunk_size;
784 size_t end = std::min(start + chunk_size, keys.size());
785
786 futures.emplace_back(std::async(std::launch::async,
787 [this, &keys, &cb, start, end]() {
788 // Prefetch for this chunk
789 for (size_t i = start; i < end; ++i) {
790 auto [_, index] = Hash::compute(keys[i], header_->total_slots);
791 __builtin_prefetch(&slots_[index], 0, 3);
792 }
793
794 // Process chunk
795 for (size_t i = start; i < end; ++i) {
796 if (auto value = get(keys[i])) {
797 cb(keys[i], *value);
798 }
799 }
800 }));
801 }
802
803 for (auto& f : futures) {
804 f.wait();
805 }
806 }
807
808 size_t parallel_mset(const std::vector<std::pair<JsonView, JsonView>>& kvs,
809 size_t thread_count = 0) {
810 if (readonly_) return 0;
811
812 if (thread_count == 0) {
813 thread_count = std::thread::hardware_concurrency();
814 }
815
816 if (kvs.size() < thread_count * 10) {
817 // Not worth parallelizing
818 return mset(kvs);
819 }
820
821 const size_t chunk_size = (kvs.size() + thread_count - 1) / thread_count;
822 std::vector<std::future<size_t>> futures;
823
824 for (size_t t = 0; t < thread_count; ++t) {
825 size_t start = t * chunk_size;
826 size_t end = std::min(start + chunk_size, kvs.size());
827
828 futures.emplace_back(std::async(std::launch::async,
829 [this, &kvs, start, end]() {
830 size_t count = 0;
831 for (size_t i = start; i < end; ++i) {
832 if (set(kvs[i].first, kvs[i].second)) {
833 ++count;
834 }
835 }
836 return count;
837 }));
838 }
839
840 size_t total = 0;
841 for (auto& f : futures) {
842 total += f.get();
843 }
844 return total;
845 }
846
847 template<typename Visitor>
848 void parallel_scan(Visitor&& visit, size_t thread_count = 0) const {
849 if (thread_count == 0) {
850 thread_count = std::thread::hardware_concurrency();
851 }
852
853 const uint64_t chunk_size = (header_->total_slots + thread_count - 1) / thread_count;
854 std::vector<std::future<void>> futures;
855
856 for (size_t t = 0; t < thread_count; ++t) {
857 uint64_t start = t * chunk_size;
858 uint64_t end = std::min(start + chunk_size, header_->total_slots);
859
860 futures.emplace_back(std::async(std::launch::async,
861 [this, &visit, start, end]() {
862 for (uint64_t i = start; i < end; ++i) {
863 const Slot& slot = slots_[i];
864 if (!slot.empty()) {
865 visit(i, slot.hash(), slot.view());
866 }
867 }
868 }));
869 }
870
871 for (auto& f : futures) {
872 f.wait();
873 }
874 }
875
876 // ===== ASYNC DURABILITY =====
877
878 class DurabilityManager {
879 private:
880 std::atomic<bool> running_{false};
881 std::unique_ptr<std::thread> sync_thread_;
882 Maph* maph_;
883 std::chrono::milliseconds interval_;
884
885 public:
886 DurabilityManager(Maph* m, std::chrono::milliseconds interval)
887 : maph_(m), interval_(interval) {}
888
889 ~DurabilityManager() {
890 stop();
891 }
892
893 void start() {
894 if (running_.exchange(true)) return;
895
896 sync_thread_ = std::make_unique<std::thread>([this]() {
897 while (running_.load()) {
898 std::this_thread::sleep_for(interval_);
899 if (running_.load() && maph_->mapped_) {
900 ::msync(maph_->mapped_, maph_->file_size_, MS_ASYNC);
901 }
902 }
903 });
904 }
905
906 void stop() {
907 if (!running_.exchange(false)) return;
908 if (sync_thread_ && sync_thread_->joinable()) {
909 sync_thread_->join();
910 }
911 }
912
913 void sync_now() {
914 if (maph_->mapped_ && !maph_->readonly_) {
915 ::msync(maph_->mapped_, maph_->file_size_, MS_SYNC);
916 }
917 }
918 };
919
920 std::unique_ptr<DurabilityManager> durability_;
921
922 public:
923 void enable_durability(std::chrono::milliseconds interval = std::chrono::seconds(1)) {
924 if (readonly_) return;
925 durability_ = std::make_unique<DurabilityManager>(this, interval);
926 durability_->start();
927 }
928
929 void disable_durability() {
930 durability_.reset();
931 }
932
933 void sync_now() {
934 if (durability_) {
935 durability_->sync_now();
936 } else {
937 sync();
938 }
939 }
940
941 private:
942 void swap(Maph& other) noexcept {
943 std::swap(fd_, other.fd_);
944 std::swap(mapped_, other.mapped_);
945 std::swap(file_size_, other.file_size_);
946 std::swap(header_, other.header_);
947 std::swap(slots_, other.slots_);
948 std::swap(readonly_, other.readonly_);
949 }
950};
951
952// ===== CONVENIENCE FUNCTIONS =====
953
954 inline auto create(const std::string& path, uint64_t slots) {
955 return Maph::create(path, slots, slots);
956}
957
958 inline auto open(const std::string& path) {
959 return Maph::open(path, false);
960}
961
962 inline auto open_readonly(const std::string& path) {
963 return Maph::open(path, true);
964}
965
966} // namespace maph
maph::Maph::DurabilityManager
Definition maph.hpp:878
maph::Maph::DurabilityManager::start
void start()
Definition maph.hpp:893
maph::Maph::DurabilityManager::DurabilityManager
DurabilityManager(Maph *m, std::chrono::milliseconds interval)
Definition maph.hpp:886
maph::Maph::DurabilityManager::stop
void stop()
Definition maph.hpp:906
maph::Maph::DurabilityManager::~DurabilityManager
~DurabilityManager()
Definition maph.hpp:889
maph::Maph::DurabilityManager::sync_now
void sync_now()
Definition maph.hpp:913
maph::Maph
Main maph class - lean and fast.
Definition maph.hpp:351
maph::Maph::Maph
Maph(Maph &&other) noexcept
Definition maph.hpp:367
maph::Maph::Maph
Maph()=default
maph::Maph::remove
bool remove(JsonView key)
Remove a key-value pair.
Definition maph.hpp:579
maph::Maph::enable_durability
void enable_durability(std::chrono::milliseconds interval=std::chrono::seconds(1))
Definition maph.hpp:923
maph::Maph::durability_
std::unique_ptr< DurabilityManager > durability_
Definition maph.hpp:920
maph::Maph::close
void close()
Close the database and unmap memory.
Definition maph.hpp:715
maph::Maph::parallel_mget
void parallel_mget(const std::vector< JsonView > &keys, Callback &&cb, size_t thread_count=0) const
Definition maph.hpp:766
maph::Maph::parallel_mset
size_t parallel_mset(const std::vector< std::pair< JsonView, JsonView > > &kvs, size_t thread_count=0)
Definition maph.hpp:808
maph::Maph::stats
Stats stats() const
Get database statistics.
Definition maph.hpp:747
maph::Maph::Stats::generation
uint64_t generation
Current generation number.
Definition maph.hpp:734
maph::Maph::Stats::static_slots
uint64_t static_slots
Number of perfect hash slots.
Definition maph.hpp:732
maph::Maph::sync_now
void sync_now()
Definition maph.hpp:933
maph::Maph::open
static std::unique_ptr< Maph > open(const std::string &path, bool readonly=false)
Open an existing database file.
Definition maph.hpp:442
maph::Maph::Stats::used_slots
uint64_t used_slots
Number of occupied slots.
Definition maph.hpp:733
maph::Maph::Stats::memory_bytes
size_t memory_bytes
Total memory usage in bytes.
Definition maph.hpp:735
maph::Maph::Maph
Maph(const Maph &)=delete
maph::Maph::operator=
Maph & operator=(const Maph &)=delete
maph::Maph::used
size_t used() const
Definition maph.hpp:277
maph::Maph::exists
bool exists(JsonView key) const
Check if a key exists.
Definition maph.hpp:617
maph::Maph::Stats::total_slots
uint64_t total_slots
Total number of slots.
Definition maph.hpp:731
maph::Maph::mset
size_t mset(const std::vector< std::pair< JsonView, JsonView > > &kvs)
Set multiple key-value pairs.
Definition maph.hpp:659
maph::Maph::parallel_scan
void parallel_scan(Visitor &&visit, size_t thread_count=0) const
Definition maph.hpp:848
maph::Maph::~Maph
~Maph()
Definition maph.hpp:362
maph::Maph::Stats::load_factor
double load_factor
Ratio of used to total slots.
Definition maph.hpp:736
maph::Maph::set
bool set(JsonView key, JsonView value)
Store a key-value pair.
Definition maph.hpp:539
maph::Maph::disable_durability
void disable_durability()
Definition maph.hpp:929
maph::Maph::scan
void scan(Visitor &&visit) const
Scan all key-value pairs in the database.
Definition maph.hpp:682
maph::Maph::sync
void sync()
Force synchronization to disk.
Definition maph.hpp:701
maph::Maph::create
static std::unique_ptr< Maph > create(const std::string &path, uint64_t total_slots, uint64_t static_slots=0)
Create a new database file.
Definition maph.hpp:387
maph::Maph::mget
void mget(const std::vector< JsonView > &keys, Callback &&cb) const
Get multiple keys in a single operation.
Definition maph.hpp:636
maph::Maph::operator=
Maph & operator=(Maph &&other) noexcept
Definition maph.hpp:368
maph::Maph::get
std::optional< JsonView > get(JsonView key) const
Get value for a key.
Definition maph.hpp:498
maph::Maph::Stats
Database statistics.
Definition maph.hpp:730
maph
Definition maph.hpp:46
maph::MAGIC_NUMBER
static constexpr uint32_t MAGIC_NUMBER
Definition maph.hpp:52
maph::CURRENT_VERSION
static constexpr uint32_t CURRENT_VERSION
Definition maph.hpp:53
maph::open
auto open(const std::string &path)
Definition maph.hpp:958
maph::open_readonly
auto open_readonly(const std::string &path)
Definition maph.hpp:962
maph::ErrorCode
ErrorCode
Error codes returned by maph operations.
Definition maph.hpp:66
maph::ErrorCode::KEY_NOT_FOUND
@ KEY_NOT_FOUND
Key does not exist in database.
maph::ErrorCode::MMAP_FAILED
@ MMAP_FAILED
Memory mapping failed.
maph::ErrorCode::FILE_TRUNCATE_FAILED
@ FILE_TRUNCATE_FAILED
Failed to resize database file.
maph::ErrorCode::VALUE_TOO_LARGE
@ VALUE_TOO_LARGE
Value exceeds maximum slot size (496 bytes)
maph::ErrorCode::TABLE_FULL
@ TABLE_FULL
Hash table is full in probe region.
maph::ErrorCode::VERSION_MISMATCH
@ VERSION_MISMATCH
Database version incompatible with library.
maph::ErrorCode::INVALID_MAGIC
@ INVALID_MAGIC
File has invalid magic number (not a maph database)
maph::ErrorCode::SUCCESS
@ SUCCESS
Operation completed successfully.
maph::ErrorCode::READONLY_STORE
@ READONLY_STORE
Attempted write operation on read-only database.
maph::ErrorCode::FILE_OPEN_FAILED
@ FILE_OPEN_FAILED
Failed to open database file.
maph::DEFAULT_STATIC_RATIO
static constexpr double DEFAULT_STATIC_RATIO
Definition maph.hpp:58
maph::create
auto create(const std::string &path, uint64_t slots)
Definition maph.hpp:954
maph::Header::generation
std::atomic< uint64_t > generation
Global generation counter (incremented on modifications)
Definition maph.hpp:201
maph::Header::total_slots
uint64_t total_slots
Total number of slots in database.
Definition maph.hpp:199
maph::Header::static_slots
uint64_t static_slots
Number of slots using perfect hashing (no collisions)
Definition maph.hpp:200
maph::Header::magic
uint32_t magic
Magic number for file validation ("MAPH")
Definition maph.hpp:197
maph::MAX_PROBE_DISTANCE
static constexpr size_t MAX_PROBE_DISTANCE
Definition maph.hpp:57
maph::SLOT_SIZE
static constexpr size_t SLOT_SIZE
Definition maph.hpp:54
maph::HEADER_SIZE
static constexpr size_t HEADER_SIZE
Definition maph.hpp:55
maph::Header::reserved
char reserved[480]
Reserved space for future extensions.
Definition maph.hpp:202
maph::JsonView
std::string_view JsonView
Definition maph.hpp:48
maph::Header::version
uint32_t version
Database format version.
Definition maph.hpp:198
maph::SLOT_DATA_SIZE
static constexpr size_t SLOT_DATA_SIZE
Definition maph.hpp:56
maph::Header
Database file header containing metadata.
Definition maph.hpp:196
maph::Hash
Hash function implementation for key distribution.
Definition maph.hpp:216
maph::Hash::compute_batch
static void compute_batch(const std::vector< JsonView > &keys, uint64_t num_slots, std::vector< Result > &results)
Definition maph.hpp:308
maph::Hash::compute
static Result compute(JsonView key, uint64_t num_slots)
Compute hash and slot index for a key.
Definition maph.hpp:238
maph::Hash::Result::index
uint32_t index
Slot index (hash % num_slots)
Definition maph.hpp:223
maph::Hash::Result::hash
uint32_t hash
Full 32-bit hash value.
Definition maph.hpp:222
maph::Hash::Result
Hash computation result.
Definition maph.hpp:221
maph::Result
Operation result containing error code and message.
Definition maph.hpp:83
maph::Result::ok
bool ok() const
Check if operation succeeded.
Definition maph.hpp:91
maph::Result::message
std::string message
Human-readable error description.
Definition maph.hpp:85
maph::Result::code
ErrorCode code
Error code indicating success or failure type.
Definition maph.hpp:84
maph::Slot
Storage slot for key-value pairs.
Definition maph.hpp:115
maph::Slot::version
uint32_t version() const
Get the version number for optimistic concurrency control.
Definition maph.hpp:135
maph::Slot::size
uint32_t size
Size of stored value in bytes.
Definition maph.hpp:117
maph::Slot::empty
bool empty() const
Check if slot is empty.
Definition maph.hpp:143
maph::Slot::hash_version
std::atomic< uint64_t > hash_version
Combined hash (high 32) and version (low 32)
Definition maph.hpp:116
maph::Slot::MAX_SIZE
static constexpr size_t MAX_SIZE
Maximum value size (496 bytes)
Definition maph.hpp:121
maph::Slot::reserved
uint32_t reserved
Reserved for future use.
Definition maph.hpp:118
maph::Slot::data
char data[SLOT_DATA_SIZE]
Value data storage.
Definition maph.hpp:119
maph::Slot::view
JsonView view() const
Get a string_view of the stored value.
Definition maph.hpp:151
maph::Slot::clear
void clear()
Clear this slot (remove the key-value pair)
Definition maph.hpp:180
maph::Slot::version
std::atomic< uint32_t > version
Definition maph.hpp:28
maph::Slot::hash
uint32_t hash
Definition maph.hpp:30
maph::Slot::hash
uint32_t hash() const
Get the hash value of the key stored in this slot.
Definition maph.hpp:127
maph::Slot::set
void set(uint32_t h, JsonView value)
Store a value in this slot.
Definition maph.hpp:166