Jazz 1.25.+
Loading...
Searching...
No Matches
volatile.h
1/* Jazz (c) 2018-2025 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
50
51namespace jazz_elements
52{
53
54#define BASE_DEQUE_10BIT 0x0a4 //< First 10 bits of base "deque"
55#define BASE_INDEX_10BIT 0x1c9 //< First 10 bits of base "index"
56#define BASE_QUEUE_10BIT 0x2b1 //< First 10 bits of base "queue"
57#define BASE_TREE_10BIT 0x254 //< First 10 bits of base "tree"
58
59#define COMMAND_JUST_THE_KEY 0x000 //< No command in the key
60#define COMMAND_CHILD_10BIT 0x103 //< First 10 bits of command "ch{ild}"
61#define COMMAND_FIRST_10BIT 0x126 //< First 10 bits of command "fi{rst}"
62#define COMMAND_GET_10BIT 0x0a7 //< First 10 bits of command "ge{t}"
63#define COMMAND_HIGH_10BIT 0x128 //< First 10 bits of command "hi{ghest}"
64#define COMMAND_LAST_10BIT 0x02c //< First 10 bits of command "la{st}"
65#define COMMAND_LOW_10BIT 0x1ec //< First 10 bits of command "lo{west}"
66#define COMMAND_NEXT_10BIT 0x0ae //< First 10 bits of command "ne{xt}"
67#define COMMAND_PARENT_10BIT 0x030 //< First 10 bits of command "pa{rent}"
68#define COMMAND_PFIRST_10BIT 0x0d0 //< First 10 bits of command "pf{irst}"
69#define COMMAND_PLAST_10BIT 0x190 //< First 10 bits of command "pl{ast}"
70#define COMMAND_PREV_10BIT 0x250 //< First 10 bits of command "pr{ev}"
71#define COMMAND_PUT_10BIT 0x2b0 //< First 10 bits of command "pu{t}"
72#define COMMAND_XHIGH_10BIT 0x118 //< First 10 bits of command "xh{ighest}"
73#define COMMAND_XLOW_10BIT 0x198 //< First 10 bits of command "xl{owest}"
74#define COMMAND_SECOND_ARG 0x3ff //< In a put call with a key, it is either a parent key or a priority.
75#define COMMAND_SIZE 0x400 //< For numbers, defining a queue size, this is added to avoid overlap.
76
77
81
82
107
108
113 uint64_t ent_hash;
114 uint64_t key_hash;
115
122 bool operator==(const EntityKeyHash &o) const {
123 return ent_hash == o.ent_hash && key_hash == o.key_hash;
124 }
125
132 bool operator<(const EntityKeyHash &o) const {
133 return ent_hash < o.ent_hash || (ent_hash == o.ent_hash && key_hash < o.key_hash);
134 }
135};
136
137
147
148
153typedef std::map<uint64_t, pVolatileTransaction> HashVolXctMap;
154
155
160typedef std::map<uint64_t, QueueEnt> HashQueueEntMap;
161
162
167typedef std::map<EntityKeyHash, pVolatileTransaction> EntKeyVolXctMap;
168
169
174struct NameUse {
175 int use;
177};
178
179
184typedef std::map<uint64_t, NameUse> HashNameUseMap;
185
186
188typedef std::string* pString;
189
190
244class Volatile : public Container {
245
246 public:
247
248 Volatile(pLogger a_logger, pConfigFile a_config);
249 ~Volatile();
250
251 virtual pChar const id();
252
253 StatusCode start ();
255
256 // The easy interface (Requires explicit pulling because of the native interface using the same names.)
257
258 using Container::get;
259 using Container::header;
260 using Container::put;
261 using Container::locate;
263 using Container::remove;
264 using Container::copy;
265
267 virtual void destroy_transaction (pTransaction &p_txn);
268
269 // The "native" interface
270
271 virtual StatusCode get (pTransaction &p_txn,
272 Locator &what);
273 virtual StatusCode get (pTransaction &p_txn,
274 Locator &what,
275 pBlock p_row_filter);
276 virtual StatusCode get (pTransaction &p_txn,
277 Locator &what,
278 pChar name);
279 virtual StatusCode locate (Locator &location,
280 Locator &what);
281 virtual StatusCode header (StaticBlockHeader &hea,
282 Locator &what);
283 virtual StatusCode header (pTransaction &p_txn,
284 Locator &what);
285 virtual StatusCode put (Locator &where,
286 pBlock p_block,
287 int mode = WRITE_AS_BASE_DEFAULT);
288 virtual StatusCode new_entity(Locator &where);
289 virtual StatusCode remove (Locator &where);
290 virtual StatusCode copy (Locator &where,
291 Locator &what);
292
293 // Support for container names in the BaseAPI .base_names()
294
296
297#ifndef CATCH_TEST
298 private:
299#endif
300
303
310 inline void new_key(Name &key) {
311 sprintf(key, "k%014lx", ++key_seed);
312 }
313
314
326 inline StatusCode populate_index(Index &index, pBlock p_block) {
327
328 if (p_block->cell_type != CELL_TYPE_TUPLE_ITEM || p_block->size != 2)
329 return SERVICE_ERROR_BAD_BLOCK;
330
331 pBlock p_key = pTuple(p_block)->get_block(0);
332 pBlock p_val = pTuple(p_block)->get_block(1);
333
334 if ( p_key->cell_type != CELL_TYPE_STRING || p_val->cell_type != CELL_TYPE_STRING
335 || p_key->rank != 1 || p_val->rank != 1 || p_key->size != p_val->size)
336 return SERVICE_ERROR_BAD_BLOCK;
337
338 for (int i = 0; i < p_key->size; i++)
339 index[p_key->get_string(i)] = p_val->get_string(i);
340
341 return SERVICE_NO_ERROR;
342 }
343
344
356 inline StatusCode put_queue_insert(uint64_t ent_hash, Name &key, double priority, pBlock p_block, int mode) {
357 HashQueueEntMap::iterator it_queue = queue_ent.find(ent_hash);
358
359 if (it_queue == queue_ent.end())
360 return SERVICE_ERROR_ENTITY_NOT_FOUND;
361
362 EntityKeyHash ek = {ent_hash, hash(key)};
363 EntKeyVolXctMap::iterator it_item = queue_key.find(ek);
364
365 if (it_item != queue_key.end()) {
366 if (mode & WRITE_ONLY_IF_NOT_EXISTS)
367 return SERVICE_ERROR_WRITE_FORBIDDEN;
368
369 pVolatileTransaction p_item = it_item->second;
370
371 pBlock p_new = block_malloc(p_block->total_bytes);
372 if (p_new == nullptr)
373 return SERVICE_ERROR_NO_MEM;
374
375 alloc_bytes -= p_item->p_block->total_bytes;
376 free(p_item->p_block);
377
378 memcpy(p_new, p_block, p_block->total_bytes);
379
380 p_item->status = BLOCK_STATUS_EMPTY;
381
382 it_queue->second.p_root = aat_remove(p_item, it_queue->second.p_root);
383
384 p_item->priority = priority;
385 p_item->times_used++;
386 p_item->p_block = p_new;
387
388 p_item->p_block->hash64 = 0;
389
390 p_item->status = BLOCK_STATUS_READY;
391
392 it_queue->second.p_root = aat_insert(p_item, it_queue->second.p_root);
393
394 queue_key[ek] = p_item;
395
396 return SERVICE_NO_ERROR;
397 }
398 if (mode & WRITE_ONLY_IF_EXISTS)
399 return SERVICE_ERROR_WRITE_FORBIDDEN;
400
401 if (it_queue->second.queue_use == it_queue->second.queue_size) {
402 pVolatileTransaction p_lowest = aat_lowest_priority(it_queue->second.p_root);
403
404 if (p_lowest->priority >= priority)
405 return SERVICE_ERROR_LOW_PRIORITY;
406
407 destroy_item(BASE_QUEUE_10BIT, ent_hash, p_lowest);
408 }
409
410 pTransaction p_txn;
411
412 int ret = new_transaction(p_txn);
413
414 if (ret != SERVICE_NO_ERROR)
415 return ret;
416
417 pBlock p_new = block_malloc(p_block->total_bytes);
418 if (p_new == nullptr) {
419 destroy_transaction(p_txn);
420
421 return SERVICE_ERROR_NO_MEM;
422 }
423 memcpy(p_new, p_block, p_block->total_bytes);
424
425 p_new->hash64 = 0;
426
427 pVolatileTransaction(p_txn)->priority = priority;
430 pVolatileTransaction(p_txn)->p_block = p_new;
431 pVolatileTransaction(p_txn)->status = BLOCK_STATUS_READY;
432
433 it_queue->second.p_root = aat_insert((pVolatileTransaction) p_txn, it_queue->second.p_root);
434 it_queue->second.queue_use++;
435
436 queue_key[ek] = (pVolatileTransaction) p_txn;
437
438 return SERVICE_NO_ERROR;
439 }
440
441
452 inline StatusCode put_in_deque(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, pBlock p_block, bool first = false) {
453
454 pTransaction p_txn;
455 int ret;
456
457 if ((ret = new_transaction(p_txn)) != SERVICE_NO_ERROR)
458 return ret;
459
460 pBlock p_new = block_malloc(p_block->total_bytes);
461 if (p_new == nullptr) {
462 destroy_transaction(p_txn);
463
464 return SERVICE_ERROR_NO_MEM;
465 }
466 memcpy(p_new, p_block, p_block->total_bytes);
467 p_txn->p_block = p_new;
468 p_txn->p_block->hash64 = 0;
469 p_txn->status = BLOCK_STATUS_READY;
470
471 deque_key[ek] = (pVolatileTransaction) p_txn;
473
474 if (it_ent->second == nullptr) {
475 it_ent->second = (pVolatileTransaction) p_txn;
478
479 return SERVICE_NO_ERROR;
480 }
481
482 if (first) {
483 pVolatileTransaction p_2nd = it_ent->second;
484 pVolatileTransaction p_last = p_2nd->p_prev;
485
486 pVolatileTransaction(p_txn)->p_next = p_2nd;
487 p_2nd->p_prev = (pVolatileTransaction) p_txn;
488 pVolatileTransaction(p_txn)->p_prev = p_last;
489 p_last->p_next = (pVolatileTransaction) p_txn;
490
491 it_ent->second = (pVolatileTransaction) p_txn;
492
493 return SERVICE_NO_ERROR;
494 }
495 pVolatileTransaction p_last = it_ent->second->p_prev;
496
497 pVolatileTransaction(p_txn)->p_prev = p_last;
498 it_ent->second->p_prev = (pVolatileTransaction) p_txn;
499 pVolatileTransaction(p_txn)->p_next = it_ent->second;
500 p_last->p_next = (pVolatileTransaction) p_txn;
501
502 return SERVICE_NO_ERROR;
503 }
504
505
516 inline StatusCode put_in_tree(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, Name &parent, pBlock p_block) {
517
518 pVolatileTransaction p_root, p_parent = nullptr, p_next = nullptr;;
519
520 if ((p_root = it_ent->second) != nullptr) {
521 if (tree_key.find(ek) != tree_key.end())
522 return SERVICE_ERROR_WRITE_FORBIDDEN;
523
524 EntityKeyHash parent_ek = {ek.ent_hash, hash(parent)};
525
526 EntKeyVolXctMap::iterator it;
527
528 if ((it = tree_key.find(parent_ek)) == tree_key.end())
529 return SERVICE_ERROR_PARENT_NOT_FOUND;
530
531 p_parent = it->second;
532 p_next = p_parent->p_child;
533 }
534
535 pTransaction p_txn;
536 int ret;
537
538 if ((ret = new_transaction(p_txn)) != SERVICE_NO_ERROR)
539 return ret;
540
541 pBlock p_new = block_malloc(p_block->total_bytes);
542 if (p_new == nullptr) {
543 destroy_transaction(p_txn);
544
545 return SERVICE_ERROR_NO_MEM;
546 }
547 memcpy(p_new, p_block, p_block->total_bytes);
548 p_txn->p_block = p_new;
549 p_txn->p_block->hash64 = 0;
550 p_txn->status = BLOCK_STATUS_READY;
551
552 tree_key[ek] = (pVolatileTransaction) p_txn;
553
554 pVolatileTransaction(p_txn)->p_parent = p_parent;
555 pVolatileTransaction(p_txn)->p_child = nullptr;
556 pVolatileTransaction(p_txn)->p_next = p_next;
557 pVolatileTransaction(p_txn)->num_wins = 0;
560
561 if (p_root == nullptr)
562 it_ent->second = (pVolatileTransaction) p_txn;
563 else
564 p_parent->p_child = (pVolatileTransaction) p_txn;
565
566 return SERVICE_NO_ERROR;
567 }
568
569
581
582 pBlock p_new = block_malloc(p_block->total_bytes);
583 if (p_new == nullptr)
584 return SERVICE_ERROR_NO_MEM;
585
586 alloc_bytes -= p_replace->p_block->total_bytes;
587 free(p_replace->p_block);
588
589 memcpy(p_new, p_block, p_block->total_bytes);
590
591 p_replace->p_block = p_new;
592
593 return SERVICE_NO_ERROR;
594 }
595
596
608 inline StatusCode put_index(Index &index, pChar key, pBlock p_block, int mode) {
609
610 if (p_block->cell_type != CELL_TYPE_STRING || p_block->size != 1)
611 return SERVICE_ERROR_BAD_BLOCK;
612
613 Index::iterator it = index.find(key);
614
615 if (it == index.end()) {
616 if (mode & WRITE_ONLY_IF_EXISTS)
617 return SERVICE_ERROR_WRITE_FORBIDDEN;
618
619 index[key] = p_block->get_string(0);
620
621 return SERVICE_NO_ERROR;
622 }
623 if (mode & WRITE_ONLY_IF_NOT_EXISTS)
624 return SERVICE_ERROR_WRITE_FORBIDDEN;
625
626 index[key] = p_block->get_string(0);
627
628 return SERVICE_NO_ERROR;
629 }
630
631
641 inline void destroy_item(int base, uint64_t ent_hash, pVolatileTransaction p_item) {
642
643 EntityKeyHash ek = {ent_hash, p_item->key_hash};
644 pTransaction p_txn = p_item;
645
646 switch (base) {
647 case BASE_DEQUE_10BIT: {
648 HashVolXctMap::iterator it_ent = deque_ent.find(ent_hash);
649 if (p_item == it_ent->second) {
650 if (p_item->p_next == p_item)
651 it_ent->second = nullptr;
652 else {
653 p_item->p_next->p_prev = p_item->p_prev;
654 p_item->p_prev->p_next = p_item->p_next;
655 it_ent->second = p_item->p_next;
656 }
657 } else {
658 p_item->p_next->p_prev = p_item->p_prev;
659 p_item->p_prev->p_next = p_item->p_next;
660 }
662 deque_key.erase(ek);
663 destroy_transaction(p_txn); }
664
665 return;
666
667 case BASE_QUEUE_10BIT: {
668 HashQueueEntMap::iterator it_ent = queue_ent.find(ent_hash);
669
670 it_ent->second.p_root = aat_remove(p_item, it_ent->second.p_root);
671 it_ent->second.queue_use--;
672
674 queue_key.erase(ek);
675 destroy_transaction(p_txn); }
676
677 return;
678
679 case BASE_TREE_10BIT:
681 destroy_transaction(p_txn);
682 tree_key.erase(ek);
683 }
684 }
685
686
694 inline uint64_t add_name(uint64_t hash, Name &key) {
695
696 HashNameUseMap::iterator it = name.find(hash);
697
698 if (it != name.end())
699 name[hash].use++;
700 else {
701 NameUse nu;
702
703 nu.use = 1;
704 memcpy(&nu.name, &key, sizeof(Name));
705
706 name[hash] = nu;
707 }
708 return hash;
709 }
710
711
717 inline void erase_name(uint64_t hash) {
718
719 HashNameUseMap::iterator it = name.find(hash);
720
721 if (it == name.end())
722 return;
723
724 if (--name[hash].use == 0)
725 name.erase(it);
726 }
727
728
741 inline StatusCode internal_get(pTransaction &p_txn, pString &p_str, uint64_t &pop_ent, Locator &what) {
742
743 int base;
745 EntityKeyHash ek;
746 ek.ent_hash = hash(what.entity);
747
748 pop_ent = 0;
749
750 switch (base = TenBitsAtAddress(what.base)) {
751 case BASE_DEQUE_10BIT: {
752 HashVolXctMap::iterator it_ent = deque_ent.find(ek.ent_hash);
753
754 if (it_ent == deque_ent.end())
755 return SERVICE_ERROR_ENTITY_NOT_FOUND;
756
757 p_root = it_ent->second; }
758
759 break;
760
761 case BASE_INDEX_10BIT: {
762 HashVolXctMap::iterator it_ent = index_ent.find(ek.ent_hash);
763
764 if (it_ent == index_ent.end())
765 return SERVICE_ERROR_ENTITY_NOT_FOUND;
766
767 p_root = it_ent->second; }
768
769 break;
770
771 case BASE_QUEUE_10BIT: {
772 HashQueueEntMap::iterator it_ent = queue_ent.find(ek.ent_hash);
773
774 if (it_ent == queue_ent.end())
775 return SERVICE_ERROR_ENTITY_NOT_FOUND;
776
777 p_root = it_ent->second.p_root; }
778
779 break;
780
781 case BASE_TREE_10BIT: {
782 HashVolXctMap::iterator it_ent = tree_ent.find(ek.ent_hash);
783
784 if (it_ent == tree_ent.end())
785 return SERVICE_ERROR_ENTITY_NOT_FOUND;
786
787 p_root = it_ent->second; }
788
789 break;
790
791 default:
792 return SERVICE_ERROR_WRONG_BASE;
793 }
794
795 if (p_root == nullptr)
796 return SERVICE_ERROR_EMPTY_ENTITY;
797
798 Name key, second;
799 int command;
800
801 if (!parse_command(key, command, second, what.key, false))
802 return SERVICE_ERROR_PARSING_COMMAND;
803
804 switch (command) {
805 case COMMAND_JUST_THE_KEY:
806 switch (base) {
807 case BASE_DEQUE_10BIT: {
808 ek.key_hash = hash(key);
809 EntKeyVolXctMap::iterator it;
810
811 if ((it = deque_key.find(ek)) == deque_key.end())
812 return SERVICE_ERROR_BLOCK_NOT_FOUND;
813
814 p_txn = it->second;
815 p_str = nullptr; }
816
817 return SERVICE_NO_ERROR;
818
819 case BASE_QUEUE_10BIT: {
820 ek.key_hash = hash(key);
821 EntKeyVolXctMap::iterator it;
822
823 if ((it = queue_key.find(ek)) == queue_key.end())
824 return SERVICE_ERROR_BLOCK_NOT_FOUND;
825
826 p_txn = it->second;
827 p_str = nullptr; }
828
829 return SERVICE_NO_ERROR;
830
831 case BASE_TREE_10BIT: {
832 ek.key_hash = hash(key);
833 EntKeyVolXctMap::iterator it;
834
835 if ((it = tree_key.find(ek)) == tree_key.end())
836 return SERVICE_ERROR_BLOCK_NOT_FOUND;
837
838 p_txn = it->second;
839 p_str = nullptr; }
840
841 return SERVICE_NO_ERROR;
842
843 default: {
844 Index::iterator it;
845
846 if ((it = p_root->p_hea->index.find(key)) == p_root->p_hea->index.end())
847 return SERVICE_ERROR_BLOCK_NOT_FOUND;
848
849 p_txn = nullptr;
850 p_str = &it->second; }
851
852 return SERVICE_NO_ERROR;
853 }
854
855 case COMMAND_CHILD_10BIT: {
856 if (base != BASE_TREE_10BIT)
857 return SERVICE_ERROR_PARSING_COMMAND;
858
859 ek.key_hash = hash(key);
860 EntKeyVolXctMap::iterator it;
861
862 if ((it = tree_key.find(ek)) == tree_key.end())
863 return SERVICE_ERROR_BLOCK_NOT_FOUND;
864
865 p_txn = it->second->p_child;
866
867 if (p_txn == nullptr)
868 return SERVICE_ERROR_BLOCK_NOT_FOUND;
869
870 p_str = nullptr; }
871
872 return SERVICE_NO_ERROR;
873
874 case COMMAND_PARENT_10BIT: {
875 if (base != BASE_TREE_10BIT)
876 return SERVICE_ERROR_PARSING_COMMAND;
877
878 ek.key_hash = hash(key);
879 EntKeyVolXctMap::iterator it;
880
881 if ((it = tree_key.find(ek)) == tree_key.end())
882 return SERVICE_ERROR_BLOCK_NOT_FOUND;
883
884 p_txn = it->second->p_parent;
885
886 if (p_txn == nullptr)
887 return SERVICE_ERROR_BLOCK_NOT_FOUND;
888
889 p_str = nullptr; }
890
891 return SERVICE_NO_ERROR;
892
893 case COMMAND_NEXT_10BIT: {
894 ek.key_hash = hash(key);
895 EntKeyVolXctMap::iterator it;
896
897 switch (base) {
898 case BASE_TREE_10BIT: {
899 if ((it = tree_key.find(ek)) == tree_key.end())
900 return SERVICE_ERROR_BLOCK_NOT_FOUND;
901
902 p_txn = it->second->p_next;
903
904 if (p_txn == nullptr)
905 return SERVICE_ERROR_BLOCK_NOT_FOUND;
906
907 p_str = nullptr; }
908
909 return SERVICE_NO_ERROR;
910
911 case BASE_DEQUE_10BIT: {
912 if ((it = deque_key.find(ek)) == deque_key.end())
913 return SERVICE_ERROR_BLOCK_NOT_FOUND;
914
915 p_txn = it->second->p_next;
916 p_str = nullptr; }
917
918 return SERVICE_NO_ERROR;
919 }
920 return SERVICE_ERROR_PARSING_COMMAND; }
921
922 case COMMAND_PREV_10BIT: {
923 if (base != BASE_DEQUE_10BIT)
924 return SERVICE_ERROR_PARSING_COMMAND;
925
926 ek.key_hash = hash(key);
927 EntKeyVolXctMap::iterator it;
928
929 if ((it = deque_key.find(ek)) == deque_key.end())
930 return SERVICE_ERROR_BLOCK_NOT_FOUND;
931
932 p_txn = it->second->p_parent;
933 p_str = nullptr; }
934
935 return SERVICE_NO_ERROR;
936
937 case COMMAND_HIGH_10BIT:
938 case COMMAND_XHIGH_10BIT: {
939 if (base != BASE_QUEUE_10BIT)
940 return SERVICE_ERROR_PARSING_COMMAND;
941
942 if (p_root == nullptr)
943 return SERVICE_ERROR_EMPTY_ENTITY;
944
945 p_txn = aat_highest_priority(p_root);
946 p_str = nullptr;
947
948 if (command == COMMAND_XHIGH_10BIT)
949 pop_ent = ek.ent_hash; }
950
951 return SERVICE_NO_ERROR;
952
953 case COMMAND_LOW_10BIT:
954 case COMMAND_XLOW_10BIT: {
955 if (base != BASE_QUEUE_10BIT)
956 return SERVICE_ERROR_PARSING_COMMAND;
957
958 if (p_root == nullptr)
959 return SERVICE_ERROR_EMPTY_ENTITY;
960
961 p_txn = aat_lowest_priority(p_root);
962 p_str = nullptr;
963
964 if (command == COMMAND_XLOW_10BIT)
965 pop_ent = ek.ent_hash; }
966
967 return SERVICE_NO_ERROR;
968
969 case COMMAND_FIRST_10BIT:
970 case COMMAND_PFIRST_10BIT: {
971 if (base != BASE_DEQUE_10BIT && (base != BASE_TREE_10BIT || command == COMMAND_PFIRST_10BIT))
972 return SERVICE_ERROR_PARSING_COMMAND;
973
974 if (p_root == nullptr)
975 return SERVICE_ERROR_EMPTY_ENTITY;
976
977 p_txn = p_root;
978 p_str = nullptr;
979
980 if (command == COMMAND_PFIRST_10BIT)
981 pop_ent = ek.ent_hash; }
982
983 return SERVICE_NO_ERROR;
984
985 case COMMAND_LAST_10BIT:
986 case COMMAND_PLAST_10BIT: {
987 if (base != BASE_DEQUE_10BIT)
988 return SERVICE_ERROR_PARSING_COMMAND;
989
990 if (p_root == nullptr)
991 return SERVICE_ERROR_EMPTY_ENTITY;
992
993 p_txn = p_root->p_prev;
994 p_str = nullptr;
995
996 if (command == COMMAND_PLAST_10BIT)
997 pop_ent = ek.ent_hash; }
998
999 return SERVICE_NO_ERROR;
1000
1001 case COMMAND_GET_10BIT:
1002 if (base != BASE_INDEX_10BIT)
1003 return SERVICE_ERROR_PARSING_COMMAND;
1004
1005 p_txn = nullptr;
1006 p_str = nullptr;
1007 pop_ent = ek.ent_hash;
1008
1009 return SERVICE_NO_ERROR;
1010 }
1011
1012 return SERVICE_ERROR_PARSING_COMMAND;
1013 }
1014
1015
1029 inline bool parse_command(Name &key_out, int &command, Name &second, Name key_in, bool is_put) {
1030 if (key_in[0] == '~') {
1031 key_out[0] = 0;
1032 second [0] = 0;
1033
1034 switch (command = TenBitsAtAddress(&key_in[1])) {
1035 case COMMAND_FIRST_10BIT:
1036 case COMMAND_LAST_10BIT:
1037 return true;
1038
1039 case COMMAND_PUT_10BIT:
1040 return is_put;
1041
1042 case COMMAND_PFIRST_10BIT:
1043 case COMMAND_PLAST_10BIT:
1044 case COMMAND_HIGH_10BIT:
1045 case COMMAND_LOW_10BIT:
1046 case COMMAND_GET_10BIT:
1047 case COMMAND_XHIGH_10BIT:
1048 case COMMAND_XLOW_10BIT:
1049 return !is_put;
1050
1051 default:
1052 if (!is_put || key_in[1] < '0' || key_in[1] > '9')
1053 return false;
1054
1055 int size, r_len;
1056
1057 if (sscanf(&key_in[1], "%d%n", &size, &r_len) != 1 || size <= 0 || key_in[r_len + 1] != 0)
1058 return false;
1059
1060 command = COMMAND_SIZE + size;
1061
1062 return true;
1063 }
1064 }
1065 strcpy(key_out, key_in);
1066 pChar pc = strchr(key_out, '~');
1067
1068 if (pc == nullptr) {
1069 command = COMMAND_JUST_THE_KEY;
1070 second[0] = 0;
1071
1072 return true;
1073 }
1074 (*pc++) = 0;
1075
1076 if (*pc == 0)
1077 return false;
1078
1079 if (is_put) {
1080 strcpy(second, pc);
1081 command = COMMAND_SECOND_ARG;
1082
1083 return true;
1084 }
1085 second[0] = 0;
1086
1087 switch (command = TenBitsAtAddress(pc)) {
1088 case COMMAND_CHILD_10BIT:
1089 case COMMAND_NEXT_10BIT:
1090 case COMMAND_PARENT_10BIT:
1091 case COMMAND_PREV_10BIT:
1092 return true;
1093 }
1094
1095 return false;
1096 }
1097
1098
1105 inline uint64_t hash(Name &name) {
1106
1107 int len = strnlen(name, sizeof(Name));
1108 int siz = sizeof(Name) - len;
1109
1110 if (siz > 1)
1111 memset(&name[len], 0, siz);
1112
1113 return MurmurHash64A(&name, NAME_SIZE);
1114 }
1115
1116
1123 inline void destroy_queue(uint64_t ent_hash, pVolatileTransaction p_txn) {
1124
1125 if (p_txn != nullptr) {
1126 destroy_queue(ent_hash, p_txn->p_prev);
1127 destroy_queue(ent_hash, p_txn->p_next);
1128
1129 destroy_item(BASE_QUEUE_10BIT, ent_hash, p_txn);
1130 }
1131 }
1132
1133
1140 inline void destroy_tree(uint64_t ent_hash, pVolatileTransaction p_txn) {
1141
1142 if (p_txn != nullptr) {
1143 destroy_tree(ent_hash, p_txn->p_next);
1144 destroy_tree(ent_hash, p_txn->p_child);
1145
1146 destroy_item(BASE_TREE_10BIT, ent_hash, p_txn);
1147 }
1148 }
1149
1150
1161
1162 return p_item->priority == p_tree->priority ? (uintptr_t) p_item < (uintptr_t) p_tree : p_item->priority < p_tree->priority;
1163 }
1164
1165
1175
1176 if (p_item != nullptr) {
1177 while (p_item->p_next != nullptr)
1178 p_item = p_item->p_next;
1179 };
1180
1181 return p_item;
1182 };
1183
1184
1194
1195 if (p_item != nullptr) {
1196 while (p_item->p_prev != nullptr)
1197 p_item = p_item->p_prev;
1198 };
1199
1200 return p_item;
1201 };
1202
1203
1213
1214 aat_decrease_level(p_tree);
1215
1216 p_tree = aat_skew(p_tree);
1217 if (p_tree->p_next != nullptr) {
1218 p_tree->p_next = aat_skew(p_tree->p_next);
1219
1220 if (p_tree->p_next->p_next != nullptr)
1221 p_tree->p_next->p_next = aat_skew(p_tree->p_next->p_next);
1222 };
1223 p_tree = aat_split(p_tree);
1224
1225 if (p_tree->p_next != nullptr)
1226 p_tree->p_next = aat_split(p_tree->p_next);
1227
1228 return p_tree;
1229 }
1230
1231
1240
1241 if (p_tree == nullptr || p_item == nullptr)
1242 return false;
1243
1244 if (p_item == p_tree)
1245 return true;
1246
1247 if (aat_to_left(p_item, p_tree))
1248 return aat_is_in_tree(p_item, p_tree->p_prev);
1249
1250 return aat_is_in_tree(p_item, p_tree->p_next);
1251 };
1252
1253
1271 pVolatileTransaction p_parent,
1272 pVolatileTransaction p_tree,
1273 pVolatileTransaction &p_deep) {
1274 if (p_kill->p_next != nullptr)
1275 p_kill->p_next = aat_remove_deep(p_kill->p_next, p_kill, p_tree, p_deep);
1276
1277 else {
1278 if (p_parent == p_tree) {
1279 p_tree->p_prev = p_kill->p_prev; // Disconnect p_kill
1280 p_kill->level = p_tree->level;
1281 p_kill->p_next = p_tree->p_next;
1282 p_kill->p_prev = p_tree->p_prev; // p_kill is the new p_tree
1283
1284 return aat_rebalance(p_kill);
1285
1286 } else {
1287 p_deep = p_kill; // Save p_kill for the end
1288
1289 return p_kill->p_prev; // Disconnect p_kill
1290 }
1291 }
1292 if (p_parent != p_tree)
1293 return aat_rebalance(p_kill);
1294
1295 else {
1296 p_deep->level = p_tree->level;
1297 p_deep->p_next = p_tree->p_next;
1298 p_deep->p_prev = aat_rebalance(p_kill);
1299
1300 return aat_rebalance(p_deep);
1301 }
1302 }
1303
1304
1315
1316 if (p_tree == nullptr || p_item == nullptr)
1317 return p_tree;
1318
1319 if (p_item == p_tree) {
1320 if (p_tree->p_prev == nullptr)
1321 return p_tree->p_next;
1322
1323 else {
1324 if (p_tree->p_next == nullptr)
1325 return p_tree->p_prev;
1326
1327 else {
1328 pVolatileTransaction p_deep = nullptr;
1329 return aat_remove_deep(p_tree->p_prev, p_tree, p_tree, p_deep);
1330 }
1331 }
1332 } else {
1333 if (aat_to_left(p_item, p_tree))
1334 p_tree->p_prev = aat_remove(p_item, p_tree->p_prev);
1335
1336 else
1337 p_tree->p_next = aat_remove(p_item, p_tree->p_next);
1338 }
1339
1340 return aat_rebalance(p_tree);
1341 };
1342
1343
1351
1352 if (p_item->p_prev == nullptr) {
1353 if (p_item->p_next == nullptr)
1354 p_item->level = 1;
1355
1356 else {
1357 int should_be = p_item->p_next->level + 1;
1358
1359 if (should_be < p_item->level) {
1360 p_item->level = should_be;
1361
1362 if (should_be < p_item->p_next->level)
1363 p_item->p_next->level = should_be;
1364 }
1365 }
1366 } else {
1367 if (p_item->p_next == nullptr) {
1368 int should_be = p_item->p_prev->level + 1;
1369
1370 if (should_be < p_item->level)
1371 p_item->level = should_be;
1372
1373 } else {
1374 int should_be = std::min(p_item->p_prev->level, p_item->p_next->level) + 1;
1375
1376 if (should_be < p_item->level) {
1377 p_item->level = should_be;
1378
1379 if (should_be < p_item->p_next->level)
1380 p_item->p_next->level = should_be;
1381 }
1382 }
1383 }
1384 };
1385
1386
1395 // rotate p_next if p_prev child has same level
1396
1397 if (p_item->p_prev != nullptr && p_item->level == p_item->p_prev->level) {
1398 pVolatileTransaction p_left = p_item->p_prev;
1399
1400 p_item->p_prev = p_left->p_next;
1401 p_left->p_next = p_item;
1402
1403 return p_left;
1404 }
1405
1406 return p_item;
1407 };
1408
1409
1418 // rotate p_prev if there are two p_next children on same level
1419
1420 if ( p_item->p_next != nullptr
1421 && p_item->p_next->p_next != nullptr
1422 && p_item->level == p_item->p_next->p_next->level) {
1423
1424 pVolatileTransaction p_right = p_item->p_next;
1425
1426 p_item->p_next = p_right->p_prev;
1427 p_right->p_prev = p_item;
1428 if (p_item->p_prev == nullptr && p_item->p_next == nullptr && p_item->level > 1) {
1429 p_item->level = 1;
1430/* The following condition looks like it could be problematic (since the previous one is already an undocumented deviation from the
1431canonical method description). It has never been observed (in intense unit testing) and should only be considered if problems happen.
1432It may very well be impossible, who knows. Just keep it as a remark, unless someone smarter proves it unnecessary. */
1433 // if ( p_right->level != 2
1434 // || ( p_right->p_next->p_next != nullptr
1435 // && p_right->p_next->p_next->level >= 2))
1436 // p_right->level++; // This is actually a breakpoint possibility, not a solution!!
1437
1438 } else
1439 p_right->level++;
1440
1441 return p_right;
1442 }
1443
1444 return p_item;
1445 };
1446
1447
1457 // Do the normal binary tree insertion procedure. Set the result of the
1458 // recursive call to the correct child in case a new node was created or the
1459 // root of the subtree changes.
1460
1461 if (p_tree == nullptr) {
1462 p_new->level = 1;
1463 p_new->p_prev = nullptr;
1464 p_new->p_next = nullptr;
1465
1466 return p_new;
1467
1468 } else {
1469
1470 if (aat_to_left(p_new, p_tree))
1471 p_tree->p_prev = aat_insert(p_new, p_tree->p_prev);
1472
1473 else
1474 p_tree->p_next = aat_insert(p_new, p_tree->p_next);
1475 }
1476
1477 // Perform aat_skew and then aat_split. The conditionals that determine whether or
1478 // not a rotation will occur or not are inside of the procedures, as given above.
1479
1480 p_tree = aat_skew(p_tree);
1481 p_tree = aat_split(p_tree);
1482
1483 return p_tree;
1484 };
1485
1486 uint64_t key_seed;
1495};
1497
1498#ifdef CATCH_TEST
1499
1500// Instancing Volatile
1501// -------------------
1502
1503extern Volatile VOL;
1504
1505#endif
1506
1507} // namespace jazz_elements
1508
1509#endif // ifndef INCLUDED_JAZZ_ELEMENTS_VOLATILE
A configuration file as a key/value store.
Definition utils.h:217
Container: A Service to manage Jazz blocks. All Jazz blocks are managed by this or a descendant of th...
Definition container.h:282
virtual StatusCode put(pChar p_where, pBlock p_block, int mode=WRITE_AS_BASE_DEFAULT)
Definition container.cpp:1903
virtual StatusCode remove(pChar p_where)
Definition container.cpp:1941
virtual StatusCode locate(Locator &location, pChar p_what)
Definition container.cpp:1833
virtual StatusCode new_entity(pChar p_where)
Definition container.cpp:1922
uint64_t alloc_bytes
The current allocation in bytes.
Definition container.h:600
virtual StatusCode copy(pChar p_where, pChar p_what)
Definition container.cpp:1963
virtual StatusCode get(pTransaction &p_txn, pChar p_what)
Definition container.cpp:1760
pBlock block_malloc(size_t size)
Definition container.h:549
virtual StatusCode header(StaticBlockHeader &hea, pChar p_what)
Definition container.cpp:1854
A simple logger.
Definition utils.h:245
pBlock get_block(int idx)
Definition tuple.h:224
Volatile: A Service to manage data objects in RAM.
Definition volatile.h:244
void aat_decrease_level(pVolatileTransaction p_item)
Definition volatile.h:1350
virtual void destroy_transaction(pTransaction &p_txn)
Definition volatile.cpp:229
void erase_name(uint64_t hash)
Definition volatile.h:717
StatusCode internal_get(pTransaction &p_txn, pString &p_str, uint64_t &pop_ent, Locator &what)
Definition volatile.h:741
StatusCode put_in_deque(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, pBlock p_block, bool first=false)
Definition volatile.h:452
pVolatileTransaction aat_split(pVolatileTransaction p_item)
Definition volatile.h:1417
StatusCode new_volatile()
Definition volatile.cpp:117
pVolatileTransaction aat_highest_priority(pVolatileTransaction p_item)
Definition volatile.h:1174
HashQueueEntMap queue_ent
Map of queues.
Definition volatile.h:1488
HashNameUseMap name
Map of names and to do reverse conversion to a hash() (including count of uses)
Definition volatile.h:1487
HashVolXctMap tree_ent
Map of trees.
Definition volatile.h:1490
bool aat_to_left(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1160
pVolatileTransaction aat_remove(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1314
EntKeyVolXctMap tree_key
Map of tree (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1494
virtual pChar const id()
Definition volatile.cpp:60
virtual StatusCode new_transaction(pTransaction &p_txn)
Definition volatile.cpp:190
virtual StatusCode remove(Locator &where)
Definition volatile.cpp:755
StatusCode shut_down()
Definition volatile.cpp:107
void destroy_tree(uint64_t ent_hash, pVolatileTransaction p_txn)
Definition volatile.h:1140
virtual StatusCode get(pTransaction &p_txn, Locator &what)
Definition volatile.cpp:280
pVolatileTransaction aat_skew(pVolatileTransaction p_item)
Definition volatile.h:1394
uint64_t hash(Name &name)
Definition volatile.h:1105
StatusCode put_replace(pVolatileTransaction p_replace, pBlock p_block)
Definition volatile.h:580
void destroy_item(int base, uint64_t ent_hash, pVolatileTransaction p_item)
Definition volatile.h:641
EntKeyVolXctMap deque_key
Map of deque (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1492
StatusCode put_in_tree(HashVolXctMap::iterator it_ent, EntityKeyHash &ek, Name &key, Name &parent, pBlock p_block)
Definition volatile.h:516
EntKeyVolXctMap queue_key
Map of queue (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:1493
void new_key(Name &key)
Definition volatile.h:310
pVolatileTransaction aat_insert(pVolatileTransaction p_new, pVolatileTransaction p_tree)
Definition volatile.h:1456
bool aat_is_in_tree(pVolatileTransaction p_item, pVolatileTransaction p_tree)
Definition volatile.h:1239
bool parse_command(Name &key_out, int &command, Name &second, Name key_in, bool is_put)
Definition volatile.h:1029
virtual StatusCode header(StaticBlockHeader &hea, Locator &what)
Definition volatile.cpp:455
virtual StatusCode locate(Locator &location, Locator &what)
Definition volatile.cpp:415
pVolatileTransaction aat_lowest_priority(pVolatileTransaction p_item)
Definition volatile.h:1193
StatusCode populate_index(Index &index, pBlock p_block)
Definition volatile.h:326
pVolatileTransaction aat_rebalance(pVolatileTransaction p_tree)
Definition volatile.h:1212
StatusCode put_index(Index &index, pChar key, pBlock p_block, int mode)
Definition volatile.h:608
StatusCode destroy_volatile()
Definition volatile.cpp:159
HashVolXctMap index_ent
Map of indices.
Definition volatile.h:1491
virtual StatusCode copy(Locator &where, Locator &what)
Definition volatile.cpp:907
virtual StatusCode new_entity(Locator &where)
Definition volatile.cpp:690
void destroy_queue(uint64_t ent_hash, pVolatileTransaction p_txn)
Definition volatile.h:1123
StatusCode start()
Starts the service, checking the configuration and starting the Service.
Definition volatile.cpp:71
pVolatileTransaction aat_remove_deep(pVolatileTransaction p_kill, pVolatileTransaction p_parent, pVolatileTransaction p_tree, pVolatileTransaction &p_deep)
Definition volatile.h:1270
void base_names(BaseNames &base_names)
Definition volatile.cpp:953
virtual StatusCode put(Locator &where, pBlock p_block, int mode=WRITE_AS_BASE_DEFAULT)
Definition volatile.cpp:546
HashVolXctMap deque_ent
Map of deques.
Definition volatile.h:1489
uint64_t add_name(uint64_t hash, Name &key)
Definition volatile.h:694
StatusCode put_queue_insert(uint64_t ent_hash, Name &key, double priority, pBlock p_block, int mode)
Definition volatile.h:356
uint64_t key_seed
Seed to create unique key ids.
Definition volatile.h:1486
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:276
int TenBitsAtAddress(const char *str)
Get ten bits taking the least significant 5 of the first two characters of a string.
Definition utils.h:166
std::map< std::string, std::string > Index
An Index kept in RAM by Volatile implemented as an stdlib map (string, string)
Definition types.h:238
std::map< uint64_t, NameUse > HashNameUseMap
HashNameUseMap: A map from hashes to Name and number of times the name is used.
Definition volatile.h:184
struct VolatileTransaction * pVolatileTransaction
A pointer to a Transaction-descendant wrapper over a Block for Volatile blocks.
Definition volatile.h:80
std::string * pString
A pointer to an std::string.
Definition volatile.h:188
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:160
char * pChar
A pointer to a char buffer.
Definition types.h:185
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:183
std::map< std::string, pContainer > BaseNames
A map of names for the containers (or structure engines like "map" or "tree" inside Volatile).
Definition container.h:152
std::map< uint64_t, pVolatileTransaction > HashVolXctMap
HashVolXctMap: A map from hashes to pointers to VolatileTransaction.
Definition volatile.h:153
int StatusCode
Type returned by the Service API.
Definition utils.h:141
std::map< EntityKeyHash, pVolatileTransaction > EntKeyVolXctMap
EntKeyVolXctMap: A map from (entity, key) hashes to pointers to VolatileTransaction.
Definition volatile.h:167
Volatile * pVolatile
Pointer to Volatile.
Definition volatile.h:1496
Index index
Any kind of Index.
Definition types.h:258
EntityKeyHash: A record containing separate hashes for entity and key.
Definition volatile.h:112
bool operator<(const EntityKeyHash &o) const
Constructor for EntityKeyHash.
Definition volatile.h:132
uint64_t ent_hash
The hash of the entity name.
Definition volatile.h:113
bool operator==(const EntityKeyHash &o) const
Constructor for EntityKeyHash.
Definition volatile.h:122
uint64_t key_hash
The hash of the key name.
Definition volatile.h:114
Locator: A minimal structure to define the location of resources inside a Container.
Definition container.h:184
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:185
Name entity
Another abstraction inside node.container.base, like the name of a table in a database.
Definition container.h:186
Name key
A key identifying a block inside the entity.
Definition container.h:187
NameUse: A pair of Name and number of times the name is used.
Definition volatile.h:174
int use
Number of times name is used. Increase by add_name() calls to the same name, decreased/destroyed by e...
Definition volatile.h:175
Name name
The name in plain text.
Definition volatile.h:176
QueueEnt: An entity to store a priority queue.
Definition volatile.h:142
int queue_use
Number of node inserted.
Definition volatile.h:144
int queue_size
The maximum number of nodes supported by the queue.
Definition volatile.h:143
pVolatileTransaction p_root
The root node.
Definition volatile.h:145
A Binary Compatible BlockHeader without Index (and therefore constructors/destructors)
Definition types.h:265
Transaction: A wrapper over a Block that defines the communication of a block with a Container.
Definition container.h:162
pBlockHeader p_hea
A pointer to the Block (if status == BLOCK_STATUS_READY) for Index.
Definition container.h:165
int status
The status of the block transaction.
Definition container.h:168
pBlock p_block
A pointer to the Block (if status == BLOCK_STATUS_READY) for Tensor, Kind and Tuple.
Definition container.h:164
VolatileTransaction: A Transaction-descendant wrapper over a Block for Volatile blocks.
Definition volatile.h:87
pVolatileTransaction p_next
Pointer to the next node in a deque or next sibling in a tree.
Definition volatile.h:92
uint64_t key_hash
Node locator hash required to find the ID of a related (next, ...) node.
Definition volatile.h:105
double priority
... or priority value in a queue.
Definition volatile.h:95
pVolatileTransaction p_child
Pointer to the first child in a tree ...
Definition volatile.h:94
int num_visits
... or MCTS tree node number of visits.
Definition volatile.h:103
pVolatileTransaction p_prev
Pointer to the previous node in a deque ...
Definition volatile.h:89
int times_used
Times the block has been reassigned in the queue ...
Definition volatile.h:102
int level
Level in the AA tree (used for auto-balancing) ...
Definition volatile.h:98
pVolatileTransaction p_parent
... or parent node in a tree.
Definition volatile.h:90
int num_wins
... or MCTS tree node number of wins.
Definition volatile.h:99