libraries/eosiolib/contracts/eosio/multi_index.hpp
Namespaces
Name |
---|
eosio |
eosio::internal_use_do_not_use |
eosio::_multi_index_detail |
Classes
Name | |
---|---|
struct | eosio::const_mem_fun |
struct | eosio::_multi_index_detail::secondary_key_traits< double > |
struct | eosio::_multi_index_detail::secondary_key_traits< long double > |
struct | eosio::_multi_index_detail::secondary_key_traits< eosio::fixed_bytes< 32 > > |
struct | eosio::indexed_by |
class | eosio::multi_index Defines EOSIO Multi Index Table. |
struct | eosio::multi_index::index::const_iterator |
struct | eosio::multi_index::const_iterator |
Defines
Name | |
---|---|
WRAP_SECONDARY_SIMPLE_TYPE(IDX, TYPE) | |
WRAP_SECONDARY_ARRAY_TYPE(IDX, TYPE) | |
MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(TYPE) |
Detailed Description
Copyright: defined in eos/LICENSE
Macros Documentation
define WRAP_SECONDARY_SIMPLE_TYPE
#define WRAP_SECONDARY_SIMPLE_TYPE(
IDX,
TYPE
)
template<>\
struct secondary_index_db_functions<TYPE> {\
static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
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 ); } \
static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, &secondary ); \
}\
static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
internal_use_do_not_use::db_##IDX##_update( iterator, payer, &secondary ); \
}\
static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, &secondary, primary ); \
}\
static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, &secondary, &primary ); \
}\
static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, &secondary, &primary ); \
}\
static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, &secondary, &primary ); \
}\
};
define WRAP_SECONDARY_ARRAY_TYPE
#define WRAP_SECONDARY_ARRAY_TYPE(
IDX,
TYPE
)
template<>\
struct secondary_index_db_functions<TYPE> {\
static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
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 ); } \
static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, secondary.data(), TYPE::num_words() ); \
}\
static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
internal_use_do_not_use::db_##IDX##_update( iterator, payer, secondary.data(), TYPE::num_words() ); \
}\
static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, secondary.data(), TYPE::num_words(), primary ); \
}\
static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
};
define MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY
#define MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(
TYPE
)
template<>\
struct secondary_key_traits<TYPE> {\
static_assert( std::numeric_limits<TYPE>::is_specialized, "TYPE does not have specialized numeric_limits" );\
static constexpr TYPE true_lowest() { return std::numeric_limits<TYPE>::lowest(); }\
};
Source code
#pragma once
#include "../../contracts/eosio/action.hpp"
#include "../../core/eosio/name.hpp"
#include "../../core/eosio/serialize.hpp"
#include "../../core/eosio/fixed_bytes.hpp"
#include <vector>
#include <tuple>
#include <boost/hana.hpp>
#include <functional>
#include <utility>
#include <type_traits>
#include <iterator>
#include <limits>
#include <algorithm>
#include <memory>
namespace eosio {
namespace internal_use_do_not_use {
extern "C" {
__attribute__((eosio_wasm_import))
int32_t db_store_i64(uint64_t, uint64_t, uint64_t, uint64_t, const void*, uint32_t);
__attribute__((eosio_wasm_import))
void db_update_i64(int32_t, uint64_t, const void*, uint32_t);
__attribute__((eosio_wasm_import))
void db_remove_i64(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_get_i64(int32_t, const void*, uint32_t);
__attribute__((eosio_wasm_import))
int32_t db_next_i64(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_previous_i64(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_find_i64(uint64_t, uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_lowerbound_i64(uint64_t, uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_upperbound_i64(uint64_t, uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_end_i64(uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx64_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint64_t*);
__attribute__((eosio_wasm_import))
void db_idx64_update(int32_t, uint64_t, const uint64_t*);
__attribute__((eosio_wasm_import))
void db_idx64_remove(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_idx64_next(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx64_previous(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx64_find_primary(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx64_find_secondary(uint64_t, uint64_t, uint64_t, const uint64_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx64_lowerbound(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx64_upperbound(uint64_t, uint64_t, uint64_t, uint64_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx64_end(uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx128_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint128_t*);
__attribute__((eosio_wasm_import))
void db_idx128_update(int32_t, uint64_t, const uint128_t*);
__attribute__((eosio_wasm_import))
void db_idx128_remove(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_idx128_next(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx128_previous(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx128_find_primary(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx128_find_secondary(uint64_t, uint64_t, uint64_t, const uint128_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx128_lowerbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx128_upperbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx128_end(uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx256_store(uint64_t, uint64_t, uint64_t, uint64_t, const uint128_t*, uint32_t);
__attribute__((eosio_wasm_import))
void db_idx256_update(int32_t, uint64_t, const uint128_t*, uint32_t);
__attribute__((eosio_wasm_import))
void db_idx256_remove(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_idx256_next(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx256_previous(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx256_find_primary(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx256_find_secondary(uint64_t, uint64_t, uint64_t, const uint128_t*, uint32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx256_lowerbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx256_upperbound(uint64_t, uint64_t, uint64_t, uint128_t*, uint32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx256_end(uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_store(uint64_t, uint64_t, uint64_t, uint64_t, const double*);
__attribute__((eosio_wasm_import))
void db_idx_double_update(int32_t, uint64_t, const double*);
__attribute__((eosio_wasm_import))
void db_idx_double_remove(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_next(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_previous(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_find_primary(uint64_t, uint64_t, uint64_t, double*, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_find_secondary(uint64_t, uint64_t, uint64_t, const double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_lowerbound(uint64_t, uint64_t, uint64_t, double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_upperbound(uint64_t, uint64_t, uint64_t, double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_double_end(uint64_t, uint64_t, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_store(uint64_t, uint64_t, uint64_t, uint64_t, const long double*);
__attribute__((eosio_wasm_import))
void db_idx_long_double_update(int32_t, uint64_t, const long double*);
__attribute__((eosio_wasm_import))
void db_idx_long_double_remove(int32_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_next(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_previous(int32_t, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_find_primary(uint64_t, uint64_t, uint64_t, long double*, uint64_t);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_find_secondary(uint64_t, uint64_t, uint64_t, const long double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_lowerbound(uint64_t, uint64_t, uint64_t, long double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_upperbound(uint64_t, uint64_t, uint64_t, long double*, uint64_t*);
__attribute__((eosio_wasm_import))
int32_t db_idx_long_double_end(uint64_t, uint64_t, uint64_t);
}
};
constexpr static inline name same_payer{};
template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
struct const_mem_fun
{
typedef typename std::remove_reference<Type>::type result_type;
template<typename ChainedPtr>
auto operator()(const ChainedPtr& x)const -> std::enable_if_t<!std::is_convertible<const ChainedPtr&, const Class&>::value, Type>
{
return operator()(*x);
}
Type operator()(const Class& x)const
{
return (x.*PtrToMemberFunction)();
}
Type operator()(const std::reference_wrapper<const Class>& x)const
{
return operator()(x.get());
}
Type operator()(const std::reference_wrapper<Class>& x)const
{
return operator()(x.get());
}
};
#define WRAP_SECONDARY_SIMPLE_TYPE(IDX, TYPE)\
template<>\
struct secondary_index_db_functions<TYPE> {\
static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
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 ); } \
static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, &secondary ); \
}\
static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
internal_use_do_not_use::db_##IDX##_update( iterator, payer, &secondary ); \
}\
static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, &secondary, primary ); \
}\
static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, &secondary, &primary ); \
}\
static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, &secondary, &primary ); \
}\
static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, &secondary, &primary ); \
}\
};
#define WRAP_SECONDARY_ARRAY_TYPE(IDX, TYPE)\
template<>\
struct secondary_index_db_functions<TYPE> {\
static int32_t db_idx_next( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_next( iterator, primary ); } \
static int32_t db_idx_previous( int32_t iterator, uint64_t* primary ) { return internal_use_do_not_use::db_##IDX##_previous( iterator, primary ); } \
static void db_idx_remove( int32_t iterator ) { internal_use_do_not_use::db_##IDX##_remove( iterator ); } \
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 ); } \
static int32_t db_idx_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_store( scope, table, payer, id, secondary.data(), TYPE::num_words() ); \
}\
static void db_idx_update( int32_t iterator, uint64_t payer, const TYPE& secondary ) {\
internal_use_do_not_use::db_##IDX##_update( iterator, payer, secondary.data(), TYPE::num_words() ); \
}\
static int32_t db_idx_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t primary, TYPE& secondary ) {\
return internal_use_do_not_use::db_##IDX##_find_primary( code, scope, table, secondary.data(), TYPE::num_words(), primary ); \
}\
static int32_t db_idx_find_secondary( uint64_t code, uint64_t scope, uint64_t table, const TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_find_secondary( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
static int32_t db_idx_lowerbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_lowerbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
static int32_t db_idx_upperbound( uint64_t code, uint64_t scope, uint64_t table, TYPE& secondary, uint64_t& primary ) {\
return internal_use_do_not_use::db_##IDX##_upperbound( code, scope, table, secondary.data(), TYPE::num_words(), &primary ); \
}\
};
#define MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(TYPE)\
template<>\
struct secondary_key_traits<TYPE> {\
static_assert( std::numeric_limits<TYPE>::is_specialized, "TYPE does not have specialized numeric_limits" );\
static constexpr TYPE true_lowest() { return std::numeric_limits<TYPE>::lowest(); }\
};
namespace _multi_index_detail {
namespace hana = boost::hana;
template<typename T>
struct secondary_index_db_functions;
template<typename T>
struct secondary_key_traits;
WRAP_SECONDARY_SIMPLE_TYPE(idx64, uint64_t)
MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(uint64_t)
WRAP_SECONDARY_SIMPLE_TYPE(idx128, uint128_t)
MAKE_TRAITS_FOR_ARITHMETIC_SECONDARY_KEY(uint128_t)
WRAP_SECONDARY_SIMPLE_TYPE(idx_double, double)
template<>
struct secondary_key_traits<double> {
static constexpr double true_lowest() { return -std::numeric_limits<double>::infinity(); }
};
WRAP_SECONDARY_SIMPLE_TYPE(idx_long_double, long double)
template<>
struct secondary_key_traits<long double> {
static constexpr long double true_lowest() { return -std::numeric_limits<long double>::infinity(); }
};
WRAP_SECONDARY_ARRAY_TYPE(idx256, eosio::fixed_bytes<32>)
template<>
struct secondary_key_traits<eosio::fixed_bytes<32>> {
static constexpr eosio::fixed_bytes<32> true_lowest() { return eosio::fixed_bytes<32>(); }
};
}
template<name::raw IndexName, typename Extractor>
struct indexed_by {
enum constants { index_name = static_cast<uint64_t>(IndexName) };
typedef Extractor secondary_extractor_type;
};
template<name::raw TableName, typename T, typename... Indices>
class multi_index
{
private:
static_assert( sizeof...(Indices) <= 16, "multi_index only supports a maximum of 16 secondary indices" );
constexpr static bool validate_table_name( name n ) {
// Limit table names to 12 characters so that the last character (4 bits) can be used to distinguish between the secondary indices.
return n.length() < 13; //(n & 0x000000000000000FULL) == 0;
}
constexpr static size_t max_stack_buffer_size = 512;
static_assert( validate_table_name( name(TableName) ), "multi_index does not support table names with a length greater than 12");
name _code;
uint64_t _scope;
mutable uint64_t _next_primary_key;
enum next_primary_key_tags : uint64_t {
no_available_primary_key = static_cast<uint64_t>(-2), // Must be the smallest uint64_t value compared to all other tags
unset_next_primary_key = static_cast<uint64_t>(-1)
};
struct item : public T
{
template<typename Constructor>
item( const multi_index* idx, Constructor&& c )
:__idx(idx){
c(*this);
}
const multi_index* __idx;
int32_t __primary_itr;
int32_t __iters[sizeof...(Indices)+(sizeof...(Indices)==0)];
};
struct item_ptr
{
item_ptr(std::unique_ptr<item>&& i, uint64_t pk, int32_t pitr)
: _item(std::move(i)), _primary_key(pk), _primary_itr(pitr) {}
std::unique_ptr<item> _item;
uint64_t _primary_key;
int32_t _primary_itr;
};
mutable std::vector<item_ptr> _items_vector;
template<name::raw IndexName, typename Extractor, uint64_t Number, bool IsConst>
struct index {
public:
typedef Extractor secondary_extractor_type;
typedef typename std::decay<decltype( Extractor()(nullptr) )>::type secondary_key_type;
constexpr static bool validate_index_name( eosio::name n ) {
return n.value != 0 && n != eosio::name("primary"); // Primary is a reserve index name.
}
static_assert( validate_index_name( name(IndexName) ), "invalid index name used in multi_index" );
enum constants {
table_name = static_cast<uint64_t>(TableName),
index_name = static_cast<uint64_t>(IndexName),
index_number = Number,
index_table_name = (static_cast<uint64_t>(TableName) & 0xFFFFFFFFFFFFFFF0ULL)
| (Number & 0x000000000000000FULL) // Assuming no more than 16 secondary indices are allowed
};
constexpr static uint64_t name() { return index_table_name; }
constexpr static uint64_t number() { return Number; }
struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T> {
public:
friend bool operator == ( const const_iterator& a, const const_iterator& b ) {
return a._item == b._item;
}
friend bool operator != ( const const_iterator& a, const const_iterator& b ) {
return a._item != b._item;
}
const T& operator*()const { return *static_cast<const T*>(_item); }
const T* operator->()const { return static_cast<const T*>(_item); }
const_iterator operator++(int){
const_iterator result(*this);
++(*this);
return result;
}
const_iterator operator--(int){
const_iterator result(*this);
--(*this);
return result;
}
const_iterator& operator++() {
using namespace _multi_index_detail;
eosio::check( _item != nullptr, "cannot increment end iterator" );
if( _item->__iters[Number] == -1 ) {
secondary_key_type temp_secondary_key;
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);
auto& mi = const_cast<item&>( *_item );
mi.__iters[Number] = idxitr;
}
uint64_t next_pk = 0;
auto next_itr = secondary_index_db_functions<secondary_key_type>::db_idx_next( _item->__iters[Number], &next_pk );
if( next_itr < 0 ) {
_item = nullptr;
return *this;
}
const T& obj = *_idx->_multidx->find( next_pk );
auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
mi.__iters[Number] = next_itr;
_item = &mi;
return *this;
}
const_iterator& operator--() {
using namespace _multi_index_detail;
uint64_t prev_pk = 0;
int32_t prev_itr = -1;
if( !_item ) {
auto ei = secondary_index_db_functions<secondary_key_type>::db_idx_end(_idx->get_code().value, _idx->get_scope(), _idx->name());
eosio::check( ei != -1, "cannot decrement end iterator when the index is empty" );
prev_itr = secondary_index_db_functions<secondary_key_type>::db_idx_previous( ei , &prev_pk );
eosio::check( prev_itr >= 0, "cannot decrement end iterator when the index is empty" );
} else {
if( _item->__iters[Number] == -1 ) {
secondary_key_type temp_secondary_key;
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);
auto& mi = const_cast<item&>( *_item );
mi.__iters[Number] = idxitr;
}
prev_itr = secondary_index_db_functions<secondary_key_type>::db_idx_previous( _item->__iters[Number], &prev_pk );
eosio::check( prev_itr >= 0, "cannot decrement iterator at beginning of index" );
}
const T& obj = *_idx->_multidx->find( prev_pk );
auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
mi.__iters[Number] = prev_itr;
_item = &mi;
return *this;
}
const_iterator():_item(nullptr){}
private:
friend struct index;
const_iterator( const index* idx, const item* i = nullptr )
: _idx(idx), _item(i) {}
const index* _idx;
const item* _item;
};
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
const_iterator cbegin()const {
using namespace _multi_index_detail;
return lower_bound( secondary_key_traits<secondary_key_type>::true_lowest() );
}
const_iterator begin()const { return cbegin(); }
const_iterator cend()const { return const_iterator( this ); }
const_iterator end()const { return cend(); }
const_reverse_iterator crbegin()const { return std::make_reverse_iterator(cend()); }
const_reverse_iterator rbegin()const { return crbegin(); }
const_reverse_iterator crend()const { return std::make_reverse_iterator(cbegin()); }
const_reverse_iterator rend()const { return crend(); }
const_iterator find( secondary_key_type&& secondary )const {
return find( secondary );
}
const_iterator find( const secondary_key_type& secondary )const {
auto lb = lower_bound( secondary );
auto e = cend();
if( lb == e ) return e;
if( secondary != secondary_extractor_type()(*lb) )
return e;
return lb;
}
const_iterator require_find( secondary_key_type&& secondary, const char* error_msg = "unable to find secondary key" )const {
return require_find( secondary, error_msg );
}
const_iterator require_find( const secondary_key_type& secondary, const char* error_msg = "unable to find secondary key" )const {
auto lb = lower_bound( secondary );
eosio::check( lb != cend(), error_msg );
eosio::check( secondary == secondary_extractor_type()(*lb), error_msg );
return lb;
}
const T& get( secondary_key_type&& secondary, const char* error_msg = "unable to find secondary key" )const {
return get( secondary, error_msg );
}
const T& get( const secondary_key_type& secondary, const char* error_msg = "unable to find secondary key" )const {
auto result = find( secondary );
eosio::check( result != cend(), error_msg );
return *result;
}
const_iterator lower_bound( secondary_key_type&& secondary )const {
return lower_bound( secondary );
}
const_iterator lower_bound( const secondary_key_type& secondary )const {
using namespace _multi_index_detail;
uint64_t primary = 0;
secondary_key_type secondary_copy(secondary);
auto itr = secondary_index_db_functions<secondary_key_type>::db_idx_lowerbound( get_code().value, get_scope(), name(), secondary_copy, primary );
if( itr < 0 ) return cend();
const T& obj = *_multidx->find( primary );
auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
mi.__iters[Number] = itr;
return {this, &mi};
}
const_iterator upper_bound( secondary_key_type&& secondary )const {
return upper_bound( secondary );
}
const_iterator upper_bound( const secondary_key_type& secondary )const {
using namespace _multi_index_detail;
uint64_t primary = 0;
secondary_key_type secondary_copy(secondary);
auto itr = secondary_index_db_functions<secondary_key_type>::db_idx_upperbound( get_code().value, get_scope(), name(), secondary_copy, primary );
if( itr < 0 ) return cend();
const T& obj = *_multidx->find( primary );
auto& mi = const_cast<item&>( static_cast<const item&>(obj) );
mi.__iters[Number] = itr;
return {this, &mi};
}
const_iterator iterator_to( const T& obj ) {
using namespace _multi_index_detail;
const auto& objitem = static_cast<const item&>(obj);
eosio::check( objitem.__idx == _multidx, "object passed to iterator_to is not in multi_index" );
if( objitem.__iters[Number] == -1 ) {
secondary_key_type temp_secondary_key;
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);
auto& mi = const_cast<item&>( objitem );
mi.__iters[Number] = idxitr;
}
return {this, &objitem};
}
template<typename Lambda>
void modify( const_iterator itr, eosio::name payer, Lambda&& updater ) {
eosio::check( itr != cend(), "cannot pass end iterator to modify" );
_multidx->modify( *itr, payer, std::forward<Lambda&&>(updater) );
}
template<typename Lambda>
void modify( const T& obj, eosio::name payer, Lambda&& updater ) {
_multidx->modify( obj, payer, std::forward<Lambda&&>(updater) );
}
const_iterator erase( const_iterator itr ) {
eosio::check( itr != cend(), "cannot pass end iterator to erase" );
const auto& obj = *itr;
++itr;
_multidx->erase(obj);
return itr;
}
eosio::name get_code()const { return _multidx->get_code(); }
uint64_t get_scope()const { return _multidx->get_scope(); }
static auto extract_secondary_key(const T& obj) { return secondary_extractor_type()(obj); }
private:
friend class multi_index;
index( typename std::conditional<IsConst, const multi_index*, multi_index*>::type midx )
:_multidx(midx){}
typename std::conditional<IsConst, const multi_index*, multi_index*>::type _multidx;
};
template<uint64_t I>
struct intc { enum e{ value = I }; operator uint64_t()const{ return I; } };
static constexpr auto transform_indices( ) {
using namespace _multi_index_detail;
typedef decltype( hana::zip_shortest(
hana::make_tuple( intc<0>(), intc<1>(), intc<2>(), intc<3>(), intc<4>(), intc<5>(),
intc<6>(), intc<7>(), intc<8>(), intc<9>(), intc<10>(), intc<11>(),
intc<12>(), intc<13>(), intc<14>(), intc<15>() ),
hana::tuple<Indices...>() ) ) indices_input_type;
return hana::transform( indices_input_type(), [&]( auto&& idx ){
typedef typename std::decay<decltype(hana::at_c<0>(idx))>::type num_type;
typedef typename std::decay<decltype(hana::at_c<1>(idx))>::type idx_type;
return hana::make_tuple( hana::type_c<index<eosio::name::raw(static_cast<uint64_t>(idx_type::index_name)),
typename idx_type::secondary_extractor_type,
num_type::e::value, false> >,
hana::type_c<index<eosio::name::raw(static_cast<uint64_t>(idx_type::index_name)),
typename idx_type::secondary_extractor_type,
num_type::e::value, true> > );
});
}
typedef decltype( multi_index::transform_indices() ) indices_type;
indices_type _indices;
const item& load_object_by_primary_iterator( int32_t itr )const {
using namespace _multi_index_detail;
auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
return ptr._primary_itr == itr;
});
if( itr2 != _items_vector.rend() )
return *itr2->_item;
auto size = internal_use_do_not_use::db_get_i64( itr, nullptr, 0 );
eosio::check( size >= 0, "error reading iterator" );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size_t(size) ? malloc(size_t(size)) : alloca(size_t(size));
internal_use_do_not_use::db_get_i64( itr, buffer, uint32_t(size) );
datastream<const char*> ds( (char*)buffer, uint32_t(size) );
auto itm = std::make_unique<item>( this, [&]( auto& i ) {
T& val = static_cast<T&>(i);
ds >> val;
i.__primary_itr = itr;
hana::for_each( _indices, [&]( auto& idx ) {
typedef typename decltype(+hana::at_c<1>(idx))::type index_type;
i.__iters[ index_type::number() ] = -1;
});
});
const item* ptr = itm.get();
auto pk = itm->primary_key();
auto pitr = itm->__primary_itr;
_items_vector.emplace_back( std::move(itm), pk, pitr );
if ( max_stack_buffer_size < size_t(size) ) {
free(buffer);
}
return *ptr;
}
public:
multi_index( name code, uint64_t scope )
:_code(code),_scope(scope),_next_primary_key(unset_next_primary_key)
{}
name get_code()const { return _code; }
uint64_t get_scope()const { return _scope; }
struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T> {
friend bool operator == ( const const_iterator& a, const const_iterator& b ) {
return a._item == b._item;
}
friend bool operator != ( const const_iterator& a, const const_iterator& b ) {
return a._item != b._item;
}
const T& operator*()const { return *static_cast<const T*>(_item); }
const T* operator->()const { return static_cast<const T*>(_item); }
const_iterator operator++(int) {
const_iterator result(*this);
++(*this);
return result;
}
const_iterator operator--(int) {
const_iterator result(*this);
--(*this);
return result;
}
const_iterator& operator++() {
eosio::check( _item != nullptr, "cannot increment end iterator" );
uint64_t next_pk;
auto next_itr = internal_use_do_not_use::db_next_i64( _item->__primary_itr, &next_pk );
if( next_itr < 0 )
_item = nullptr;
else
_item = &_multidx->load_object_by_primary_iterator( next_itr );
return *this;
}
const_iterator& operator--() {
uint64_t prev_pk;
int32_t prev_itr = -1;
if( !_item ) {
auto ei = internal_use_do_not_use::db_end_i64(_multidx->get_code().value, _multidx->get_scope(), static_cast<uint64_t>(TableName));
eosio::check( ei != -1, "cannot decrement end iterator when the table is empty" );
prev_itr = internal_use_do_not_use::db_previous_i64( ei , &prev_pk );
eosio::check( prev_itr >= 0, "cannot decrement end iterator when the table is empty" );
} else {
prev_itr = internal_use_do_not_use::db_previous_i64( _item->__primary_itr, &prev_pk );
eosio::check( prev_itr >= 0, "cannot decrement iterator at beginning of table" );
}
_item = &_multidx->load_object_by_primary_iterator( prev_itr );
return *this;
}
private:
const_iterator( const multi_index* mi, const item* i = nullptr )
:_multidx(mi),_item(i){}
const multi_index* _multidx;
const item* _item;
friend class multi_index;
};
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
const_iterator cbegin()const {
return lower_bound(std::numeric_limits<uint64_t>::lowest());
}
const_iterator begin()const { return cbegin(); }
const_iterator cend()const { return const_iterator( this ); }
const_iterator end()const { return cend(); }
const_reverse_iterator crbegin()const { return std::make_reverse_iterator(cend()); }
const_reverse_iterator rbegin()const { return crbegin(); }
const_reverse_iterator crend()const { return std::make_reverse_iterator(cbegin()); }
const_reverse_iterator rend()const { return crend(); }
const_iterator lower_bound( uint64_t primary )const {
auto itr = internal_use_do_not_use::db_lowerbound_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary );
if( itr < 0 ) return end();
const auto& obj = load_object_by_primary_iterator( itr );
return {this, &obj};
}
const_iterator upper_bound( uint64_t primary )const {
auto itr = internal_use_do_not_use::db_upperbound_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary );
if( itr < 0 ) return end();
const auto& obj = load_object_by_primary_iterator( itr );
return {this, &obj};
}
uint64_t available_primary_key()const {
if( _next_primary_key == unset_next_primary_key ) {
// This is the first time available_primary_key() is called for this multi_index instance.
if( begin() == end() ) { // empty table
_next_primary_key = 0;
} else {
auto itr = --end(); // last row of table sorted by primary key
auto pk = itr->primary_key(); // largest primary key currently in table
if( pk >= no_available_primary_key ) // Reserve the tags
_next_primary_key = no_available_primary_key;
else
_next_primary_key = pk + 1;
}
}
eosio::check( _next_primary_key < no_available_primary_key, "next primary key in table is at autoincrement limit");
return _next_primary_key;
}
template<name::raw IndexName>
auto get_index() {
using namespace _multi_index_detail;
auto res = hana::find_if( _indices, []( auto&& in ) {
return std::integral_constant<bool, static_cast<uint64_t>(std::decay<typename decltype(+hana::at_c<0>(in))::type>::type::index_name) == static_cast<uint64_t>(IndexName)>();
});
static_assert( res != hana::nothing, "name provided is not the name of any secondary index within multi_index" );
return typename decltype(+hana::at_c<0>(res.value()))::type(this);
}
template<name::raw IndexName>
auto get_index()const {
using namespace _multi_index_detail;
auto res = hana::find_if( _indices, []( auto&& in ) {
return std::integral_constant<bool, static_cast<uint64_t>(std::decay<typename decltype(+hana::at_c<1>(in))::type>::type::index_name) == static_cast<uint64_t>(IndexName)>();
});
static_assert( res != hana::nothing, "name provided is not the name of any secondary index within multi_index" );
return typename decltype(+hana::at_c<1>(res.value()))::type(this);
}
const_iterator iterator_to( const T& obj )const {
const auto& objitem = static_cast<const item&>(obj);
eosio::check( objitem.__idx == this, "object passed to iterator_to is not in multi_index" );
return {this, &objitem};
}
template<typename Lambda>
const_iterator emplace( name payer, Lambda&& constructor ) {
using namespace _multi_index_detail;
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.
auto itm = std::make_unique<item>( this, [&]( auto& i ){
T& obj = static_cast<T&>(i);
constructor( obj );
size_t size = pack_size( obj );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
datastream<char*> ds( (char*)buffer, size );
ds << obj;
auto pk = obj.primary_key();
i.__primary_itr = internal_use_do_not_use::db_store_i64( _scope, static_cast<uint64_t>(TableName), payer.value, pk, buffer, size );
if ( max_stack_buffer_size < size ) {
free(buffer);
}
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
hana::for_each( _indices, [&]( auto& idx ) {
typedef typename decltype(+hana::at_c<0>(idx))::type index_type;
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) );
});
});
const item* ptr = itm.get();
auto pk = itm->primary_key();
auto pitr = itm->__primary_itr;
_items_vector.emplace_back( std::move(itm), pk, pitr );
return {this, ptr};
}
template<typename Lambda>
void modify( const_iterator itr, name payer, Lambda&& updater ) {
eosio::check( itr != end(), "cannot pass end iterator to modify" );
modify( *itr, payer, std::forward<Lambda&&>(updater) );
}
template<typename Lambda>
void modify( const T& obj, name payer, Lambda&& updater ) {
using namespace _multi_index_detail;
const auto& objitem = static_cast<const item&>(obj);
eosio::check( objitem.__idx == this, "object passed to modify is not in multi_index" );
auto& mutableitem = const_cast<item&>(objitem);
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.
auto secondary_keys = hana::transform( _indices, [&]( auto&& idx ) {
typedef typename decltype(+hana::at_c<0>(idx))::type index_type;
return index_type::extract_secondary_key( obj );
});
auto pk = obj.primary_key();
auto& mutableobj = const_cast<T&>(obj); // Do not forget the auto& otherwise it would make a copy and thus not update at all.
updater( mutableobj );
eosio::check( pk == obj.primary_key(), "updater cannot change primary key when modifying an object" );
size_t size = pack_size( obj );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
datastream<char*> ds( (char*)buffer, size );
ds << obj;
internal_use_do_not_use::db_update_i64( objitem.__primary_itr, payer.value, buffer, size );
if ( max_stack_buffer_size < size ) {
free( buffer );
}
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
hana::for_each( _indices, [&]( auto& idx ) {
typedef typename decltype(+hana::at_c<0>(idx))::type index_type;
auto secondary = index_type::extract_secondary_key( obj );
if( memcmp( &hana::at_c<index_type::index_number>(secondary_keys), &secondary, sizeof(secondary) ) != 0 ) {
auto indexitr = mutableitem.__iters[index_type::number()];
if( indexitr < 0 ) {
typename index_type::secondary_key_type temp_secondary_key;
indexitr = mutableitem.__iters[index_type::number()]
= secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_find_primary( _code.value, _scope, index_type::name(), pk, temp_secondary_key );
}
secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_update( indexitr, payer.value, secondary );
}
});
}
const T& get( uint64_t primary, const char* error_msg = "unable to find key" )const {
auto result = find( primary );
eosio::check( result != cend(), error_msg );
return *result;
}
const_iterator find( uint64_t primary )const {
auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
return ptr._item->primary_key() == primary;
});
if( itr2 != _items_vector.rend() )
return iterator_to(*(itr2->_item));
auto itr = internal_use_do_not_use::db_find_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary );
if( itr < 0 ) return end();
const item& i = load_object_by_primary_iterator( itr );
return iterator_to(static_cast<const T&>(i));
}
const_iterator require_find( uint64_t primary, const char* error_msg = "unable to find key" )const {
auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
return ptr._item->primary_key() == primary;
});
if( itr2 != _items_vector.rend() )
return iterator_to(*(itr2->_item));
auto itr = internal_use_do_not_use::db_find_i64( _code.value, _scope, static_cast<uint64_t>(TableName), primary );
eosio::check( itr >= 0, error_msg );
const item& i = load_object_by_primary_iterator( itr );
return iterator_to(static_cast<const T&>(i));
}
const_iterator erase( const_iterator itr ) {
eosio::check( itr != end(), "cannot pass end iterator to erase" );
const auto& obj = *itr;
++itr;
erase(obj);
return itr;
}
void erase( const T& obj ) {
using namespace _multi_index_detail;
const auto& objitem = static_cast<const item&>(obj);
eosio::check( objitem.__idx == this, "object passed to erase is not in multi_index" );
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.
auto pk = objitem.primary_key();
auto itr2 = std::find_if(_items_vector.rbegin(), _items_vector.rend(), [&](const item_ptr& ptr) {
return ptr._item->primary_key() == pk;
});
eosio::check( itr2 != _items_vector.rend(), "attempt to remove object that was not in multi_index" );
internal_use_do_not_use::db_remove_i64( objitem.__primary_itr );
hana::for_each( _indices, [&]( auto& idx ) {
typedef typename decltype(+hana::at_c<0>(idx))::type index_type;
auto i = objitem.__iters[index_type::number()];
if( i < 0 ) {
typename index_type::secondary_key_type secondary;
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 );
}
if( i >= 0 )
secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_remove( i );
});
_items_vector.erase(--(itr2.base()));
}
};
}
Updated on 2022-12-05 at 15:38:08 +0000