CDT  v4.2.0
инструменты разработчика
multi_index.hpp
См. документацию.
1
5#pragma once
6
7#include "../../contracts/eosio/action.hpp"
8#include "../../core/eosio/name.hpp"
9#include "../../core/eosio/serialize.hpp"
10#include "../../core/eosio/fixed_bytes.hpp"
11
12#include <bluegrass/meta/for_each.hpp>
13
14#include <vector>
15#include <tuple>
16#include <functional>
17#include <utility>
18#include <type_traits>
19#include <iterator>
20#include <limits>
21#include <algorithm>
22#include <memory>
23
29namespace eosio {
30 namespace internal_use_do_not_use {
31 extern "C" {
32 __attribute__((eosio_wasm_import))
33 int32_t db_store_i64(uint64_t, uint64_t, uint64_t, uint64_t, const void*, uint32_t);
34
35 __attribute__((eosio_wasm_import))
36 void db_update_i64(int32_t, uint64_t, const void*, uint32_t);
37
38 __attribute__((eosio_wasm_import))
39 void db_remove_i64(int32_t);
40
41 __attribute__((eosio_wasm_import))
42 int32_t db_get_i64(int32_t, const void*, uint32_t);
43
44 __attribute__((eosio_wasm_import))
45 int32_t db_next_i64(int32_t, uint64_t*);
46
47 __attribute__((eosio_wasm_import))
48 int32_t db_previous_i64(int32_t, uint64_t*);
49
50 __attribute__((eosio_wasm_import))
51 int32_t db_find_i64(uint64_t, uint64_t, uint64_t, uint64_t);
52
53 __attribute__((eosio_wasm_import))
54 int32_t db_lowerbound_i64(uint64_t, uint64_t, uint64_t, uint64_t);
55
56 __attribute__((eosio_wasm_import))
57 int32_t db_upperbound_i64(uint64_t, uint64_t, uint64_t, uint64_t);
58
59 __attribute__((eosio_wasm_import))
60 int32_t db_end_i64(uint64_t, uint64_t, uint64_t);
61
62 __attribute__((eosio_wasm_import))
63 int32_t db_idx64_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint64_t*);
64
65 __attribute__((eosio_wasm_import))
66 void db_idx64_update(int32_t, uint64_t, const uint64_t*);
67
68 __attribute__((eosio_wasm_import))
69 void db_idx64_remove(int32_t);
70
71 __attribute__((eosio_wasm_import))
72 int32_t db_idx64_next(int32_t, uint64_t*);
73
74 __attribute__((eosio_wasm_import))
75 int32_t db_idx64_previous(int32_t, uint64_t*);
76
77 __attribute__((eosio_wasm_import))
78 int32_t db_idx64_find_primary(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t);
79
80 __attribute__((eosio_wasm_import))
81 int32_t db_idx64_find_secondary(uint64_t, uint64_t, uint64_t, const uint64_t*, uint64_t*);
82
83 __attribute__((eosio_wasm_import))
84 int32_t db_idx64_lowerbound(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*);
85
86 __attribute__((eosio_wasm_import))
87 int32_t db_idx64_upperbound(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*);
88
89 __attribute__((eosio_wasm_import))
90 int32_t db_idx64_end(uint64_t, uint64_t, uint64_t);
91
92 __attribute__((eosio_wasm_import))
93 int32_t db_idx128_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint128_t*);
94
95 __attribute__((eosio_wasm_import))
96 void db_idx128_update(int32_t, uint64_t, const uint128_t*);
97
98 __attribute__((eosio_wasm_import))
99 void db_idx128_remove(int32_t);
100
101 __attribute__((eosio_wasm_import))
102 int32_t db_idx128_next(int32_t, uint64_t*);
103
104 __attribute__((eosio_wasm_import))
105 int32_t db_idx128_previous(int32_t, uint64_t*);
106
107 __attribute__((eosio_wasm_import))
108 int32_t db_idx128_find_primary(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t);
109
110 __attribute__((eosio_wasm_import))
111 int32_t db_idx128_find_secondary(uint64_t, uint64_t, uint64_t, const uint128_t*, uint64_t*);
112
113 __attribute__((eosio_wasm_import))
114 int32_t db_idx128_lowerbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t*);
115
116 __attribute__((eosio_wasm_import))
117 int32_t db_idx128_upperbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t*);
118
119 __attribute__((eosio_wasm_import))
120 int32_t db_idx128_end(uint64_t, uint64_t, uint64_t);
121
122 __attribute__((eosio_wasm_import))
123 int32_t db_idx256_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint128_t*, uint32_t);
124
125 __attribute__((eosio_wasm_import))
126 void db_idx256_update(int32_t, uint64_t, const uint128_t*, uint32_t);
127
128 __attribute__((eosio_wasm_import))
129 void db_idx256_remove(int32_t);
130
131 __attribute__((eosio_wasm_import))
132 int32_t db_idx256_next(int32_t, uint64_t*);
133
134 __attribute__((eosio_wasm_import))
135 int32_t db_idx256_previous(int32_t, uint64_t*);
136
137 __attribute__((eosio_wasm_import))
138 int32_t db_idx256_find_primary(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t);
139
140 __attribute__((eosio_wasm_import))
141 int32_t db_idx256_find_secondary(uint64_t, uint64_t, uint64_t, const uint128_t*, uint32_t, uint64_t*);
142
143 __attribute__((eosio_wasm_import))
144 int32_t db_idx256_lowerbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t*);
145
146 __attribute__((eosio_wasm_import))
147 int32_t db_idx256_upperbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t*);
148
149 __attribute__((eosio_wasm_import))
150 int32_t db_idx256_end(uint64_t, uint64_t, uint64_t);
151
152 __attribute__((eosio_wasm_import))
153 int32_t db_idx_double_store(uint64_t, uint64_t, uint64_t, uint64_t, const double*);
154
155 __attribute__((eosio_wasm_import))
156 void db_idx_double_update(int32_t, uint64_t, const double*);
157
158 __attribute__((eosio_wasm_import))
159 void db_idx_double_remove(int32_t);
160
161 __attribute__((eosio_wasm_import))
162 int32_t db_idx_double_next(int32_t, uint64_t*);
163
164 __attribute__((eosio_wasm_import))
165 int32_t db_idx_double_previous(int32_t, uint64_t*);
166
167 __attribute__((eosio_wasm_import))
168 int32_t db_idx_double_find_primary(uint64_t, uint64_t, uint64_t, double*, uint64_t);
169
170 __attribute__((eosio_wasm_import))
171 int32_t db_idx_double_find_secondary(uint64_t, uint64_t, uint64_t, const double*, uint64_t*);
172
173 __attribute__((eosio_wasm_import))
174 int32_t db_idx_double_lowerbound(uint64_t, uint64_t, uint64_t, double*, uint64_t*);
175
176 __attribute__((eosio_wasm_import))
177 int32_t db_idx_double_upperbound(uint64_t, uint64_t, uint64_t, double*, uint64_t*);
178
179 __attribute__((eosio_wasm_import))
180 int32_t db_idx_double_end(uint64_t, uint64_t, uint64_t);
181
182 __attribute__((eosio_wasm_import))
183 int32_t db_idx_long_double_store(uint64_t, uint64_t, uint64_t, uint64_t, const long double*);
184
185 __attribute__((eosio_wasm_import))
186 void db_idx_long_double_update(int32_t, uint64_t, const long double*);
187
188 __attribute__((eosio_wasm_import))
189 void db_idx_long_double_remove(int32_t);
190
191 __attribute__((eosio_wasm_import))
192 int32_t db_idx_long_double_next(int32_t, uint64_t*);
193
194 __attribute__((eosio_wasm_import))
195 int32_t db_idx_long_double_previous(int32_t, uint64_t*);
196
197 __attribute__((eosio_wasm_import))
198 int32_t db_idx_long_double_find_primary(uint64_t, uint64_t, uint64_t, long double*, uint64_t);
199
200 __attribute__((eosio_wasm_import))
201 int32_t db_idx_long_double_find_secondary(uint64_t, uint64_t, uint64_t, const long double*, uint64_t*);
202
203 __attribute__((eosio_wasm_import))
204 int32_t db_idx_long_double_lowerbound(uint64_t, uint64_t, uint64_t, long double*, uint64_t*);
205
206 __attribute__((eosio_wasm_import))
207 int32_t db_idx_long_double_upperbound(uint64_t, uint64_t, uint64_t, long double*, uint64_t*);
208
209 __attribute__((eosio_wasm_import))
210 int32_t db_idx_long_double_end(uint64_t, uint64_t, uint64_t);
211 }
212 };
213
214constexpr static inline name same_payer{};
215
216template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
218{
219 typedef typename std::remove_reference<Type>::type result_type;
220
221 template<typename ChainedPtr>
222
223 auto operator()(const ChainedPtr& x)const -> std::enable_if_t<!std::is_convertible<const ChainedPtr&, const Class&>::value, Type>
224 {
225 return operator()(*x);
226 }
227
228 Type operator()(const Class& x)const
229 {
230 return (x.*PtrToMemberFunction)();
231 }
232
233 Type operator()(const std::reference_wrapper<const Class>& x)const
234 {
235 return operator()(x.get());
236 }
237
238 Type operator()(const std::reference_wrapper<Class>& x)const
239 {
240 return operator()(x.get());
241 }
242};
243
244#define WRAP_SECONDARY_SIMPLE_TYPE(IDX, TYPE)\
245template<>\
246struct secondary_index_db_functions<TYPE> {\
247 static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
248 static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
249 static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
250 static int32_t db_idx_end( uint64_t code, uint64_t scope, uint64_t table ) { return internal_use_do_not_use::db_##IDX##_end( code, scope, table ); } \
251 static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
252 return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, &secondary ); \
253 }\
254 static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
255 internal_use_do_not_use::db_##IDX##_update( iterator, payer, &secondary ); \
256 }\
257 static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
258 return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, &secondary, primary ); \
259 }\
260 static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
261 return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, &secondary, &primary ); \
262 }\
263 static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
264 return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, &secondary, &primary ); \
265 }\
266 static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
267 return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, &secondary, &primary ); \
268 }\
269};
270
271#define WRAP_SECONDARY_ARRAY_TYPE(IDX, TYPE)\
272template<>\
273struct secondary_index_db_functions<TYPE> {\
274 static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
275 static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
276 static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
277 static int32_t db_idx_end( uint64_t code, uint64_t scope, uint64_t table ) { return internal_use_do_not_use::db_##IDX##_end( code, scope, table ); } \
278 static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
279 return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, secondary.data(), TYPE::num_words() ); \
280 }\
281 static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
282 internal_use_do_not_use::db_##IDX##_update( iterator, payer, secondary.data(), TYPE::num_words() ); \
283 }\
284 static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
285 return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, secondary.data(), TYPE::num_words(), primary ); \
286 }\
287 static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
288 return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
289 }\
290 static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
291 return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
292 }\
293 static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
294 return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
295 }\
296};
297
298#define MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(TYPE)\
299template<>\
300struct secondary_key_traits<TYPE> {\
301 static_assert( std::numeric_limits<TYPE>::is_specialized, "TYPE does not have specialized numeric_limits" );\
302 static constexpr TYPE true_lowest() { return std::numeric_limits<TYPE>::lowest(); }\
303};
304
305namespace _multi_index_detail {
306
307 template<typename T>
309
310 template<typename T>
312
315
316 WRAP_SECONDARY_SIMPLE_TYPE(idx128, uint128_t)
318
319 WRAP_SECONDARY_SIMPLE_TYPE(idx_double, double)
320 template<>
321 struct secondary_key_traits<double> {
322 static constexpr double true_lowest() { return -std::numeric_limits<double>::infinity(); }
323 };
324
325 WRAP_SECONDARY_SIMPLE_TYPE(idx_long_double, long double)
326 template<>
327 struct secondary_key_traits<long double> {
328 static constexpr long double true_lowest() { return -std::numeric_limits<long double>::infinity(); }
329 };
330
332 template<>
335 };
336
337 template<typename PK>
338 inline uint64_t to_raw_key(PK pk) { return pk; }
339 inline uint64_t to_raw_key(eosio::name pk) { return pk.value; }
340
341}
342
375template<name::raw IndexName, typename Extractor>
377 enum constants { index_name = static_cast<uint64_t>(IndexName) };
378 typedef Extractor secondary_extractor_type;
379};
380
437template<name::raw TableName, typename T, typename... Indices>
439{
440 private:
441
442 static_assert( std::is_same_v<decltype(_multi_index_detail::to_raw_key(std::declval<T>().primary_key())), uint64_t>,
443 "Primary key must be uint64_t or name" );
444 static_assert( sizeof...(Indices) <= 16, "multi_index only supports a maximum of 16 secondary indices" );
445
446 constexpr static bool validate_table_name( name n ) {
447 // Limit table names to 12 characters so that the last character (4 bits) can be used to distinguish between the secondary indices.
448 return n.length() < 13; //(n & 0x000000000000000FULL) == 0;
449 }
450
451 constexpr static size_t max_stack_buffer_size = 512;
452
453 static_assert( validate_table_name( name(TableName) ), "multi_index does not support table names with a length greater than 12");
454
455 name _code;
456 uint64_t _scope;
457
458 mutable uint64_t _next_primary_key;
459
460 enum next_primary_key_tags : uint64_t {
461 no_available_primary_key = static_cast<uint64_t>(-2), // Must be the smallest uint64_t value compared to all other tags
462 unset_next_primary_key = static_cast<uint64_t>(-1)
463 };
464
465 struct item : public T
466 {
467 template<typename Constructor>
468 item( const multi_index* idx, Constructor&& c )
469 :__idx(idx){
470 c(*this);
471 }
472
473 const multi_index* __idx;
474 int32_t __primary_itr;
475 int32_t __iters[sizeof...(Indices)+(sizeof...(Indices)==0)];
476 };
477
478 struct item_ptr
479 {
480 item_ptr(std::unique_ptr<item>&& i, uint64_t pk, int32_t pitr)
481 : _item(std::move(i)), _primary_key(pk), _primary_itr(pitr) {}
482
483 std::unique_ptr<item> _item;
484 uint64_t _primary_key;
485 int32_t _primary_itr;
486 };
487
488 mutable std::vector<item_ptr> _items_vector;
489
490 template<name::raw IndexName, typename Extractor, uint64_t Number, bool IsConst>
491 struct index {
492 public:
493 typedef Extractor secondary_extractor_type;
494 typedef typename std::decay<decltype( Extractor()(nullptr) )>::type secondary_key_type;
495
496 constexpr static bool validate_index_name( eosio::name n ) {
497 return n.value != 0 && n != eosio::name("primary"); // Primary is a reserve index name.
498 }
499
500 static_assert( validate_index_name( name(IndexName) ), "invalid index name used in multi_index" );
501
502 enum constants {
503 table_name = static_cast<uint64_t>(TableName),
504 index_name = static_cast<uint64_t>(IndexName),
505 index_number = Number,
506 index_table_name = (static_cast<uint64_t>(TableName) & 0xFFFFFFFFFFFFFFF0ULL)
507 | (Number & 0x000000000000000FULL) // Assuming no more than 16 secondary indices are allowed
508 };
509
510 constexpr static uint64_t name() { return index_table_name; }
511 constexpr static uint64_t number() { return Number; }
512
513 struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T> {
514 public:
515 friend bool operator == ( const const_iterator& a, const const_iterator& b ) {
516 return a._item == b._item;
517 }
518 friend bool operator != ( const const_iterator& a, const const_iterator& b ) {
519 return a._item != b._item;
520 }
521
522 const T& operator*()const { return *static_cast<const T*>(_item); }
523 const T* operator->()const { return static_cast<const T*>(_item); }
524
526 const_iterator result(*this);
527 ++(*this);
528 return result;
529 }
530
532 const_iterator result(*this);
533 --(*this);
534 return result;
535 }
536
538 using namespace _multi_index_detail;
539
540 eosio::check( _item != nullptr, "cannot increment end iterator" );
541
542 if( _item->__iters[Number] == -1 ) {
543 secondary_key_type temp_secondary_key;
544 auto idxitr = secondary_index_db_functions<secondary_key_type>::db_idx_find_primary(_idx->get_code().value, _idx->get_scope(), _idx->name(), _item->primary_key(), temp_secondary_key);
545 auto& mi = const_cast<item&>( *_item );
546 mi.__iters[Number] = idxitr;
547 }
548
549 uint64_t next_pk = 0;
550 auto next_itr = secondary_index_db_functions<secondary_key_type>::db_idx_next( _item->__iters[Number], &next_pk );
551 if( next_itr < 0 ) {
552 _item = nullptr;
553 return *this;
554 }
555
556 const T& obj = *_idx->_multidx->find( next_pk );
557 auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
558 mi.__iters[Number] = next_itr;
559 _item = &mi;
560
561 return *this;
562 }
563
565 using namespace _multi_index_detail;
566
567 uint64_t prev_pk = 0;
568 int32_t prev_itr = -1;
569
570 if( !_item ) {
571 auto ei = secondary_index_db_functions<secondary_key_type>::db_idx_end(_idx->get_code().value, _idx->get_scope(), _idx->name());
572 eosio::check( ei != -1, "cannot decrement end iterator when the index is empty" );
573 prev_itr = secondary_index_db_functions<secondary_key_type>::db_idx_previous( ei , &prev_pk );
574 eosio::check( prev_itr >= 0, "cannot decrement end iterator when the index is empty" );
575 } else {
576 if( _item->__iters[Number] == -1 ) {
577 secondary_key_type temp_secondary_key;
578 auto idxitr = secondary_index_db_functions<secondary_key_type>::db_idx_find_primary(_idx->get_code().value, _idx->get_scope(), _idx->name(), _item->primary_key(), temp_secondary_key);
579 auto& mi = const_cast<item&>( *_item );
580 mi.__iters[Number] = idxitr;
581 }
582 prev_itr = secondary_index_db_functions<secondary_key_type>::db_idx_previous( _item->__iters[Number], &prev_pk );
583 eosio::check( prev_itr >= 0, "cannot decrement iterator at beginning of index" );
584 }
585
586 const T& obj = *_idx->_multidx->find( prev_pk );
587 auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
588 mi.__iters[Number] = prev_itr;
589 _item = &mi;
590
591 return *this;
592 }
593
594 const_iterator():_item(nullptr){}
595 private:
596 friend struct index;
597 const_iterator( const index* idx, const item* i = nullptr )
598 : _idx(idx), _item(i) {}
599
600 const index* _idx;
601 const item* _item;
602 };
603
604 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
605
606 const_iterator cbegin()const {
607 using namespace _multi_index_detail;
608 return lower_bound( secondary_key_traits<secondary_key_type>::true_lowest() );
609 }
610 const_iterator begin()const { return cbegin(); }
611
612 const_iterator cend()const { return const_iterator( this ); }
613 const_iterator end()const { return cend(); }
614
615 const_reverse_iterator crbegin()const { return std::make_reverse_iterator(cend()); }
616 const_reverse_iterator rbegin()const { return crbegin(); }
617
618 const_reverse_iterator crend()const { return std::make_reverse_iterator(cbegin()); }
619 const_reverse_iterator rend()const { return crend(); }
620
621 const_iterator find( secondary_key_type&& secondary )const {
622 return find( secondary );
623 }
624
625 const_iterator find( const secondary_key_type& secondary )const {
626 auto lb = lower_bound( secondary );
627 auto e = cend();
628 if( lb == e ) return e;
629
630 if( secondary != secondary_extractor_type()(*lb) )
631 return e;
632 return lb;
633 }
634
635 const_iterator require_find( secondary_key_type&& secondary, const char* error_msg = "unable to find secondary key" )const {
636 return require_find( secondary, error_msg );
637 }
638
639 const_iterator require_find( const secondary_key_type& secondary, const char* error_msg = "unable to find secondary key" )const {
640 auto lb = lower_bound( secondary );
641 eosio::check( lb != cend(), error_msg );
642 eosio::check( secondary == secondary_extractor_type()(*lb), error_msg );
643 return lb;
644 }
645
654 const T& get( secondary_key_type&& secondary, const char* error_msg = "unable to find secondary key" )const {
655 return get( secondary, error_msg );
656 }
657
666 const T& get( const secondary_key_type& secondary, const char* error_msg = "unable to find secondary key" )const {
667 auto result = find( secondary );
668 eosio::check( result != cend(), error_msg );
669 return *result;
670 }
671
672 const_iterator lower_bound( secondary_key_type&& secondary )const {
673 return lower_bound( secondary );
674 }
675 const_iterator lower_bound( const secondary_key_type& secondary )const {
676 using namespace _multi_index_detail;
677
678 uint64_t primary = 0;
679 secondary_key_type secondary_copy(secondary);
680 auto itr = secondary_index_db_functions<secondary_key_type>::db_idx_lowerbound( get_code().value, get_scope(), name(), secondary_copy, primary );
681 if( itr < 0 ) return cend();
682
683 const T& obj = *_multidx->find( primary );
684 auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
685 mi.__iters[Number] = itr;
686
687 return {this, &mi};
688 }
689
690 const_iterator upper_bound( secondary_key_type&& secondary )const {
691 return upper_bound( secondary );
692 }
693 const_iterator upper_bound( const secondary_key_type& secondary )const {
694 using namespace _multi_index_detail;
695
696 uint64_t primary = 0;
697 secondary_key_type secondary_copy(secondary);
698 auto itr = secondary_index_db_functions<secondary_key_type>::db_idx_upperbound( get_code().value, get_scope(), name(), secondary_copy, primary );
699 if( itr < 0 ) return cend();
700
701 const T& obj = *_multidx->find( primary );
702 auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
703 mi.__iters[Number] = itr;
704
705 return {this, &mi};
706 }
712 const_iterator iterator_to( const T& obj ) {
713 using namespace _multi_index_detail;
714
715 const auto& objitem = static_cast<const item&>(obj);
716 eosio::check( objitem.__idx == _multidx, "object passed to iterator_to is not in multi_index" );
717
718 if( objitem.__iters[Number] == -1 ) {
719 secondary_key_type temp_secondary_key;
720 auto idxitr = secondary_index_db_functions<secondary_key_type>::db_idx_find_primary(get_code().value, get_scope(), name(), objitem.primary_key(), temp_secondary_key);
721 auto& mi = const_cast<item&>( objitem );
722 mi.__iters[Number] = idxitr;
723 }
724
725 return {this, &objitem};
726 }
727
728 template<typename Lambda>
729 void modify( const_iterator itr, eosio::name payer, Lambda&& updater ) {
730 eosio::check( itr != cend(), "cannot pass end iterator to modify" );
731
732 _multidx->modify( *itr, payer, std::forward<Lambda&&>(updater) );
733 }
734
735 template<typename Lambda>
736 void modify( const T& obj, eosio::name payer, Lambda&& updater ) {
737 _multidx->modify( obj, payer, std::forward<Lambda&&>(updater) );
738 }
739
740 const_iterator erase( const_iterator itr ) {
741 eosio::check( itr != cend(), "cannot pass end iterator to erase" );
742
743 const auto& obj = *itr;
744 ++itr;
745
746 _multidx->erase(obj);
747
748 return itr;
749 }
750
751 eosio::name get_code()const { return _multidx->get_code(); }
752 uint64_t get_scope()const { return _multidx->get_scope(); }
753
754 static auto extract_secondary_key(const T& obj) { return secondary_extractor_type()(obj); }
755
756 // used only for type deduction
757 constexpr index() : _multidx(nullptr) { }
758 private:
759 friend class multi_index;
760
761 index( typename std::conditional<IsConst, const multi_index*, multi_index*>::type midx )
762 :_multidx(midx){}
763
764 typename std::conditional<IsConst, const multi_index*, multi_index*>::type _multidx;
765 };
766
767 template<uint64_t I>
768 struct intc { enum e{ value = I }; operator uint64_t()const{ return I; } };
769 enum index_cv { const_index = 0, mutable_index = 1 };
770
771 template<std::size_t Num, typename... Values>
772 class make_index_tuple {
773
774 template <std::size_t... Seq>
775 static constexpr auto get_type(std::index_sequence<Seq...>) {
776 return std::make_tuple(std::make_tuple(index<eosio::name::raw(static_cast<uint64_t>(Values::index_name)),
777 typename Values::secondary_extractor_type,
778 intc<Seq>::e::value, const_index>{},
779 index<eosio::name::raw(static_cast<uint64_t>(Values::index_name)),
780 typename Values::secondary_extractor_type,
781 intc<Seq>::e::value, mutable_index>{})...);
782 }
783 public:
784 using type = decltype( get_type(std::make_index_sequence<Num>{}) );
785 };
786
787 using indices_type = typename make_index_tuple<sizeof... (Indices), Indices...>::type;
788
789 class make_extractor_tuple {
790 template <typename Obj, typename IndicesType, std::size_t... Seq>
791 static constexpr auto extractor_tuple(IndicesType, const Obj& obj, std::index_sequence<Seq...>) {
792 return std::make_tuple(std::tuple_element_t<const_index, std::tuple_element_t<Seq, IndicesType>>::extract_secondary_key(obj)...);
793 }
794 public:
795 template <typename Obj, typename IndicesType>
796 static constexpr auto get_extractor_tuple(IndicesType, const Obj& obj) {
797 return extractor_tuple(IndicesType{}, obj, std::make_index_sequence<std::tuple_size_v<IndicesType>>{});
798 }
799 };
800
801 const item& load_object_by_primary_iterator( int32_t itr )const {
802 using namespace _multi_index_detail;
803
804 auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
805 return ptr._primary_itr == itr;
806 });
807 if( itr2 != _items_vector.rend() )
808 return *itr2->_item;
809
810 auto size = internal_use_do_not_use::db_get_i64( itr, nullptr, 0 );
811 eosio::check( size >= 0, "error reading iterator" );
812
813 //using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
814 void* buffer = max_stack_buffer_size < size_t(size) ? malloc(size_t(size)) : alloca(size_t(size));
815
816 internal_use_do_not_use::db_get_i64( itr, buffer, uint32_t(size) );
817
818 datastream<const char*> ds( (char*)buffer, uint32_t(size) );
819
820 auto itm = std::make_unique<item>( this, [&]( auto& i ) {
821 T& val = static_cast<T&>(i);
822 ds >> val;
823
824 i.__primary_itr = itr;
825 bluegrass::meta::for_each(indices_type{}, [&](auto idx){
826 typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
827 i.__iters[ index_type::number() ] = -1;
828 });
829 });
830
831 const item* ptr = itm.get();
832 auto pk = _multi_index_detail::to_raw_key(itm->primary_key());
833 auto pitr = itm->__primary_itr;
834
835 _items_vector.emplace_back( std::move(itm), pk, pitr );
836
837 if ( max_stack_buffer_size < size_t(size) ) {
838 free(buffer);
839 }
840
841 return *ptr;
842 }
843
844 public:
893 :_code(code),_scope(scope),_next_primary_key(unset_next_primary_key)
894 {}
895
915 name get_code()const { return _code; }
916
936 uint64_t get_scope()const { return _scope; }
937
938 struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T> {
939 friend bool operator == ( const const_iterator& a, const const_iterator& b ) {
940 return a._item == b._item;
941 }
942 friend bool operator != ( const const_iterator& a, const const_iterator& b ) {
943 return a._item != b._item;
944 }
945
946 const T& operator*()const { return *static_cast<const T*>(_item); }
947 const T* operator->()const { return static_cast<const T*>(_item); }
948
950 const_iterator result(*this);
951 ++(*this);
952 return result;
953 }
954
956 const_iterator result(*this);
957 --(*this);
958 return result;
959 }
960
962 eosio::check( _item != nullptr, "cannot increment end iterator" );
963
964 uint64_t next_pk;
965 auto next_itr = internal_use_do_not_use::db_next_i64( _item->__primary_itr, &next_pk );
966 if( next_itr < 0 )
967 _item = nullptr;
968 else
969 _item = &_multidx->load_object_by_primary_iterator( next_itr );
970 return *this;
971 }
973 uint64_t prev_pk;
974 int32_t prev_itr = -1;
975
976 if( !_item ) {
977 auto ei = internal_use_do_not_use::db_end_i64(_multidx->get_code().value, _multidx->get_scope(), static_cast<uint64_t>(TableName));
978 eosio::check( ei != -1, "cannot decrement end iterator when the table is empty" );
979 prev_itr = internal_use_do_not_use::db_previous_i64( ei , &prev_pk );
980 eosio::check( prev_itr >= 0, "cannot decrement end iterator when the table is empty" );
981 } else {
982 prev_itr = internal_use_do_not_use::db_previous_i64( _item->__primary_itr, &prev_pk );
983 eosio::check( prev_itr >= 0, "cannot decrement iterator at beginning of table" );
984 }
985
986 _item = &_multidx->load_object_by_primary_iterator( prev_itr );
987 return *this;
988 }
989
990 private:
991 const_iterator( const multi_index* mi, const item* i = nullptr )
992 :_multidx(mi),_item(i){}
993
994 const multi_index* _multidx;
995 const item* _item;
996 friend class multi_index;
997 };
998
999 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
1000
1024 return lower_bound(std::numeric_limits<uint64_t>::lowest());
1025 }
1026
1049 const_iterator begin()const { return cbegin(); }
1050
1073 const_iterator cend()const { return const_iterator( this ); }
1074
1097 const_iterator end()const { return cend(); }
1098
1132 const_reverse_iterator crbegin()const { return std::make_reverse_iterator(cend()); }
1133
1167 const_reverse_iterator rbegin()const { return crbegin(); }
1168
1203 const_reverse_iterator crend()const { return std::make_reverse_iterator(cbegin()); }
1204
1239 const_reverse_iterator rend()const { return crend(); }
1240
1280 template<typename PK>
1281 const_iterator lower_bound( PK primary )const {
1282 uint64_t primary_int = _multi_index_detail::to_raw_key(primary);
1283 auto itr = internal_use_do_not_use::db_lowerbound_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary_int );
1284 if( itr < 0 ) return end();
1285 const auto& obj = load_object_by_primary_iterator( itr );
1286 return {this, &obj};
1287 }
1288
1326 template<typename PK>
1327 const_iterator upper_bound( PK primary )const {
1328 uint64_t primary_int = _multi_index_detail::to_raw_key(primary);
1329 auto itr = internal_use_do_not_use::db_upperbound_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary_int );
1330 if( itr < 0 ) return end();
1331 const auto& obj = load_object_by_primary_iterator( itr );
1332 return {this, &obj};
1333 }
1334
1367 if( _next_primary_key == unset_next_primary_key ) {
1368 // This is the first time available_primary_key() is called for this multi_index instance.
1369 if( begin() == end() ) { // empty table
1370 _next_primary_key = 0;
1371 } else {
1372 auto itr = --end(); // last row of table sorted by primary key
1373 auto pk = itr->primary_key(); // largest primary key currently in table
1374 if( pk >= no_available_primary_key ) // Reserve the tags
1375 _next_primary_key = no_available_primary_key;
1376 else
1377 _next_primary_key = pk + 1;
1378 }
1379 }
1380
1381 eosio::check( _next_primary_key < no_available_primary_key, "next primary key in table is at autoincrement limit");
1382 return _next_primary_key;
1383 }
1384
1426 template<name::raw IndexName>
1427 auto get_index() {
1428 using namespace _multi_index_detail;
1429
1430 constexpr uint64_t index_num = bluegrass::meta::for_each(indices_type{}, [](auto idx){
1431 return std::tuple_element_t<const_index, decltype(idx)>::index_name == static_cast<uint64_t>(IndexName);
1432 });
1433
1434 static_assert( index_num < sizeof...(Indices), "name provided is not the name of any secondary index within multi_index" );
1435
1436 return std::tuple_element_t<const_index, std::tuple_element_t<index_num, indices_type>>(this);
1437 }
1438
1477 template<name::raw IndexName>
1478 auto get_index()const {
1479 using namespace _multi_index_detail;
1480
1481 constexpr uint64_t index_num = bluegrass::meta::for_each(indices_type{}, [](auto idx){
1482 return std::tuple_element_t<mutable_index, decltype(idx)>::index_name == static_cast<uint64_t>(IndexName);
1483 });
1484
1485 static_assert( index_num < sizeof...(Indices), "name provided is not the name of any secondary index within multi_index" );
1486
1487 return std::tuple_element_t<mutable_index, std::tuple_element_t<index_num, indices_type>>(this);
1488 }
1489
1529 const_iterator iterator_to( const T& obj )const {
1530 const auto& objitem = static_cast<const item&>(obj);
1531 eosio::check( objitem.__idx == this, "object passed to iterator_to is not in multi_index" );
1532 return {this, &objitem};
1533 }
1571 template<typename Lambda>
1572 const_iterator emplace( name payer, Lambda&& constructor ) {
1573 using namespace _multi_index_detail;
1574
1575 eosio::check( _code == current_receiver(), "cannot create objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
1576
1577 auto itm = std::make_unique<item>( this, [&]( auto& i ){
1578 T& obj = static_cast<T&>(i);
1579 constructor( obj );
1580
1581 size_t size = pack_size( obj );
1582
1583 //using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
1584 void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
1585
1586 datastream<char*> ds( (char*)buffer, size );
1587 ds << obj;
1588
1589 uint64_t pk = _multi_index_detail::to_raw_key(obj.primary_key());
1590
1591 i.__primary_itr = internal_use_do_not_use::db_store_i64( _scope, static_cast<uint64_t>(TableName), payer.value, pk, buffer, size );
1592
1593 if ( max_stack_buffer_size < size ) {
1594 free(buffer);
1595 }
1596
1597 if( pk >= _next_primary_key )
1598 _next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
1599
1600 bluegrass::meta::for_each(indices_type{}, [&](auto idx){
1601 typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
1602
1603 i.__iters[index_type::number()] = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_store( _scope, index_type::name(), payer.value, obj.primary_key(), index_type::extract_secondary_key(obj) );
1604 });
1605 });
1606
1607 const item* ptr = itm.get();
1608 auto pk = _multi_index_detail::to_raw_key(itm->primary_key());
1609 auto pitr = itm->__primary_itr;
1610
1611 _items_vector.emplace_back( std::move(itm), pk, pitr );
1612
1613 return {this, ptr};
1614 }
1615
1656 template<typename Lambda>
1657 void modify( const_iterator itr, name payer, Lambda&& updater ) {
1658 eosio::check( itr != end(), "cannot pass end iterator to modify" );
1659
1660 modify( *itr, payer, std::forward<Lambda&&>(updater) );
1661 }
1662
1704 template<typename Lambda>
1705 void modify( const T& obj, name payer, Lambda&& updater ) {
1706 using namespace _multi_index_detail;
1707
1708 const auto& objitem = static_cast<const item&>(obj);
1709 eosio::check( objitem.__idx == this, "object passed to modify is not in multi_index" );
1710 auto& mutableitem = const_cast<item&>(objitem);
1711 eosio::check( _code == current_receiver(), "cannot modify objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
1712
1713 auto secondary_keys = make_extractor_tuple::get_extractor_tuple(indices_type{}, obj);
1714
1715 uint64_t pk = _multi_index_detail::to_raw_key(obj.primary_key());
1716
1717 auto& mutableobj = const_cast<T&>(obj); // Do not forget the auto& otherwise it would make a copy and thus not update at all.
1718 updater( mutableobj );
1719
1720 eosio::check( pk == _multi_index_detail::to_raw_key(obj.primary_key()), "updater cannot change primary key when modifying an object" );
1721
1722 size_t size = pack_size( obj );
1723 //using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
1724 void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
1725
1726 datastream<char*> ds( (char*)buffer, size );
1727 ds << obj;
1728
1729 internal_use_do_not_use::db_update_i64( objitem.__primary_itr, payer.value, buffer, size );
1730
1731 if ( max_stack_buffer_size < size ) {
1732 free( buffer );
1733 }
1734
1735 if( pk >= _next_primary_key )
1736 _next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
1737
1738 bluegrass::meta::for_each(indices_type{}, [&](auto idx){
1739 typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
1740 auto secondary = index_type::extract_secondary_key( obj );
1741 if( memcmp( &std::get<index_type::index_number>(secondary_keys), &secondary, sizeof(secondary) ) != 0 ) {
1742 auto indexitr = mutableitem.__iters[index_type::number()];
1743
1744 if( indexitr < 0 ) {
1745 typename index_type::secondary_key_type temp_secondary_key;
1746 indexitr = mutableitem.__iters[index_type::number()]
1747 = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_find_primary( _code.value, _scope, index_type::name(), pk, temp_secondary_key );
1748 }
1749
1750 secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_update( indexitr, payer.value, secondary );
1751 }
1752 } );
1753 }
1754
1787 template<typename PK>
1788 const T& get( PK primary, const char* error_msg = "unable to find key" )const {
1789 auto result = find( primary );
1790 eosio::check( result != cend(), error_msg );
1791 return *result;
1792 }
1793
1817 template<typename PK>
1818 const_iterator find( PK primary )const {
1819 auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
1820 return ptr._item->primary_key() == primary;
1821 });
1822 if( itr2 != _items_vector.rend() )
1823 return iterator_to(*(itr2->_item));
1824
1825 uint64_t primary_int = _multi_index_detail::to_raw_key(primary);
1826 auto itr = internal_use_do_not_use::db_find_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary_int );
1827 if( itr < 0 ) return end();
1828
1829 const item& i = load_object_by_primary_iterator( itr );
1830 return iterator_to(static_cast<const T&>(i));
1831 }
1832
1842 template<typename PK>
1843 const_iterator require_find( PK primary, const char* error_msg = "unable to find key" )const {
1844 auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
1845 return ptr._item->primary_key() == primary;
1846 });
1847 if( itr2 != _items_vector.rend() )
1848 return iterator_to(*(itr2->_item));
1849
1850 uint64_t primary_int = _multi_index_detail::to_raw_key(primary);
1851 auto itr = internal_use_do_not_use::db_find_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary_int );
1852 eosio::check( itr >= 0, error_msg );
1853
1854 const item& i = load_object_by_primary_iterator( itr );
1855 return iterator_to(static_cast<const T&>(i));
1856 }
1857
1895 eosio::check( itr != end(), "cannot pass end iterator to erase" );
1896
1897 const auto& obj = *itr;
1898 ++itr;
1899
1900 erase(obj);
1901
1902 return itr;
1903 }
1904
1937 void erase( const T& obj ) {
1938 using namespace _multi_index_detail;
1939
1940 const auto& objitem = static_cast<const item&>(obj);
1941 eosio::check( objitem.__idx == this, "object passed to erase is not in multi_index" );
1942 eosio::check( _code == current_receiver(), "cannot erase objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
1943
1944 auto pk = objitem.primary_key();
1945 auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
1946 return ptr._item->primary_key() == pk;
1947 });
1948
1949 eosio::check( itr2 != _items_vector.rend(), "attempt to remove object that was not in multi_index" );
1950
1951 internal_use_do_not_use::db_remove_i64( objitem.__primary_itr );
1952
1953 bluegrass::meta::for_each(indices_type{}, [&](auto idx){
1954 typedef std::tuple_element_t<const_index, decltype(idx)> index_type;
1955
1956 auto i = objitem.__iters[index_type::number()];
1957 if( i < 0 ) {
1958 typename index_type::secondary_key_type secondary;
1959 i = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_find_primary( _code.value, _scope, index_type::name(), objitem.primary_key(), secondary );
1960 }
1961 if( i >= 0 )
1962 secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_remove( i );
1963 });
1964
1965 _items_vector.erase(--(itr2.base()));
1966 }
1967
1968};
1969}