Jazz 1.26.+
Loading...
Searching...
No Matches
volatile.h
1/* Jazz (c) 2018-2026 kaalam.ai (The Authors of Jazz), using (under the same license):
2
3 1. Biomodelling - The AATBlockQueue class (c) Jacques BasaldĂșa, 2009-2012 licensed
4 exclusively for the use in the Jazz server software.
5
6 Copyright 2009-2012 Jacques BasaldĂșa
7
8 2. BBVA - Jazz: A lightweight analytical web server for data-driven applications.
9
10 Copyright 2016-2017 Banco Bilbao Vizcaya Argentaria, S.A.
11
12 This product includes software developed at
13
14 BBVA (https://www.bbva.com/)
15
16 3. LMDB, Copyright 2011-2017 Howard Chu, Symas Corp. All rights reserved.
17
18 Licensed under http://www.OpenLDAP.org/license.html
19
20
21 Licensed under the Apache License, Version 2.0 (the "License");
22 you may not use this file except in compliance with the License.
23 You may obtain a copy of the License at
24
25 http://www.apache.org/licenses/LICENSE-2.0
26
27 Unless required by applicable law or agreed to in writing, software
28 distributed under the License is distributed on an "AS IS" BASIS,
29 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 See the License for the specific language governing permissions and
31 limitations under the License.
32*/
33
34
35#include "src/jazz_elements/channel.h"
36
37#ifdef CATCH_TEST
38#ifndef INCLUDED_JAZZ_CATCH2
39#define INCLUDED_JAZZ_CATCH2
40
41#include "src/catch2/catch.hpp"
42
43#endif
44#endif
45
46
47#ifndef INCLUDED_JAZZ_ELEMENTS_VOLATILE
48#define INCLUDED_JAZZ_ELEMENTS_VOLATILE
49
50namespace jazz_elements
51{
52
53#define BASE_DEQUE_10BIT 0x0a4 //< First 10 bits of base "deque"
54#define BASE_INDEX_10BIT 0x1c9 //< First 10 bits of base "index"
55#define BASE_QUEUE_10BIT 0x2b1 //< First 10 bits of base "queue"
56#define BASE_TREE_10BIT 0x254 //< First 10 bits of base "tree"
57
58#define COMMAND_JUST_THE_KEY 0x000 //< No command in the key
59#define COMMAND_CHILD_10BIT 0x103 //< First 10 bits of command "ch{ild}"
60#define COMMAND_FIRST_10BIT 0x126 //< First 10 bits of command "fi{rst}"
61#define COMMAND_GET_10BIT 0x0a7 //< First 10 bits of command "ge{t}"
62#define COMMAND_HIGH_10BIT 0x128 //< First 10 bits of command "hi{ghest}"
63#define COMMAND_LAST_10BIT 0x02c //< First 10 bits of command "la{st}"
64#define COMMAND_LOW_10BIT 0x1ec //< First 10 bits of command "lo{west}"
65#define COMMAND_NEXT_10BIT 0x0ae //< First 10 bits of command "ne{xt}"
66#define COMMAND_PARENT_10BIT 0x030 //< First 10 bits of command "pa{rent}"
67#define COMMAND_PFIRST_10BIT 0x0d0 //< First 10 bits of command "pf{irst}"
68#define COMMAND_PLAST_10BIT 0x190 //< First 10 bits of command "pl{ast}"
69#define COMMAND_PREV_10BIT 0x250 //< First 10 bits of command "pr{ev}"
70#define COMMAND_PUT_10BIT 0x2b0 //< First 10 bits of command "pu{t}"
71#define COMMAND_XHIGH_10BIT 0x118 //< First 10 bits of command "xh{ighest}"
72#define COMMAND_XLOW_10BIT 0x198 //< First 10 bits of command "xl{owest}"
73#define COMMAND_SECOND_ARG 0x3ff //< In a put call with a key, it is either a parent key or a priority.
74#define COMMAND_SIZE 0x400 //< For numbers, defining a queue size, this is added to avoid overlap.
75
76
80
81
106
107
112 uint64_t ent_hash;
113 uint64_t key_hash;
114
121 bool operator==(const EntityKeyHash &o) const {
122 return ent_hash == o.ent_hash && key_hash == o.key_hash;
123 }
124
131 bool operator<(const EntityKeyHash &o) const {
132 return ent_hash < o.ent_hash || (ent_hash == o.ent_hash && key_hash < o.key_hash);
133 }
134};
135
136
146
147
152typedef std::map<uint64_t, pVolatileTransaction> HashVolXctMap;
153
154
159typedef std::map<uint64_t, QueueEnt> HashQueueEntMap;
160
161
166typedef std::map<EntityKeyHash, pVolatileTransaction> EntKeyVolXctMap;
167
168
173struct NameUse {
174 int use;
176};
177
178
183typedef std::map<uint64_t, NameUse> HashNameUseMap;
184
185
188
189
243class Volatile : public Container {
244
245 public:
246
247 Volatile(pLogger a_logger, pConfigFile a_config);
248 ~Volatile();
249
250 virtual pChar const id();
251
252 StatusCode start ();
254
255 // The easy interface (Requires explicit pulling because of the native interface using the same names.)
256
257 using Container::get;
258 using Container::header;
259 using Container::put;
260 using Container::locate;
262 using Container::remove;
263 using Container::copy;
264
266 virtual void destroy_transaction (pTransaction &p_txn);
267
268 // The "native" interface
269
270 virtual StatusCode get (pTransaction &p_txn,
271 Locator &what);
272 virtual StatusCode get (pTransaction &p_txn,
273 Locator &what,
274 pBlock p_row_filter);
275 virtual StatusCode get (pTransaction &p_txn,
276 Locator &what,
277 pChar name);
278 virtual StatusCode locate (Locator &location,
279 Locator &what);
280 virtual StatusCode header (StaticBlockHeader &hea,
281 Locator &what);
282 virtual StatusCode header (pTransaction &p_txn,
283 Locator &what);
284 virtual StatusCode put (Locator &where,
285 pBlock p_block,
286 int mode = WRITE_AS_BASE_DEFAULT);
287 virtual StatusCode new_entity(Locator &where);
288 virtual StatusCode remove (Locator &where);
289 virtual StatusCode copy (Locator &where,
290 Locator &what);
291
292 // Support for container names in the BaseAPI .base_names()
293
295
296#ifndef CATCH_TEST
297 private:
298#endif
299
302
309 inline void new_key(Name &key) {
310 sprintf(key, "k%014lx", ++key_seed);
311 }
312
313
325 inline StatusCode populate_index(Index &index, pBlock p_block) {
326
327 if (p_block->cell_type != CELL_TYPE_TUPLE || p_block->size != 2) return SERVICE_ERROR_BAD_BLOCK;
328
329 pBlock p_key = pTuple(p_block)->get_block(0);
330 pBlock p_val = pTuple(p_block)->get_block(1);
331
332 if ( p_key->cell_type != CELL_TYPE_STRING || p_val->cell_type != CELL_TYPE_STRING
333 || p_key->rank != 1 || p_val->rank != 1 || p_key->size != p_val->size) return SERVICE_ERROR_BAD_BLOCK;
334
335 for (int i = 0; i < p_key->size; i++)
336 index[p_key->get_string(i)] = p_val->get_string(i);
337
338 return SERVICE_NO_ERROR;
339 }
340
341
353 inline StatusCode put_queue_insert(uint64_t ent_hash, Name &key, double priority, pBlock p_block, int mode) {
354 HashQueueEntMap::iterator it_queue = queue_ent.find(ent_hash);
355
356 if (it_queue == queue_ent.end()) return SERVICE_ERROR_ENTITY_NOT_FOUND;
357
358 EntityKeyHash ek = {ent_hash, hash(key)};
359 EntKeyVolXctMap::iterator it_item = queue_key.find(ek);
360
361 if (it_item != queue_key.end()) {
362 if (mode & WRITE_ONLY_IF_NOT_EXISTS) return SERVICE_ERROR_WRITE_FORBIDDEN;
363
364 pVolatileTransaction p_item = it_item->second;
365
366 pBlock p_new = block_malloc(p_block->total_bytes);
367 if (p_new == nullptr) return SERVICE_ERROR_NO_MEM;
368
369 alloc_bytes -= p_item->p_block->total_bytes;
370 free(p_item->p_block);
371
372 memcpy(p_new, p_block, p_block->total_bytes);
373
374 p_item->status = BLOCK_STATUS_EMPTY;
375
376 it_queue->second.p_root = aat_remove(p_item, it_queue->second.p_root);
377
378 p_item->priority = priority;
379 p_item->times_used++;
380 p_item->p_block = p_new;
381
382 p_item->p_block->hash64 = 0;
383
384 p_item->status = BLOCK_STATUS_READY;
385
386 it_queue->second.p_root = aat_insert(p_item, it_queue->second.p_root);
387
388 queue_key[ek] = p_item;
389
390 return SERVICE_NO_ERROR;
391 }
392 if (mode & WRITE_ONLY_IF_EXISTS) return SERVICE_ERROR_WRITE_FORBIDDEN;
393
394 if (it_queue->second.queue_use == it_queue->second.queue_size) {
395 pVolatileTransaction p_lowest = aat_lowest_priority(it_queue->second.p_root);
396
397 if (p_lowest->priority >= priority) return SERVICE_ERROR_LOW_PRIORITY;
398
399 destroy_item(BASE_QUEUE_10BIT, ent_hash, p_lowest);
400 }
401
402 pTransaction p_txn;
403
404 int ret = new_transaction(p_txn);
405
406 if (ret != SERVICE_NO_ERROR) return ret;
407
408 pBlock p_new = block_malloc(p_block->total_bytes);
409 if (p_new == nullptr) {
410 destroy_transaction(p_txn);
411
412 return SERVICE_ERROR_NO_MEM;
413 }
414 memcpy(p_new, p_block, p_block->total_bytes);
415
416 p_new->hash64 = 0;
417
418 pVolatileTransaction(p_txn)->priority = priority;
421 pVolatileTransaction(p_txn)->p_block = p_new;
422 pVolatileTransaction(p_txn)->status = BLOCK_STATUS_READY;
423
424 it_queue->second.p_root = aat_insert((pVolatileTransaction) p_txn, it_queue->second.p_root);
425 it_queue->second.queue_use++;
426
427 queue_key[ek] = (pVolatileTransaction) p_txn;
428
429 return SERVICE_NO_ERROR;
430 }
431
432
443 inline StatusCode put_in_deque(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, pBlock p_block, bool first = false) {
444
445 pTransaction p_txn;
446 int ret;
447
448 if ((ret = new_transaction(p_txn)) != SERVICE_NO_ERROR) return ret;
449
450 pBlock p_new = block_malloc(p_block->total_bytes);
451 if (p_new == nullptr) {
452 destroy_transaction(p_txn);
453
454 return SERVICE_ERROR_NO_MEM;
455 }
456 memcpy(p_new, p_block, p_block->total_bytes);
457 p_txn->p_block = p_new;
458 p_txn->p_block->hash64 = 0;
459 p_txn->status = BLOCK_STATUS_READY;
460
461 deque_key[ek] = (pVolatileTransaction) p_txn;
463
464 if (it_ent->second == nullptr) {
465 it_ent->second = (pVolatileTransaction) p_txn;
468
469 return SERVICE_NO_ERROR;
470 }
471
472 if (first) {
473 pVolatileTransaction p_2nd = it_ent->second;
474 pVolatileTransaction p_last = p_2nd->p_prev;
475
476 pVolatileTransaction(p_txn)->p_next = p_2nd;
477 p_2nd->p_prev = (pVolatileTransaction) p_txn;
478 pVolatileTransaction(p_txn)->p_prev = p_last;
479 p_last->p_next = (pVolatileTransaction) p_txn;
480
481 it_ent->second = (pVolatileTransaction) p_txn;
482
483 return SERVICE_NO_ERROR;
484 }
485 pVolatileTransaction p_last = it_ent->second->p_prev;
486
487 pVolatileTransaction(p_txn)->p_prev = p_last;
488 it_ent->second->p_prev = (pVolatileTransaction) p_txn;
489 pVolatileTransaction(p_txn)->p_next = it_ent->second;
490 p_last->p_next = (pVolatileTransaction) p_txn;
491
492 return SERVICE_NO_ERROR;
493 }
494
495
506 inline StatusCode put_in_tree(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, Name &parent, pBlock p_block) {
507
508 pVolatileTransaction p_root, p_parent = nullptr, p_next = nullptr;;
509
510 if ((p_root = it_ent->second) != nullptr) {
511 if (tree_key.find(ek) != tree_key.end()) return SERVICE_ERROR_WRITE_FORBIDDEN;
512
513 EntityKeyHash parent_ek = {ek.ent_hash, hash(parent)};
514
515 EntKeyVolXctMap::iterator it;
516
517 if ((it = tree_key.find(parent_ek)) == tree_key.end()) return SERVICE_ERROR_PARENT_NOT_FOUND;
518
519 p_parent = it->second;
520 p_next = p_parent->p_child;
521 }
522
523 pTransaction p_txn;
524 int ret;
525
526 if ((ret = new_transaction(p_txn)) != SERVICE_NO_ERROR) return ret;
527
528 pBlock p_new = block_malloc(p_block->total_bytes);
529 if (p_new == nullptr) {
530 destroy_transaction(p_txn);
531
532 return SERVICE_ERROR_NO_MEM;
533 }
534 memcpy(p_new, p_block, p_block->total_bytes);
535 p_txn->p_block = p_new;
536 p_txn->p_block->hash64 = 0;
537 p_txn->status = BLOCK_STATUS_READY;
538
539 tree_key[ek] = (pVolatileTransaction) p_txn;
540
541 pVolatileTransaction(p_txn)->p_parent = p_parent;
542 pVolatileTransaction(p_txn)->p_child = nullptr;
543 pVolatileTransaction(p_txn)->p_next = p_next;
544 pVolatileTransaction(p_txn)->num_wins = 0;
547
548 if (p_root == nullptr)
549 it_ent->second = (pVolatileTransaction) p_txn;
550 else
551 p_parent->p_child = (pVolatileTransaction) p_txn;
552
553 return SERVICE_NO_ERROR;
554 }
555
556
568
569 pBlock p_new = block_malloc(p_block->total_bytes);
570
571 if (p_new == nullptr) return SERVICE_ERROR_NO_MEM;
572
573 alloc_bytes -= p_replace->p_block->total_bytes;
574 free(p_replace->p_block);
575
576 memcpy(p_new, p_block, p_block->total_bytes);
577
578 p_replace->p_block = p_new;
579
580 return SERVICE_NO_ERROR;
581 }
582
583
595 inline StatusCode put_index(Index &index, pChar key, pBlock p_block, int mode) {
596
597 if (p_block->cell_type != CELL_TYPE_STRING || p_block->size != 1) return SERVICE_ERROR_BAD_BLOCK;
598
599 Index::iterator it = index.find(key);
600
601 if (it == index.end()) {
602 if (mode & WRITE_ONLY_IF_EXISTS) return SERVICE_ERROR_WRITE_FORBIDDEN;
603
604 index[key] = p_block->get_string(0);
605
606 return SERVICE_NO_ERROR;
607 }
608 if (mode & WRITE_ONLY_IF_NOT_EXISTS) return SERVICE_ERROR_WRITE_FORBIDDEN;
609
610 index[key] = p_block->get_string(0);
611
612 return SERVICE_NO_ERROR;
613 }
614
615
625 inline void destroy_item(int base, uint64_t ent_hash, pVolatileTransaction p_item) {
626
627 EntityKeyHash ek = {ent_hash, p_item->key_hash};
628 pTransaction p_txn = p_item;
629
630 switch (base) {
631 case BASE_DEQUE_10BIT: {
632 HashVolXctMap::iterator it_ent = deque_ent.find(ent_hash);
633 if (p_item == it_ent->second) {
634 if (p_item->p_next == p_item)
635 it_ent->second = nullptr;
636 else {
637 p_item->p_next->p_prev = p_item->p_prev;
638 p_item->p_prev->p_next = p_item->p_next;
639 it_ent->second = p_item->p_next;
640 }
641 } else {
642 p_item->p_next->p_prev = p_item->p_prev;
643 p_item->p_prev->p_next = p_item->p_next;
644 }
646 deque_key.erase(ek);
647 destroy_transaction(p_txn); }
648
649 return;
650
651 case BASE_QUEUE_10BIT: {
652 HashQueueEntMap::iterator it_ent = queue_ent.find(ent_hash);
653
654 it_ent->second.p_root = aat_remove(p_item, it_ent->second.p_root);
655 it_ent->second.queue_use--;
656
658 queue_key.erase(ek);
659 destroy_transaction(p_txn); }
660
661 return;
662
663 case BASE_TREE_10BIT:
665 destroy_transaction(p_txn);
666 tree_key.erase(ek);
667 }
668 }
669
670
678 inline uint64_t add_name(uint64_t hash, Name &key) {
679
680 HashNameUseMap::iterator it = name.find(hash);
681
682 if (it != name.end())
683 name[hash].use++;
684 else {
685 NameUse nu;
686
687 nu.use = 1;
688 memcpy(&nu.name, &key, sizeof(Name));
689
690 name[hash] = nu;
691 }
692 return hash;
693 }
694
695
701 inline void erase_name(uint64_t hash) {
702
703 HashNameUseMap::iterator it = name.find(hash);
704
705 if (it == name.end()) return;
706
707 if (--name[hash].use == 0)
708 name.erase(it);
709 }
710
711
724 inline StatusCode internal_get(pTransaction &p_txn, pString &p_str, uint64_t &pop_ent, Locator &what) {
725
726 int base;
728 EntityKeyHash ek;
729 ek.ent_hash = hash(what.entity);
730
731 pop_ent = 0;
732
733 switch (base = TenBitsAtAddress(what.base)) {
734 case BASE_DEQUE_10BIT: {
735 HashVolXctMap::iterator it_ent = deque_ent.find(ek.ent_hash);
736
737 if (it_ent == deque_ent.end()) return SERVICE_ERROR_ENTITY_NOT_FOUND;
738
739 p_root = it_ent->second; }
740
741 break;
742
743 case BASE_INDEX_10BIT: {
744 HashVolXctMap::iterator it_ent = index_ent.find(ek.ent_hash);
745
746 if (it_ent == index_ent.end()) return SERVICE_ERROR_ENTITY_NOT_FOUND;
747
748 p_root = it_ent->second; }
749
750 break;
751
752 case BASE_QUEUE_10BIT: {
753 HashQueueEntMap::iterator it_ent = queue_ent.find(ek.ent_hash);
754
755 if (it_ent == queue_ent.end()) return SERVICE_ERROR_ENTITY_NOT_FOUND;
756
757 p_root = it_ent->second.p_root; }
758
759 break;
760
761 case BASE_TREE_10BIT: {
762 HashVolXctMap::iterator it_ent = tree_ent.find(ek.ent_hash);
763
764 if (it_ent == tree_ent.end()) return SERVICE_ERROR_ENTITY_NOT_FOUND;
765
766 p_root = it_ent->second; }
767
768 break;
769
770 default:
771 return SERVICE_ERROR_WRONG_BASE;
772 }
773
774 if (p_root == nullptr) return SERVICE_ERROR_EMPTY_ENTITY;
775
776 Name key, second;
777 int command;
778
779 if (!parse_command(key, command, second, what.key, false))
780 return SERVICE_ERROR_PARSING_COMMAND;
781
782 switch (command) {
783 case COMMAND_JUST_THE_KEY:
784 switch (base) {
785 case BASE_DEQUE_10BIT: {
786 ek.key_hash = hash(key);
787 EntKeyVolXctMap::iterator it;
788
789 if ((it = deque_key.find(ek)) == deque_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
790
791 p_txn = it->second;
792 p_str = nullptr; }
793
794 return SERVICE_NO_ERROR;
795
796 case BASE_QUEUE_10BIT: {
797 ek.key_hash = hash(key);
798 EntKeyVolXctMap::iterator it;
799
800 if ((it = queue_key.find(ek)) == queue_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
801
802 p_txn = it->second;
803 p_str = nullptr; }
804
805 return SERVICE_NO_ERROR;
806
807 case BASE_TREE_10BIT: {
808 ek.key_hash = hash(key);
809 EntKeyVolXctMap::iterator it;
810
811 if ((it = tree_key.find(ek)) == tree_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
812
813 p_txn = it->second;
814 p_str = nullptr; }
815
816 return SERVICE_NO_ERROR;
817
818 default: {
819 Index::iterator it;
820
821 if ((it = p_root->p_hea->index.find(key)) == p_root->p_hea->index.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
822
823 p_txn = nullptr;
824 p_str = &it->second; }
825
826 return SERVICE_NO_ERROR;
827 }
828
829 case COMMAND_CHILD_10BIT: {
830 if (base != BASE_TREE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
831
832 ek.key_hash = hash(key);
833 EntKeyVolXctMap::iterator it;
834
835 if ((it = tree_key.find(ek)) == tree_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
836
837 p_txn = it->second->p_child;
838
839 if (p_txn == nullptr) return SERVICE_ERROR_BLOCK_NOT_FOUND;
840
841 p_str = nullptr; }
842
843 return SERVICE_NO_ERROR;
844
845 case COMMAND_PARENT_10BIT: {
846 if (base != BASE_TREE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
847
848 ek.key_hash = hash(key);
849 EntKeyVolXctMap::iterator it;
850
851 if ((it = tree_key.find(ek)) == tree_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
852
853 p_txn = it->second->p_parent;
854
855 if (p_txn == nullptr) return SERVICE_ERROR_BLOCK_NOT_FOUND;
856
857 p_str = nullptr; }
858
859 return SERVICE_NO_ERROR;
860
861 case COMMAND_NEXT_10BIT: {
862 ek.key_hash = hash(key);
863 EntKeyVolXctMap::iterator it;
864
865 switch (base) {
866 case BASE_TREE_10BIT: {
867 if ((it = tree_key.find(ek)) == tree_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
868
869 p_txn = it->second->p_next;
870
871 if (p_txn == nullptr) return SERVICE_ERROR_BLOCK_NOT_FOUND;
872
873 p_str = nullptr; }
874
875 return SERVICE_NO_ERROR;
876
877 case BASE_DEQUE_10BIT: {
878 if ((it = deque_key.find(ek)) == deque_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
879
880 p_txn = it->second->p_next;
881 p_str = nullptr; }
882
883 return SERVICE_NO_ERROR;
884 }
885 return SERVICE_ERROR_PARSING_COMMAND; }
886
887 case COMMAND_PREV_10BIT: {
888 if (base != BASE_DEQUE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
889
890 ek.key_hash = hash(key);
891 EntKeyVolXctMap::iterator it;
892
893 if ((it = deque_key.find(ek)) == deque_key.end()) return SERVICE_ERROR_BLOCK_NOT_FOUND;
894
895 p_txn = it->second->p_parent;
896 p_str = nullptr; }
897
898 return SERVICE_NO_ERROR;
899
900 case COMMAND_HIGH_10BIT:
901 case COMMAND_XHIGH_10BIT: {
902 if (base != BASE_QUEUE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
903
904 if (p_root == nullptr) return SERVICE_ERROR_EMPTY_ENTITY;
905
906 p_txn = aat_highest_priority(p_root);
907 p_str = nullptr;
908
909 if (command == COMMAND_XHIGH_10BIT)
910 pop_ent = ek.ent_hash; }
911
912 return SERVICE_NO_ERROR;
913
914 case COMMAND_LOW_10BIT:
915 case COMMAND_XLOW_10BIT: {
916 if (base != BASE_QUEUE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
917
918 if (p_root == nullptr) return SERVICE_ERROR_EMPTY_ENTITY;
919
920 p_txn = aat_lowest_priority(p_root);
921 p_str = nullptr;
922
923 if (command == COMMAND_XLOW_10BIT)
924 pop_ent = ek.ent_hash; }
925
926 return SERVICE_NO_ERROR;
927
928 case COMMAND_FIRST_10BIT:
929 case COMMAND_PFIRST_10BIT: {
930 if ( (base != BASE_DEQUE_10BIT)
931 && (base != BASE_TREE_10BIT || command == COMMAND_PFIRST_10BIT)) return SERVICE_ERROR_PARSING_COMMAND;
932
933 if (p_root == nullptr) return SERVICE_ERROR_EMPTY_ENTITY;
934
935 p_txn = p_root;
936 p_str = nullptr;
937
938 if (command == COMMAND_PFIRST_10BIT)
939 pop_ent = ek.ent_hash; }
940
941 return SERVICE_NO_ERROR;
942
943 case COMMAND_LAST_10BIT:
944 case COMMAND_PLAST_10BIT: {
945 if (base != BASE_DEQUE_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
946
947 if (p_root == nullptr) return SERVICE_ERROR_EMPTY_ENTITY;
948
949 p_txn = p_root->p_prev;
950 p_str = nullptr;
951
952 if (command == COMMAND_PLAST_10BIT)
953 pop_ent = ek.ent_hash; }
954
955 return SERVICE_NO_ERROR;
956 }
957
958// case command == COMMAND_GET_10BIT: This condition cannot fail or parse_command() would have.
959
960 if (base != BASE_INDEX_10BIT) return SERVICE_ERROR_PARSING_COMMAND;
961
962 p_txn = nullptr;
963 p_str = nullptr;
964 pop_ent = ek.ent_hash;
965
966 return SERVICE_NO_ERROR;
967 }
968
969
983 inline bool parse_command(Name &key_out, int &command, Name &second, Name key_in, bool is_put) {
984 if (key_in[0] == '~') {
985 key_out[0] = 0;
986 second [0] = 0;
987
988 switch (command = TenBitsAtAddress(&key_in[1])) {
989 case COMMAND_FIRST_10BIT:
990 case COMMAND_LAST_10BIT:
991 return true;
992
993 case COMMAND_PUT_10BIT:
994 return is_put;
995
996 case COMMAND_PFIRST_10BIT:
997 case COMMAND_PLAST_10BIT:
998 case COMMAND_HIGH_10BIT:
999 case COMMAND_LOW_10BIT:
1000 case COMMAND_GET_10BIT:
1001 case COMMAND_XHIGH_10BIT:
1002 case COMMAND_XLOW_10BIT:
1003 return !is_put;
1004
1005 default:
1006 if (!is_put || key_in[1] < '0' || key_in[1] > '9') return false;
1007
1008 int size, r_len;
1009
1010 if (sscanf(&key_in[1], "%d%n", &size, &r_len) != 1 || size <= 0 || key_in[r_len + 1] != 0) return false;
1011
1012 command = COMMAND_SIZE + size;
1013
1014 return true;
1015 }
1016 }
1017 strcpy(key_out, key_in);
1018 pChar pc = strchr(key_out, '~');
1019
1020 if (pc == nullptr) {
1021 command = COMMAND_JUST_THE_KEY;
1022 second[0] = 0;
1023
1024 return true;
1025 }
1026 (*pc++) = 0;
1027
1028 if (*pc == 0)
1029 return false;
1030
1031 if (is_put) {
1032 strcpy(second, pc);
1033 command = COMMAND_SECOND_ARG;
1034
1035 return true;
1036 }
1037 second[0] = 0;
1038
1039 switch (command = TenBitsAtAddress(pc)) {
1040 case COMMAND_CHILD_10BIT:
1041 case COMMAND_NEXT_10BIT:
1042 case COMMAND_PARENT_10BIT:
1043 case COMMAND_PREV_10BIT:
1044 return true;
1045 }
1046
1047 return false;
1048 }
1049
1050
1057 inline uint64_t hash(Name &name) {
1058
1059 int len = strnlen(name, sizeof(Name));
1060 int siz = sizeof(Name) - len;
1061
1062 if (siz > 1)
1063 memset(&name[len], 0, siz);
1064
1065 return MurmurHash64A(&name, NAME_SIZE);
1066 }
1067
1068
1075 inline void destroy_queue(uint64_t ent_hash, pVolatileTransaction p_txn) {
1076
1077 if (p_txn != nullptr) {
1078 destroy_queue(ent_hash, p_txn->p_prev);
1079 destroy_queue(ent_hash, p_txn->p_next);
1080
1081 destroy_item(BASE_QUEUE_10BIT, ent_hash, p_txn);
1082 }
1083 }
1084
1085
1092 inline void destroy_tree(uint64_t ent_hash, pVolatileTransaction p_txn) {
1093
1094 if (p_txn != nullptr) {
1095 destroy_tree(ent_hash, p_txn->p_next);
1096 destroy_tree(ent_hash, p_txn->p_child);
1097
1098 destroy_item(BASE_TREE_10BIT, ent_hash, p_txn);
1099 }
1100 }
1101
1102
1113
1114 return p_item->priority == p_tree->priority ? (uintptr_t) p_item < (uintptr_t) p_tree : p_item->priority < p_tree->priority;
1115 }
1116
1117
1127
1128 if (p_item != nullptr) {
1129 while (p_item->p_next != nullptr)
1130 p_item = p_item->p_next;
1131 };
1132
1133 return p_item;
1134 };
1135
1136
1146
1147 if (p_item != nullptr) {
1148 while (p_item->p_prev != nullptr)
1149 p_item = p_item->p_prev;
1150 };
1151
1152 return p_item;
1153 };
1154
1155
1165
1166 aat_decrease_level(p_tree);
1167
1168 p_tree = aat_skew(p_tree);
1169 if (p_tree->p_next != nullptr) {
1170 p_tree->p_next = aat_skew(p_tree->p_next);
1171
1172 if (p_tree->p_next->p_next != nullptr)
1173 p_tree->p_next->p_next = aat_skew(p_tree->p_next->p_next);
1174 };
1175 p_tree = aat_split(p_tree);
1176
1177 if (p_tree->p_next != nullptr)
1178 p_tree->p_next = aat_split(p_tree->p_next);
1179
1180 return p_tree;
1181 }
1182
1183
1192
1193 if (p_tree == nullptr || p_item == nullptr) return false;
1194
1195 if (p_item == p_tree) return true;
1196
1197 if (aat_to_left(p_item, p_tree)) return aat_is_in_tree(p_item, p_tree->p_prev);
1198
1199 return aat_is_in_tree(p_item, p_tree->p_next);
1200 };
1201
1202
1220 pVolatileTransaction p_parent,
1221 pVolatileTransaction p_tree,
1222 pVolatileTransaction &p_deep) {
1223 if (p_kill->p_next != nullptr)
1224 p_kill->p_next = aat_remove_deep(p_kill->p_next, p_kill, p_tree, p_deep);
1225
1226 else {
1227 if (p_parent == p_tree) {
1228 p_tree->p_prev = p_kill->p_prev; // Disconnect p_kill
1229 p_kill->level = p_tree->level;
1230 p_kill->p_next = p_tree->p_next;
1231 p_kill->p_prev = p_tree->p_prev; // p_kill is the new p_tree
1232
1233 return aat_rebalance(p_kill);
1234
1235 } else {
1236 p_deep = p_kill; // Save p_kill for the end
1237
1238 return p_kill->p_prev; // Disconnect p_kill
1239 }
1240 }
1241 if (p_parent != p_tree) return aat_rebalance(p_kill);
1242
1243 else {
1244 p_deep->level = p_tree->level;
1245 p_deep->p_next = p_tree->p_next;
1246 p_deep->p_prev = aat_rebalance(p_kill);
1247
1248 return aat_rebalance(p_deep);
1249 }
1250 }
1251
1252
1263
1264 if (p_tree == nullptr || p_item == nullptr) return p_tree;
1265
1266 if (p_item == p_tree) {
1267 if (p_tree->p_prev == nullptr) return p_tree->p_next;
1268
1269 else {
1270 if (p_tree->p_next == nullptr) return p_tree->p_prev;
1271
1272 else {
1273 pVolatileTransaction p_deep = nullptr;
1274 return aat_remove_deep(p_tree->p_prev, p_tree, p_tree, p_deep);
1275 }
1276 }
1277 } else {
1278 if (aat_to_left(p_item, p_tree))
1279 p_tree->p_prev = aat_remove(p_item, p_tree->p_prev);
1280
1281 else
1282 p_tree->p_next = aat_remove(p_item, p_tree->p_next);
1283 }
1284
1285 return aat_rebalance(p_tree);
1286 };
1287
1288
1296
1297 if (p_item->p_prev == nullptr) {
1298 if (p_item->p_next == nullptr)
1299 p_item->level = 1;
1300
1301 else {
1302 int should_be = p_item->p_next->level + 1;
1303
1304 if (should_be < p_item->level)
1305 p_item->level = should_be;
1306 }
1307 } else {
1308 if (p_item->p_next == nullptr) {
1309 int should_be = p_item->p_prev->level + 1;
1310
1311 if (should_be < p_item->level)
1312 p_item->level = should_be;
1313
1314 } else {
1315 int should_be = std::min(p_item->p_prev->level, p_item->p_next->level) + 1;
1316
1317 if (should_be < p_item->level) {
1318 p_item->level = should_be;
1319
1320 if (should_be < p_item->p_next->level)
1321 p_item->p_next->level = should_be;
1322 }
1323 }
1324 }
1325 };
1326
1327
1336 // rotate p_next if p_prev child has same level
1337
1338 if (p_item->p_prev != nullptr && p_item->level == p_item->p_prev->level) {
1339 pVolatileTransaction p_left = p_item->p_prev;
1340
1341 p_item->p_prev = p_left->p_next;
1342 p_left->p_next = p_item;
1343
1344 return p_left;
1345 }
1346
1347 return p_item;
1348 };
1349
1350
1359 // rotate p_prev if there are two p_next children on same level
1360
1361 if ( p_item->p_next != nullptr
1362 && p_item->p_next->p_next != nullptr
1363 && p_item->level == p_item->p_next->p_next->level) {
1364
1365 pVolatileTransaction p_right = p_item->p_next;
1366
1367 p_item->p_next = p_right->p_prev;
1368 p_right->p_prev = p_item;
1369 if (p_item->p_prev == nullptr && p_item->p_next == nullptr && p_item->level > 1) {
1370 p_item->level = 1;
1371/* The following condition looks like it could be problematic (since the previous one is already an undocumented deviation from the
1372canonical method description). It has never been observed (in intense unit testing) and should only be considered if problems happen.
1373It may very well be impossible, who knows. Just keep it as a remark, unless someone smarter proves it unnecessary. */
1374 // if ( p_right->level != 2
1375 // || ( p_right->p_next->p_next != nullptr
1376 // && p_right->p_next->p_next->level >= 2))
1377 // p_right->level++; // This is actually a breakpoint possibility, not a solution!!
1378
1379 } else
1380 p_right->level++;
1381
1382 return p_right;
1383 }
1384
1385 return p_item;
1386 };
1387
1388
1398 // Do the normal binary tree insertion procedure. Set the result of the
1399 // recursive call to the correct child in case a new node was created or the
1400 // root of the subtree changes.
1401
1402 if (p_tree == nullptr) {
1403 p_new->level = 1;
1404 p_new->p_prev = nullptr;
1405 p_new->p_next = nullptr;
1406
1407 return p_new;
1408
1409 } else {
1410
1411 if (aat_to_left(p_new, p_tree))
1412 p_tree->p_prev = aat_insert(p_new, p_tree->p_prev);
1413
1414 else
1415 p_tree->p_next = aat_insert(p_new, p_tree->p_next);
1416 }
1417
1418 // Perform aat_skew and then aat_split. The conditionals that determine whether or
1419 // not a rotation will occur or not are inside of the procedures, as given above.
1420
1421 p_tree = aat_skew(p_tree);
1422 p_tree = aat_split(p_tree);
1423
1424 return p_tree;
1425 };
1426
1427 uint64_t key_seed;
1436};
1438
1439#ifdef CATCH_TEST
1440
1441// Instancing Volatile
1442// -------------------
1443
1444extern Volatile VOL;
1445
1446#endif
1447
1448} // namespace jazz_elements
1449
1450#endif // ifndef INCLUDED_JAZZ_ELEMENTS_VOLATILE
A configuration file as a key/value store.
Definition utils.h:218
Container: A Service to manage Jazz blocks. All Jazz blocks are managed by this or a descendant of th...
Definition container.h:287
virtual StatusCode put(pChar p_where, pBlock p_block, int mode=WRITE_AS_BASE_DEFAULT)
Definition container.cpp:1927
virtual StatusCode remove(pChar p_where)
Definition container.cpp:1965
virtual StatusCode locate(Locator &location, pChar p_what)
Definition container.cpp:1857
virtual StatusCode new_entity(pChar p_where)
Definition container.cpp:1946
uint64_t alloc_bytes
The current allocation in bytes.
Definition container.h:627
virtual StatusCode copy(pChar p_where, pChar p_what)
Definition container.cpp:1987
virtual StatusCode get(pTransaction &p_txn, pChar p_what)
Definition container.cpp:1784
pBlock block_malloc(size_t size)
Definition container.h:570
virtual StatusCode header(StaticBlockHeader &hea, pChar p_what)
Definition container.cpp:1878
A simple logger.
Definition utils.h:248
pBlock get_block(int idx)
Definition tuple.h:221
Volatile: A Service to manage data objects in RAM.
Definition volatile.h:243
void aat_decrease_level(pVolatileTransaction p_item)
Definition volatile.h:1295
virtual void destroy_transaction(pTransaction &p_txn)
Definition volatile.cpp:224
void erase_name(uint64_t hash)
Definition volatile.h:701
StatusCode internal_get(pTransaction &p_txn, pString &p_str, uint64_t &pop_ent, Locator &what)
Definition volatile.h:724
StatusCode put_in_deque(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, pBlock p_block, bool first=false)
Definition volatile.h:443
pVolatileTransaction aat_split(pVolatileTransaction p_item)
Definition volatile.h:1358
StatusCode new_volatile()
Definition volatile.cpp:116
pVolatileTransaction aat_highest_priority(pVolatileTransaction p_item)
Definition volatile.h:1126
HashQueueEntMap queue_ent
Map of queues.
Definition volatile.h:1429
HashNameUseMap name
Map of names and to do reverse conversion to a hash() (including count of uses)
Definition volatile.h:1428
HashVolXctMap tree_ent
Map of trees.
Definition volatile.h:1431
bool aat_to_left(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1112
pVolatileTransaction aat_remove(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1262
EntKeyVolXctMap tree_key
Map of tree (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1435
virtual pChar const id()
Definition volatile.cpp:59
virtual StatusCode new_transaction(pTransaction &p_txn)
Definition volatile.cpp:185
virtual StatusCode remove(Locator &where)
Definition volatile.cpp:728
StatusCode shut_down()
Definition volatile.cpp:106
void destroy_tree(uint64_t ent_hash, pVolatileTransaction p_txn)
Definition volatile.h:1092
virtual StatusCode get(pTransaction &p_txn, Locator &what)
Definition volatile.cpp:275
pVolatileTransaction aat_skew(pVolatileTransaction p_item)
Definition volatile.h:1335
uint64_t hash(Name &name)
Definition volatile.h:1057
StatusCode put_replace(pVolatileTransaction p_replace, pBlock p_block)
Definition volatile.h:567
void destroy_item(int base, uint64_t ent_hash, pVolatileTransaction p_item)
Definition volatile.h:625
EntKeyVolXctMap deque_key
Map of deque (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1433
StatusCode put_in_tree(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, Name &parent, pBlock p_block)
Definition volatile.h:506
EntKeyVolXctMap queue_key
Map of queue (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1434
void new_key(Name &key)
Definition volatile.h:309
pVolatileTransaction aat_insert(pVolatileTransaction p_new, pVolatileTransaction p_tree)
Definition volatile.h:1397
bool aat_is_in_tree(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1191
bool parse_command(Name &key_out, int &command, Name &second, Name key_in, bool is_put)
Definition volatile.h:983
virtual StatusCode header(StaticBlockHeader &hea, Locator &what)
Definition volatile.cpp:449
virtual StatusCode locate(Locator &location, Locator &what)
Definition volatile.cpp:409
pVolatileTransaction aat_lowest_priority(pVolatileTransaction p_item)
Definition volatile.h:1145
StatusCode populate_index(Index &index, pBlock p_block)
Definition volatile.h:325
pVolatileTransaction aat_rebalance(pVolatileTransaction p_tree)
Definition volatile.h:1164
StatusCode put_index(Index &index, pChar key, pBlock p_block, int mode)
Definition volatile.h:595
StatusCode destroy_volatile()
Definition volatile.cpp:154
HashVolXctMap index_ent
Map of indices.
Definition volatile.h:1432
virtual StatusCode copy(Locator &where, Locator &what)
Definition volatile.cpp:873
virtual StatusCode new_entity(Locator &where)
Definition volatile.cpp:668
void destroy_queue(uint64_t ent_hash, pVolatileTransaction p_txn)
Definition volatile.h:1075
StatusCode start()
Starts the service, checking the configuration and starting the Service.
Definition volatile.cpp:70
pVolatileTransaction aat_remove_deep(pVolatileTransaction p_kill, pVolatileTransaction p_parent, pVolatileTransaction p_tree, pVolatileTransaction &p_deep)
Definition volatile.h:1219
void base_names(BaseNames &base_names)
Definition volatile.cpp:916
virtual StatusCode put(Locator &where, pBlock p_block, int mode=WRITE_AS_BASE_DEFAULT)
Definition volatile.cpp:539
HashVolXctMap deque_ent
Map of deques.
Definition volatile.h:1430
uint64_t add_name(uint64_t hash, Name &key)
Definition volatile.h:678
StatusCode put_queue_insert(uint64_t ent_hash, Name &key, double priority, pBlock p_block, int mode)
Definition volatile.h:353
uint64_t key_seed
Seed to create unique key ids.
Definition volatile.h:1427
The namespace for Jazz Utils, Blocks, Kinds, Tuples, Containers, etc.
Definition block.cpp:39
Tuple * pTuple
A pointer to a Tuple object.
Definition tuple.h:273
std::string String
A standard string used in many other places in Jazz.
Definition types.h:239
String * pString
A pointer to an String.
Definition volatile.h:187
int TenBitsAtAddress(const char *str)
Get ten bits taking the least significant 5 of the first two characters of a string.
Definition utils.h:167
std::map< uint64_t, NameUse > HashNameUseMap
HashNameUseMap: A map from hashes to Name and number of times the name is used.
Definition volatile.h:183
struct VolatileTransaction * pVolatileTransaction
A pointer to a Transaction-descendant wrapper over a Block for Volatile blocks.
Definition volatile.h:79
std::map< String, pContainer > BaseNames
A map of names for the containers (or structure engines like "map" or "tree" inside Volatile).
Definition container.h:157
uint64_t MurmurHash64A(const void *key, int len)
MurmurHash2, 64-bit versions, by Austin Appleby.
Definition utils.cpp:250
std::map< uint64_t, QueueEnt > HashQueueEntMap
HashQueueEntMap: A map from hashes to QueueEnt.
Definition volatile.h:159
char * pChar
A pointer to a char buffer.
Definition types.h:189
class Block * pBlock
A (forward defined) pointer to a Block.
Definition block.h:66
char Name[NAME_SIZE]
A short identifier used in Blocks, Containers and API.
Definition types.h:187
std::map< uint64_t, pVolatileTransaction > HashVolXctMap
HashVolXctMap: A map from hashes to pointers to VolatileTransaction.
Definition volatile.h:152
int StatusCode
Type returned by the Service API.
Definition utils.h:142
std::map< EntityKeyHash, pVolatileTransaction > EntKeyVolXctMap
EntKeyVolXctMap: A map from (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:166
Volatile * pVolatile
Pointer to Volatile.
Definition volatile.h:1437
std::map< String, String > Index
An Index kept in RAM by Volatile implemented as an stdlib map (string, string)
Definition types.h:243
Index index
Any kind of Index.
Definition types.h:263
EntityKeyHash: A record containing separate hashes for entity and key.
Definition volatile.h:111
bool operator<(const EntityKeyHash &o) const
Constructor for EntityKeyHash.
Definition volatile.h:131
uint64_t ent_hash
The hash of the entity name.
Definition volatile.h:112
bool operator==(const EntityKeyHash &o) const
Constructor for EntityKeyHash.
Definition volatile.h:121
uint64_t key_hash
The hash of the key name.
Definition volatile.h:113
Locator: A minimal structure to define the location of resources inside a Container.
Definition container.h:189
char base[SHORT_NAME_SIZE]
A Jazz node level unique name to locate a Container and possibly a type of service inside it.
Definition container.h:190
Name entity
Another abstraction inside node.container.base, like the name of a table in a database.
Definition container.h:191
Name key
A key identifying a block inside the entity.
Definition container.h:192
NameUse: A pair of Name and number of times the name is used.
Definition volatile.h:173
int use
Number of times name is used. Increase by add_name() calls to the same name, decreased/destroyed by e...
Definition volatile.h:174
Name name
The name in plain text.
Definition volatile.h:175
QueueEnt: An entity to store a priority queue.
Definition volatile.h:141
int queue_use
Number of node inserted.
Definition volatile.h:143
int queue_size
The maximum number of nodes supported by the queue.
Definition volatile.h:142
pVolatileTransaction p_root
The root node.
Definition volatile.h:144
A Binary Compatible BlockHeader without Index (and therefore constructors/destructors)
Definition types.h:270
Transaction: A wrapper over a Block that defines the communication of a block with a Container.
Definition container.h:167
pBlockHeader p_hea
A pointer to the Block (if status == BLOCK_STATUS_READY) for Index.
Definition container.h:170
int status
The status of the block transaction.
Definition container.h:173
pBlock p_block
A pointer to the Block (if status == BLOCK_STATUS_READY) for Tensor, Kind and Tuple.
Definition container.h:169
VolatileTransaction: A Transaction-descendant wrapper over a Block for Volatile blocks.
Definition volatile.h:86
pVolatileTransaction p_next
Pointer to the next node in a deque or next sibling in a tree.
Definition volatile.h:91
uint64_t key_hash
Node locator hash required to find the ID of a related (next, ...) node.
Definition volatile.h:104
double priority
... or priority value in a queue.
Definition volatile.h:94
pVolatileTransaction p_child
Pointer to the first child in a tree ...
Definition volatile.h:93
int num_visits
... or MCTS tree node number of visits.
Definition volatile.h:102
pVolatileTransaction p_prev
Pointer to the previous node in a deque ...
Definition volatile.h:88
int times_used
Times the block has been reassigned in the queue ...
Definition volatile.h:101
int level
Level in the AA tree (used for auto-balancing) ...
Definition volatile.h:97
pVolatileTransaction p_parent
... or parent node in a tree.
Definition volatile.h:89
int num_wins
... or MCTS tree node number of wins.
Definition volatile.h:98