未验证 提交 fee30e1c 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #1522 from EOSIO/updated-multi_index

Updated multi_index and new C DB API
......@@ -55,6 +55,7 @@ tests/chain_bench
tests/chain_test
tests/intense_test
tests/performance_test
tests/tests/config.hpp
doxygen
......
......@@ -22,6 +22,7 @@ add_subdirectory(proxy)
add_subdirectory(test_api)
add_subdirectory(test_api_mem)
add_subdirectory(test_api_db)
add_subdirectory(test_api_multi_index)
add_subdirectory(simpledb)
#add_subdirectory(storage)
#add_subdirectory(social)
......
......@@ -19,14 +19,14 @@ class datastream {
public:
datastream( T start, size_t s )
:_start(start),_pos(start),_end(start+s){}
/**
* Skips a specified number of bytes from this stream
* @brief Skips a specific number of bytes from this stream
* @param s The number of bytes to skip
*/
inline void skip( size_t s ){ _pos += s; }
/**
* Reads a specified number of bytes from the stream into a buffer
* @brief Reads a specified number of bytes from this stream into a buffer
......@@ -52,30 +52,30 @@ class datastream {
_pos += s;
return true;
}
/**
* Writes a byte into the stream
* @brief Writes a byte into the stream
* @param c byte to write
*/
inline bool put(char c) {
inline bool put(char c) {
eosio_assert( _pos < _end, "put" );
*_pos = c;
++_pos;
*_pos = c;
++_pos;
return true;
}
/**
* Reads a byte from the stream
* @brief Reads a byte from the stream
* @param c reference to destination byte
*/
inline bool get( unsigned char& c ) { return get( *(char*)&c ); }
inline bool get( char& c )
inline bool get( char& c )
{
eosio_assert( _pos < _end, "get" );
c = *_pos;
++_pos;
++_pos;
return true;
}
......@@ -86,7 +86,7 @@ class datastream {
*/
T pos()const { return _pos; }
inline bool valid()const { return _pos <= _end && _pos >= _start; }
/**
* Sets the position within the current stream
* @brief Sets the position within the current stream
......@@ -100,7 +100,7 @@ class datastream {
* @return p the position within the current stream
*/
inline size_t tellp()const { return size_t(_pos - _start); }
/**
* Returns the number of remaining bytes that can be read/skipped
* @brief Returns the number of remaining bytes that can be read/skipped
......@@ -132,25 +132,25 @@ class datastream<size_t> {
};
/**
* Serialize a uint256 into a stream
* @brief Serialize a uint256
* Serialize a key256 into a stream
* @brief Serialize a key256
* @param ds stream to write
* @param d value to serialize
*/
template<typename Stream>
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const uint256 d) {
ds.write( (const char*)&d, sizeof(d) );
inline datastream<Stream>& operator<<(datastream<Stream>& ds, const key256 d) {
ds.write( (const char*)d.data(), d.size() );
return ds;
}
/**
* Deserialize a uint256 from a stream
* @brief Deserialize a uint256
* Deserialize a key256 from a stream
* @brief Deserialize a key256
* @param ds stream to read
* @param d destination for deserialized value
*/
template<typename Stream>
inline datastream<Stream>& operator>>(datastream<Stream>& ds, uint256& d) {
ds.read((char*)&d, sizeof(d) );
inline datastream<Stream>& operator>>(datastream<Stream>& ds, key256& d) {
ds.read((char*)d.data(), d.size() );
return ds;
}
......@@ -430,7 +430,7 @@ T unpack( const char* buffer, size_t len ) {
template<typename T>
size_t pack_size( const T& value ) {
datastream<size_t> ps;
datastream<size_t> ps;
ps << value;
return ps.tellp();
}
......@@ -446,4 +446,3 @@ bytes pack( const T& value ) {
}
}
......@@ -15,23 +15,23 @@ extern "C" {
* EOS.IO organizes data according to the following broad structure:
*
* - **scope** - an account where the data is stored
* - **code** - the account name which has write permission
* - **code** - the account name which has write permission
* - **table** - a name for the table that is being stored
* - **record** - a row in the table
*
* Every transaction specifies the set of valid scopes that may be read and/or written
* to. The code that is running determines what can be written to; therefore, write operations
* do not allow you to specify/configure the code.
* do not allow you to specify/configure the code.
*
* @note Attempts to read and/or write outside the valid scope and/or code sections will
* @note Attempts to read and/or write outside the valid scope and/or code sections will
* cause your transaction to fail.
*
*
* @section tabletypes table Types
* There are several supported table types identified by the number and
* size of the index.
* size of the index.
*
* 1. @ref dbi64
* 1. @ref dbi64
* 2. @ref dbi128i128
*
* The database APIs assume that the first bytes of each record represent
......@@ -264,7 +264,7 @@ int32_t remove_i64( account_name scope, table_name table, void* data );
*
*/
int32_t store_str( account_name scope, table_name table, account_name bta, char* key, uint32_t keylen, char* value, uint32_t valuelen );
/**
* @param scope - the account scope that will be read, must exist in the transaction scopes list
* @param table - the ID/name of the table within the current scope/code context to modify
......@@ -286,7 +286,7 @@ int32_t store_str( account_name scope, table_name table, account_name bta, char*
*
*/
int32_t update_str( account_name scope, table_name table, account_name bta, char* key, uint32_t keylen, char* value, uint32_t valuelen );
/**
* @param scope - the account scope that will be read, must exist in the transaction scopes list
* @param code - identifies the code that controls write-access to the data
......@@ -294,7 +294,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @param key - location of the record key
* @param keylen - length of the record key
* @param value - location to copy the record value
* @param valuelen - maximum length of the record value to read
* @param valuelen - maximum length of the record value to read
*
* @return the number of bytes read or -1 if key was not found
*/
......@@ -305,7 +305,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @param code - identifies the code that controls write-access to the data
* @param table - the ID/name of the table within the scope/code context to query
* @param value - location to copy the front record value
* @param valuelen - maximum length of the record value to read
* @param valuelen - maximum length of the record value to read
* @return the number of bytes read or -1 if key was not found
*/
int32_t front_str( account_name code, account_name scope, table_name table, char* value, uint32_t valuelen );
......@@ -315,7 +315,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @param code - identifies the code that controls write-access to the data
* @param table - the ID/name of the table within the scope/code context to query
* @param value - location to copy the back record value
* @param valuelen - maximum length of the record value to read
* @param valuelen - maximum length of the record value to read
* @return the number of bytes read or -1 if key was not found
*/
int32_t back_str( account_name code, account_name scope, table_name table, char* value, uint32_t valuelen );
......@@ -327,7 +327,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @param key - location of the record key
* @param keylen - length of the record key
* @param value - location to copy the next record value
* @param valuelen - maximum length of the record value to read
* @param valuelen - maximum length of the record value to read
* @return the number of bytes read or -1 if key was not found
*/
int32_t next_str( account_name code, account_name scope, table_name table, char* key, uint32_t keylen, char* value, uint32_t valuelen );
......@@ -339,7 +339,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @param key - location of the record key
* @param keylen - length of the record key
* @param value - location to copy the previous record value
* @param valuelen - maximum length of the record value to read
* @param valuelen - maximum length of the record value to read
* @return the number of bytes read or -1 if key was not found
*/
int32_t previous_str( account_name code, account_name scope, table_name table, char* key, uint32_t keylen, char* value, uint32_t valuelen );
......@@ -367,7 +367,7 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @return the number of bytes read or -1 if key was not found
*/
int32_t upper_bound_str( account_name code, account_name scope, table_name table, char* key, uint32_t keylen, char* value, uint32_t valuelen );
/**
* @param key - location of the record key
* @param keylen - length of the record key
......@@ -375,22 +375,22 @@ int32_t update_str( account_name scope, table_name table, account_name bta, char
* @return 1 if a record was removed, and 0 if no record with key was found
*/
int32_t remove_str( account_name scope, table_name table, char* key, uint32_t keylen );
///@} dbstr
/**
* @defgroup dbi128i128 Dual 128 bit Index table
* @brief Interface to a database table with 128 bit primary and secondary keys and arbitary binary data value.
* @ingroup databaseC
* @ingroup databaseC
*
* @param scope - the account where table data will be found
* @param code - the code which owns the table
* @param table - the name of the table where record is stored
* @param data - a pointer to memory that is at least 32 bytes long
* @param data - a pointer to memory that is at least 32 bytes long
* @param len - the length of data, must be greater than or equal to 32 bytes
*
* @return the total number of bytes read or -1 for "not found" or "end" where bytes
* read includes 32 bytes of the key
* @return the total number of bytes read or -1 for "not found" or "end" where bytes
* read includes 32 bytes of the key
*
* These methods assume a database table with records of the form:
*
......@@ -671,16 +671,16 @@ int32_t update_i128i128( account_name scope, table_name table, account_name bta,
/**
* @defgroup dbi64i64i64 Triple 64 bit Index table
* @brief Interface to a database table with 64 bit primary, secondary and tertiary keys and arbitrary binary data value.
* @ingroup databaseC
* @ingroup databaseC
*
* @param scope - the account where table data will be found
* @param code - the code which owns the table
* @param table - the name of the table where record is stored
* @param data - a pointer to memory that is at least 32 bytes long
* @param data - a pointer to memory that is at least 32 bytes long
* @param len - the length of data, must be greater than or equal to 32 bytes
*
* @return the total number of bytes read or -1 for "not found" or "end" where bytes
* read includes 24 bytes of the key
* @return the total number of bytes read or -1 for "not found" or "end" where bytes
* read includes 24 bytes of the key
*
* These methods assume a database table with records of the form:
*
......@@ -1023,7 +1023,7 @@ int32_t store_i64i64i64( account_name scope, table_name table, account_name bta,
* @return 1 if the record was updated, 0 if no record with key was found
*/
int32_t update_i64i64i64( account_name scope, table_name table, account_name bta, const void* data, uint32_t len );
///@} dbi64i64i64
///@} dbi64i64i64
int32_t db_store_i64(account_name scope, table_name table, account_name payer, uint64_t id, const void* data, uint32_t len);
void db_update_i64(int32_t iterator, account_name payer, const void* data, uint32_t len);
......@@ -1032,8 +1032,9 @@ int32_t db_get_i64(int32_t iterator, const void* data, uint32_t len);
int32_t db_next_i64(int32_t iterator, uint64_t* primary);
int32_t db_previous_i64(int32_t iterator, uint64_t* primary);
int32_t db_find_i64(account_name code, account_name scope, table_name table, uint64_t id);
int32_t db_lowerbound_i64( account_name code, account_name scope, table_name table, uint64_t id);
int32_t db_upperbound_i64( account_name code, account_name scope, table_name table, uint64_t id);
int32_t db_lowerbound_i64(account_name code, account_name scope, table_name table, uint64_t id);
int32_t db_upperbound_i64(account_name code, account_name scope, table_name table, uint64_t id);
int32_t db_end_i64(account_name code, account_name scope, table_name table);
int32_t db_idx64_store(account_name scope, table_name table, account_name payer, uint64_t id, const uint64_t* secondary);
void db_idx64_update(int32_t iterator, account_name payer, const uint64_t* secondary);
......@@ -1044,6 +1045,7 @@ int32_t db_idx64_find_primary(account_name code, account_name scope, table_name
int32_t db_idx64_find_secondary(account_name code, account_name scope, table_name table, const uint64_t* secondary, uint64_t* primary);
int32_t db_idx64_lowerbound(account_name code, account_name scope, table_name table, uint64_t* secondary, uint64_t* primary);
int32_t db_idx64_upperbound(account_name code, account_name scope, table_name table, uint64_t* secondary, uint64_t* primary);
int32_t db_idx64_end(account_name code, account_name scope, table_name table);
int32_t db_idx128_store(account_name scope, table_name table, account_name payer, uint64_t id, const uint128_t* secondary);
void db_idx128_update(int32_t iterator, account_name payer, const uint128_t* secondary);
......@@ -1054,5 +1056,17 @@ int32_t db_idx128_find_primary(account_name code, account_name scope, table_name
int32_t db_idx128_find_secondary(account_name code, account_name scope, table_name table, const uint128_t* secondary, uint64_t* primary);
int32_t db_idx128_lowerbound(account_name code, account_name scope, table_name table, uint128_t* secondary, uint64_t* primary);
int32_t db_idx128_upperbound(account_name code, account_name scope, table_name table, uint128_t* secondary, uint64_t* primary);
int32_t db_idx128_end(account_name code, account_name scope, table_name table);
int32_t db_idx256_store(account_name scope, table_name table, account_name payer, uint64_t id, const void* data, uint32_t data_len );
void db_idx256_update(int32_t iterator, account_name payer, const void* data, uint32_t data_len);
void db_idx256_remove(int32_t iterator);
int32_t db_idx256_next(int32_t iterator, uint64_t* primary);
int32_t db_idx256_previous(int32_t iterator, uint64_t* primary);
int32_t db_idx256_find_primary(account_name code, account_name scope, table_name table, void* data, uint32_t data_len, uint64_t primary);
int32_t db_idx256_find_secondary(account_name code, account_name scope, table_name table, const void* data, uint32_t data_len, uint64_t* primary);
int32_t db_idx256_lowerbound(account_name code, account_name scope, table_name table, void* data, uint32_t data_len, uint64_t* primary);
int32_t db_idx256_upperbound(account_name code, account_name scope, table_name table, void* data, uint32_t data_len, uint64_t* primary);
int32_t db_idx256_end(account_name code, account_name scope, table_name table);
}
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <array>
#include <algorithm>
#include <type_traits>
#include <eosiolib/system.h>
namespace eosio {
template<size_t Size>
class fixed_key;
template<size_t Size>
bool operator==(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator!=(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator<(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
/**
* @defgroup fixed_key fixed size key sorted lexicographically
* @ingroup types
* @{
*/
template<size_t Size>
class fixed_key {
private:
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same< bool_pack<bs..., true>, bool_pack<true, bs...> >;
public:
typedef uint128_t word_t;
static constexpr size_t num_words() { return (Size + sizeof(word_t) - 1) / sizeof(word_t); }
static constexpr size_t padded_bytes() { return num_words() * sizeof(word_t) - Size; }
/**
* @brief Default constructor to fixed_key object
*
* @details Default constructor to fixed_key object which initializes all bytes to zero
*/
fixed_key() : _data() {}
/**
* @brief Constructor to fixed_key object from array of num_words() words
*
* @details Constructor to fixed_key object from array of num_words() words
* @param arr data
*/
fixed_key(const word_t (&arr)[num_words()])
{
std::copy(arr, arr + num_words(), _data.begin());
}
/**
* @brief Constructor to fixed_key object from std::array of num_words() words
*
* @details Constructor to fixed_key object from std::array of num_words() words
* @param arr data
*/
fixed_key(const std::array<word_t, num_words()>& arr)
{
std::copy(arr.begin(), arr.end(), _data.begin());
}
template<typename FirstWord, typename... Rest>
static
fixed_key<Size>
make_from_word_sequence(typename std::enable_if<std::is_integral<FirstWord>::value &&
!std::is_same<FirstWord, bool>::value &&
sizeof(FirstWord) <= sizeof(word_t) &&
all_true<(std::is_same<FirstWord, Rest>::value)...>::value,
FirstWord>::type first_word,
Rest... rest)
{
static_assert( sizeof(word_t) == (sizeof(word_t)/sizeof(FirstWord)) * sizeof(FirstWord),
"size of the backing word size is not divisble by the size of the words supplied as arguments" );
static_assert( sizeof(FirstWord) * (1 + sizeof...(Rest)) <= Size, "too many words supplied to fixed_key constructor" );
fixed_key<Size> key;
auto itr = key._data.begin();
word_t temp_word = 0;
const size_t sub_word_shift = 8 * sizeof(FirstWord);
const size_t num_sub_words = sizeof(word_t) / sizeof(FirstWord);
auto sub_words_left = num_sub_words;
for( auto&& w : { first_word, rest... } ) {
if( sub_words_left > 1 ) {
temp_word |= static_cast<word_t>(w);
temp_word <<= sub_word_shift;
--sub_words_left;
continue;
}
eosio_assert( sub_words_left == 1, "unexpected error in fixed_key constructor" );
temp_word |= static_cast<word_t>(w);
sub_words_left = num_sub_words;
*itr = temp_word;
temp_word = 0;
++itr;
}
if( sub_words_left != num_sub_words ) {
if( sub_words_left > 1 )
temp_word <<= 8 * (sub_words_left-1);
*itr = temp_word;
}
return key;
}
const auto& get_array()const { return _data; }
auto data() { return _data.data(); }
auto data()const { return _data.data(); }
auto size()const { return _data.size(); }
std::array<uint8_t, Size> extract_as_byte_array()const {
std::array<uint8_t, Size> arr;
const size_t num_sub_words = sizeof(word_t);
auto arr_itr = arr.begin();
auto data_itr = _data.begin();
for( size_t counter = _data.size(); counter > 0; --counter, ++data_itr ) {
size_t sub_words_left = num_sub_words;
if( counter == 1 ) { // If last word in _data array...
sub_words_left -= padded_bytes();
}
auto temp_word = *data_itr;
for( ; sub_words_left > 0; --sub_words_left ) {
*(arr_itr + sub_words_left - 1) = static_cast<uint8_t>(temp_word & 0xFF);
temp_word >>= 8;
}
arr_itr += num_sub_words;
}
return arr;
}
// Comparison operators
friend bool operator== <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator!= <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator> <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator< <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
private:
std::array<word_t, num_words()> _data;
};
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 == c2, return true, otherwise false
*/
template<size_t Size>
bool operator==(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data == c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 != c2, return true, otherwise false
*/
template<size_t Size>
bool operator!=(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data != c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 > c2, return true, otherwise false
*/
template<size_t Size>
bool operator>(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data > c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 < c2, return true, otherwise false
*/
template<size_t Size>
bool operator<(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data < c2._data;
}
/// @} fixed_key
typedef fixed_key<32> key256;
}
#pragma once
#include <eosiolib/table.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/dispatcher.hpp>
......@@ -61,6 +61,8 @@ namespace eosio {
uint64_t symbol = token_type::symbol;
token_type balance;
auto primary_key() const { return symbol; }
EOSLIB_SERIALIZE( account, (symbol)(balance) )
};
......@@ -68,6 +70,8 @@ namespace eosio {
uint64_t symbol = token_type::symbol;
token_type supply;
auto primary_key() const { return symbol; }
EOSLIB_SERIALIZE( currency_stats, (symbol)(supply) )
};
......@@ -75,25 +79,41 @@ namespace eosio {
* Each user stores their balance in the singleton table under the
* scope of their account name.
*/
typedef table64<code, accounts_table_name, code, account> accounts;
typedef table64<code, stats_table_name, code, currency_stats> stats;
typedef eosio::multi_index<accounts_table_name, account> accounts;
typedef eosio::multi_index<stats_table_name, currency_stats> stats;
static token_type get_balance( account_name owner ) {
return accounts::get_or_create( token_type::symbol, owner ).balance;
accounts t( code, owner );
auto ptr = t.find( symbol );
return ptr ? ptr->balance : token_type( asset(0, symbol) );
}
static void set_balance( account_name owner, token_type balance ) {
accounts::set( account{token_type::symbol,balance}, owner );
static void set_balance( account_name owner, token_type balance, account_name create_bill_to, account_name update_bill_to ) {
accounts t( code, owner );
auto f = [&](account& acc) {
acc.symbol = symbol;
acc.balance = balance;
};
auto ptr = t.find( symbol );
if (ptr) {
t.update( *ptr, update_bill_to, f);
} else {
t.emplace( create_bill_to, f);
}
}
static void on( const issue& act ) {
require_auth( code );
auto s = stats::get_or_create(token_type::symbol);
s.supply += act.quantity;
stats::set(s);
stats t( code, code );
auto ptr = t.find( symbol );
if (ptr) {
t.update(*ptr, 0, [&](currency_stats& s) { s.supply += act.quantity; });
} else {
t.emplace(code, [&](currency_stats& s) { s.supply = act.quantity; });
}
set_balance( code, get_balance( code ) + act.quantity );
set_balance( code, get_balance( code ) + act.quantity, code, 0 );
inline_transfer( code, act.to, act.quantity );
}
......@@ -103,8 +123,8 @@ namespace eosio {
require_auth( act.from );
require_recipient(act.to,act.from);
set_balance( act.from, get_balance( act.from ) - act.quantity );
set_balance( act.to, get_balance( act.to ) + act.quantity );
set_balance( act.from, get_balance( act.from ) - act.quantity, act.from, act.from );
set_balance( act.to, get_balance( act.to ) + act.quantity, act.from, 0 );
}
static void inline_transfer( account_name from, account_name to, token_type quantity,
......
此差异已折叠。
......@@ -100,7 +100,7 @@ extern "C" {
/**
*/
void printhex( void* data, uint32_t datalen );
void printhex( const void* data, uint32_t datalen );
/// @}
#ifdef __cplusplus
......
......@@ -6,6 +6,8 @@
#include <eosiolib/print.h>
#include <eosiolib/types.hpp>
#include <eosiolib/math.hpp>
#include <eosiolib/fixed_key.hpp>
#include <utility>
namespace eosio {
......@@ -47,6 +49,10 @@ namespace eosio {
printi(uint64_t(num));
}
inline void print( long num ) {
printi(num);
}
/**
* Prints unsigned integer as a 64 bit unsigned integer
* @brief Prints unsigned integer
......@@ -71,7 +77,20 @@ namespace eosio {
* @param num to be printed
*/
inline void print( uint128_t num ) {
printi128((uint128_t*)&num);
printi128(&num);
}
/**
* Prints fixed_key as a hexidecimal string
* @brief Prints fixed_key as a hexidecimal string
* @param val to be printed
*/
template<size_t Size>
inline void print( const fixed_key<Size>& val ) {
auto arr = val.extract_as_byte_array();
prints("0x");
printhex(static_cast<const void*>(arr.data()), arr.size());
}
/**
......@@ -96,7 +115,7 @@ namespace eosio {
inline void print_f( const char* s ) {
prints(s);
}
template <typename Arg, typename... Args>
inline void print_f( const char* s, Arg val, Args... rest ) {
while ( *s != '\0' ) {
......@@ -151,9 +170,9 @@ namespace eosio {
* @endcode
*/
template<typename Arg, typename... Args>
void print( Arg a, Args... args ) {
print(a);
print(args...);
void print( Arg&& a, Args&&... args ) {
print(std::forward<Arg>(a));
print(std::forward<Args>(args)...);
}
/**
......
#pragma once
#include <eosiolib/db.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/system.h>
namespace eosio {
/**
* This wrapper uses a single table to store named objects various types.
* This wrapper uses a single table to store named objects various types.
*
* @tparam Code - the name of the code which has write permission
* @tparam SingletonName - the name of this singlton variable
* @tparam T - the type of the singleton
* @tparam T - the type of the singleton
*/
template<account_name Code, uint64_t SingletonName, account_name BillToAccount, typename T>
class singleton
{
constexpr static uint64_t pk_value = SingletonName;
struct row {
T value;
uint64_t primary_key() const { return pk_value; }
EOSLIB_SERIALIZE( row, (value) );
};
typedef eosio::multi_index<SingletonName, row> table;
public:
//static const uint64_t singleton_table_name = N(singleton);
static bool exists( scope_name scope = Code ) {
uint64_t key = SingletonName;
auto read = load_i64( Code, scope, key, (char*)&key, sizeof(key) );
return read > 0;
table t( Code, scope );
return t.find( pk_value );
}
static T get( scope_name scope = Code ) {
char temp[1024+8];
*reinterpret_cast<uint64_t *>(temp) = SingletonName;
auto read = load_i64( Code, scope, SingletonName, temp, sizeof(temp) );
eosio_assert( read > 0, "singleton does not exist" );
return unpack<T>( temp + sizeof(SingletonName), read );
table t( Code, scope );
auto ptr = t.find( pk_value );
eosio_assert( bool(ptr), "singleton does not exist" );
return ptr->value;
}
static T get_or_default( scope_name scope = Code, const T& def = T() ) {
char temp[1024+8];
*reinterpret_cast<uint64_t *>(temp) = SingletonName;
auto read = load_i64( Code, scope, SingletonName, temp, sizeof(temp) );
if ( read < 0 ) {
return def;
}
return unpack<T>( temp + sizeof(SingletonName), size_t(read) );
table t( Code, scope );
auto ptr = t.find( pk_value );
return ptr ? ptr->value : def;
}
static T get_or_create( scope_name scope = Code, const T& def = T() ) {
char temp[1024+8];
*reinterpret_cast<uint64_t *>(temp) = SingletonName;
auto read = load_i64( Code, scope, SingletonName, temp, sizeof(temp) );
if( read < 0 ) {
set( def, scope );
return def;
}
return unpack<T>( temp + sizeof(SingletonName), read );
table t( Code, scope );
auto ptr = t.find( pk_value );
return ptr ? ptr->value
: t.emplace(BillToAccount, [&](row& r) { r.value = def; });
}
static void set( const T& value = T(), scope_name scope = Code, account_name b = BillToAccount ) {
auto size = pack_size( value );
char buf[size+ sizeof(SingletonName)];
eosio_assert( sizeof(buf) <= 1024 + 8, "singleton too big to store" );
datastream<char*> ds( buf, size + sizeof(SingletonName) );
ds << SingletonName;
ds << value;
store_i64( scope, SingletonName, b, buf, sizeof(buf) );
table t( Code, scope );
auto ptr = t.find( pk_value );
if (ptr) {
t.update(*ptr, b, [&](row& r) { r.value = value; });
} else {
t.emplace(b, [&](row& r) { r.value = value; });
}
}
static void remove( scope_name scope = Code ) {
uint64_t key = SingletonName;
remove_i64( scope, SingletonName, &key );
table t( Code, scope );
auto ptr = t.find( pk_value );
if (ptr) {
t.remove(*ptr);
}
}
};
......
......@@ -23,10 +23,6 @@ extern "C" {
* @{
*/
struct uint256 {
uint64_t words[4];
};
typedef uint64_t account_name;
typedef uint64_t permission_name;
typedef uint64_t token_name;
......
......@@ -23,7 +23,7 @@ namespace eosio {
/**
* @brief Converts a base32 string to a uint64_t.
* @brief Converts a base32 string to a uint64_t.
*
* @details Converts a base32 string to a uint64_t. This is a constexpr so that
* this method can be used in template arguments as well.
......@@ -87,7 +87,6 @@ namespace eosio {
return ds >> v.value;
}
};
/// @}
} // namespace eos
......@@ -66,7 +66,6 @@
"name": "accountrow",
"base": "",
"fields": [
{"name":"singleton_name", "type":"uint64"},
{"name":"identity", "type":"uint64"}
]
}
......
此差异已折叠。
......@@ -25,28 +25,14 @@ namespace identity_test {
{
uint64_t identity;
template<typename DataStream>
friend DataStream& operator << ( DataStream& ds, const get_owner_for_identity& c ){
return ds << c.identity;
}
template<typename DataStream>
friend DataStream& operator >> ( DataStream& ds, get_owner_for_identity& c ){
return ds >> c.identity;
}
EOSLIB_SERIALIZE( get_owner_for_identity, (identity) );
};
struct get_identity_for_account : public action_meta< code, N(getidentity) >
{
account_name account ;
template<typename DataStream>
friend DataStream& operator << ( DataStream& ds, const get_identity_for_account& c ){
return ds << c.account;
}
template<typename DataStream>
friend DataStream& operator >> ( DataStream& ds, get_identity_for_account& c ){
return ds >> c.account;
}
EOSLIB_SERIALIZE( get_identity_for_account, (account) );
};
typedef singleton<code, N(result), code, uint64_t> result_table;
......
{
"types": [{
"new_type_name": "my_account_name",
"type": "name"
}
],
"types": [],
"structs": [{
"name": "message",
"name": "trigger",
"base": "",
"fields": [
{"name":"from", "type":"my_account_name"},
{"name":"to", "type":"my_account_name"},
{"name": "message", "type":"string" }
{"name": "what", "type": "uint32" }
]
},{
"name": "messages_count",
"base": "",
"fields": [
{"name": "user", "type": "my_account_name"},
{"name": "count", "type": "uint32"}
]
}
}
],
"actions": [{
"name": "message",
"type": "message"
"name": "trigger",
"type": "trigger"
}
],
"tables": [{
"name": "msgsent",
"type": "messages_count",
"index_type": "i64",
"key_names" : ["user"],
"key_types" : ["my_account_name"]
},{
"name": "msgreceived",
"type": "messages_count",
"index_type": "i64",
"key_names" : ["user"],
"key_types" : ["my_account_name"]
}
]
}
\ No newline at end of file
"tables": []
}
#include <eosiolib/eosio.hpp>
#include <eosiolib/dispatcher.hpp>
#include <eosiolib/multi_index.hpp>
using namespace eosio;
namespace multi_index_test {
struct limit_order {
uint64_t id;
......@@ -10,47 +13,166 @@ struct limit_order {
uint64_t expiration;
account_name owner;
auto primary_key()const { return id; }
uint64_t get_expiration()const { return expiration; }
uint128_t get_price()const { return price; }
auto primary_key()const { return id; }
uint64_t get_expiration()const { return expiration; }
uint128_t get_price()const { return price; }
EOSLIB_SERIALIZE( limit_order, (id)(price)(expiration)(owner) )
};
EOSLIB_SERIALIZE( limit_order, (id)(price)(expiration)(owner) )
};
extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action ) {
eosio::multi_index<N(orders), limit_order,
indexed_by<N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration> >,
indexed_by<N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
> orders( N(exchange), N(exchange) );
struct test_k256 {
uint64_t id;
key256 val;
auto payer = code;
const auto& order = orders.emplace( payer, [&]( auto& o ) {
o.id = 1;
o.expiration = 3;
o.owner = N(dan);
});
auto primary_key()const { return id; }
key256 get_val()const { return val; }
orders.update( order, payer, [&]( auto& o ) {
o.expiration = 4;
});
EOSLIB_SERIALIZE( test_k256, (id)(val) )
};
const auto* o = orders.find( 1 );
class multi_index_test {
public:
orders.remove( order );
ACTION(N(multitest), trigger) {
trigger(): what(0) {}
trigger(uint32_t w): what(w) {}
auto expidx = orders.get_index<N(byexp)>();
uint32_t what;
for( const auto& item : orders ) {
(void)item.id;
}
EOSLIB_SERIALIZE(trigger, (what))
};
for( const auto& item : expidx ) {
(void)item.id;
}
static void on(const trigger& act)
{
auto payer = act.get_account();
print("Acting on trigger action.\n");
switch(act.what)
{
case 0:
{
print("Testing uint128_t secondary index.\n");
eosio::multi_index<N(orders), limit_order,
indexed_by< N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration> >,
indexed_by< N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
> orders( N(multitest), N(multitest) );
const auto& order1 = orders.emplace( payer, [&]( auto& o ) {
o.id = 1;
o.expiration = 300;
o.owner = N(dan);
});
const auto& order2 = orders.emplace( payer, [&]( auto& o ) {
o.id = 2;
o.expiration = 200;
o.owner = N(alice);
});
print("Items sorted by primary key:\n");
for( const auto& item : orders ) {
print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name(item.owner), "\n");
}
auto expidx = orders.get_index<N(byexp)>();
print("Items sorted by expiration:\n");
for( const auto& item : expidx ) {
print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name(item.owner), "\n");
}
print("Updating expiration of order with ID=2 to 400.\n");
orders.update( order2, payer, [&]( auto& o ) {
o.expiration = 400;
});
print("Items sorted by expiration:\n");
for( const auto& item : expidx ) {
print(" ID=", item.id, ", expiration=", item.expiration, ", owner=", name(item.owner), "\n");
}
auto lower = expidx.lower_bound(100);
print("First order with an expiration of at least 100 has ID=", lower->id, " and expiration=", lower->get_expiration(), "\n");
}
break;
case 1: // Test key265 secondary index
{
print("Testing key256 secondary index.\n");
eosio::multi_index<N(test1), test_k256,
indexed_by< N(byval), const_mem_fun<test_k256, key256, &test_k256::get_val> >
> testtable( N(multitest), N(exchange) ); // Code must be same as the receiver? Scope doesn't have to be.
const auto& entry1 = testtable.emplace( payer, [&]( auto& o ) {
o.id = 1;
o.val = key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 42ULL);
});
const auto& entry2 = testtable.emplace( payer, [&]( auto& o ) {
o.id = 2;
o.val = key256::make_from_word_sequence<uint64_t>(1ULL, 2ULL, 3ULL, 4ULL);
});
auto lower = expidx.lower_bound(4);
const auto& entry3 = testtable.emplace( payer, [&]( auto& o ) {
o.id = 3;
o.val = key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 42ULL);
});
const auto* e = testtable.find( 2 );
print("Items sorted by primary key:\n");
for( const auto& item : testtable ) {
print(" ID=", item.primary_key(), ", val=", item.val, "\n");
}
auto validx = testtable.get_index<N(byval)>();
auto lower1 = validx.lower_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 40ULL));
print("First entry with a val of at least 40 has ID=", lower1->id, ".\n");
auto lower2 = validx.lower_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 50ULL));
print("First entry with a val of at least 50 has ID=", lower2->id, ".\n");
if( &*lower2 == e ) {
print("Previously found entry is the same as the one found earlier with a primary key value of 2.\n");
}
print("Items sorted by val (secondary key):\n");
for( const auto& item : validx ) {
print(" ID=", item.primary_key(), ", val=");
cout << item.val << "\n";
}
auto upper = validx.upper_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 42ULL));
print("First entry with a val greater than 42 has ID=", upper->id, ".\n");
print("Removed entry with ID=", lower1->id, ".\n");
testtable.remove( *lower1 );
print("Items sorted by primary key:\n");
for( const auto& item : testtable ) {
print(" ID=", item.primary_key(), ", val=");
cout << item.val << "\n";
}
}
break;
default:
eosio_assert(0, "Given what code is not supported.");
break;
}
}
};
} /// multi_index_test
namespace multi_index_test {
extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t code, uint64_t action ) {
eosio_assert(eosio::dispatch<multi_index_test, multi_index_test::trigger>(code, action),
"Could not dispatch");
}
}
}
......@@ -11,16 +11,25 @@ namespace proxy {
using namespace eosio;
namespace configs {
bool get(config &out, const account_name &self) {
auto read = load_i64(self, self, N(config), (char*)&out, sizeof(config));
if (read < 0) {
auto it = db_find_i64(self, self, N(config), config::key);
if (it != -1) {
auto size = db_get_i64(it, (char*)&out, sizeof(config));
eosio_assert(size == sizeof(config), "Wrong record size");
return true;
} else {
return false;
}
return true;
}
void store(const config &in, const account_name &self) {
store_i64(self, N(config), self, (const char *)&in, sizeof(config));
auto it = db_find_i64(self, self, N(config), config::key);
if (it != -1) {
db_update_i64(it, self, (const char *)&in, sizeof(config));
} else {
db_store_i64(self, N(config), self, config::key, (const char *)&in, sizeof(config));
}
}
};
......
......@@ -15,7 +15,7 @@ namespace proxy {
//@abi table
struct config {
config(){}
const uint64_t key = N(config);
constexpr static uint64_t key = N(config);
account_name owner = 0;
uint32_t delay = 0;
uint32_t next_id = 0;
......
......@@ -12,7 +12,7 @@
using namespace eosio;
namespace simpledb {
template<uint64_t Val>
struct dispatchable {
constexpr static uint64_t action_name = Val;
......@@ -20,22 +20,22 @@ namespace simpledb {
//@abi table
struct record1 {
uint64_t key;
uint256 u256;
//uint256 u256;
uint128_t u128;
uint64_t u64;
uint32_t u32;
uint16_t u16;
uint16_t u16;
uint8_t u8;
int64_t i64;
int32_t i32;
int16_t i16;
int8_t i8;
EOSLIB_SERIALIZE( record1, (key)(u256)(u128)(u64)(u32)(u16)(u8)(i64)(i32)(i16)(i8) );
EOSLIB_SERIALIZE( record1, (key)(u128)(u64)(u32)(u16)(u8)(i64)(i32)(i16)(i8) );
};
//@abi action insert1
......@@ -223,4 +223,4 @@ extern "C" {
}
}
}
\ No newline at end of file
}
......@@ -24,7 +24,7 @@ static constexpr u64 WASM_TEST_ACTION(const char* cls, const char* method)
CLASS::METHOD(); \
return; \
}
#pragma pack(push, 1)
struct dummy_action {
char a; //1
......@@ -123,7 +123,7 @@ struct test_db {
static void key_i64i64i64_under_limit();
static void key_i64i64i64_available_space_exceed_limit();
static void key_i64i64i64_another_under_limit();
static void primary_i64_general();
static void primary_i64_lowerbound();
static void primary_i64_upperbound();
......@@ -133,6 +133,16 @@ struct test_db {
static void idx64_upperbound();
};
struct test_multi_index {
static void idx64_general();
static void idx64_store_only();
static void idx64_check_without_storing();
static void idx128_autoincrement_test();
static void idx128_autoincrement_test_part1();
static void idx128_autoincrement_test_part2();
static void idx256_general();
};
struct test_crypto {
static void test_recover_key();
static void test_recover_key_assert_true();
......@@ -233,4 +243,3 @@ struct test_checktime {
static void checktime_pass();
static void checktime_failure();
};
......@@ -7,7 +7,7 @@
#include "test_api.hpp"
void test_types::types_size() {
eosio_assert( sizeof(int64_t) == 8, "int64_t size != 8");
eosio_assert( sizeof(uint64_t) == 8, "uint64_t size != 8");
eosio_assert( sizeof(uint32_t) == 4, "uint32_t size != 4");
......@@ -20,11 +20,11 @@ void test_types::types_size() {
eosio_assert( sizeof(token_name) == 8, "token_name size != 8");
eosio_assert( sizeof(table_name) == 8, "table_name size != 8");
eosio_assert( sizeof(time) == 4, "time size != 4");
eosio_assert( sizeof(uint256) == 32, "uint256 != 32" );
eosio_assert( sizeof(key256) == 32, "key256 size != 32" );
}
void test_types::char_to_symbol() {
eosio_assert( eosio::char_to_symbol('1') == 1, "eosio::char_to_symbol('1') != 1");
eosio_assert( eosio::char_to_symbol('2') == 2, "eosio::char_to_symbol('2') != 2");
eosio_assert( eosio::char_to_symbol('3') == 3, "eosio::char_to_symbol('3') != 3");
......@@ -56,7 +56,7 @@ void test_types::char_to_symbol() {
eosio_assert( eosio::char_to_symbol('x') == 29, "eosio::char_to_symbol('x') != 29");
eosio_assert( eosio::char_to_symbol('y') == 30, "eosio::char_to_symbol('y') != 30");
eosio_assert( eosio::char_to_symbol('z') == 31, "eosio::char_to_symbol('z') != 31");
for(unsigned char i = 0; i<255; i++) {
if((i >= 'a' && i <= 'z') || (i >= '1' || i <= '5')) continue;
eosio_assert( eosio::char_to_symbol(i) == 0, "eosio::char_to_symbol() != 0");
......
......@@ -89,7 +89,7 @@ void test_db::key_str_table() {
const char* atr[] = { "atr", "atr", "atr", "atr" };
const char* ztr[] = { "ztr", "ztr", "ztr", "ztr" };
eosio::var_table<N(tester), N(tester), N(atr), char*> StringTableAtr;
eosio::var_table<N(tester), N(tester), N(ztr), char*> StringTableZtr;
eosio::var_table<N(tester), N(tester), N(str), char*> StringTableStr;
......@@ -100,13 +100,13 @@ void test_db::key_str_table() {
for( int ii = 0; ii < 4; ++ii ) {
res = StringTableAtr.store( (char*)keys[ii], STRLEN(keys[ii]), (char*)atr[ii], STRLEN(atr[ii]) );
eosio_assert( res != 0, "atr" );
res = StringTableZtr.store( (char*)keys[ii], STRLEN(keys[ii]), (char*)ztr[ii], STRLEN(ztr[ii]) );
eosio_assert(res != 0, "ztr" );
}
char tmp[64];
res = StringTableStr.store ((char *)keys[0], STRLEN(keys[0]), (char *)vals[0], STRLEN(vals[0]));
eosio_assert(res != 0, "store alice" );
......@@ -307,7 +307,7 @@ void test_db::key_i64_general() {
res = previous_i64( current_receiver(), current_receiver(), N(test_table), &tmp, sizeof(test_model) );
eosio_assert(res == sizeof(test_model) && tmp.name == N(carol) && tmp.age == 30 && tmp.phone == 545342453, "carol previous");
res = previous_i64( current_receiver(), current_receiver(), N(test_table), &tmp, sizeof(test_model) );
eosio_assert(res == sizeof(test_model) && tmp.name == N(bob) && tmp.age == 15 && tmp.phone == 11932435, "bob previous");
......@@ -340,13 +340,13 @@ void test_db::key_i64_general() {
alice.age = 21;
alice.phone = 1234;
res = store_i64(current_receiver(), N(test_table), &alice, sizeof(test_model));
eosio_assert(res == 0, "store alice 2" );
my_memset(&alice, 0, sizeof(test_model));
alice.name = N(alice);
res = load_i64(current_receiver(), current_receiver(), N(test_table), &alice, sizeof(test_model));
eosio_assert(res == sizeof(test_model) && alice.age == 21 && alice.phone == 1234, "alice error 2");
......@@ -426,7 +426,7 @@ void test_db::key_i64_general() {
res = load_i64(current_receiver(), current_receiver(), N(test_table), &tmp2, sizeof(test_model_v3));
eosio_assert(res == sizeof(test_model_v2) &&
tmp2.age == 21 &&
tmp2.age == 21 &&
tmp2.phone == 1234 &&
tmp2.new_field == 66655444,
"load4update");
......@@ -437,7 +437,7 @@ void test_db::key_i64_general() {
res = load_i64(current_receiver(), current_receiver(), N(test_table), &tmp2, sizeof(test_model_v3));
eosio_assert(res == sizeof(test_model_v3) &&
tmp2.age == 21 &&
tmp2.age == 21 &&
tmp2.phone == 1234 &&
tmp2.new_field == 66655444 &&
tmp2.another_field == 221122,
......@@ -449,7 +449,7 @@ void test_db::key_i64_general() {
res = load_i64(current_receiver(), current_receiver(), N(test_table), &tmp2, sizeof(test_model_v3));
eosio_assert(res == sizeof(test_model_v3) &&
tmp2.age == 11 &&
tmp2.age == 11 &&
tmp2.phone == 1234 &&
tmp2.new_field == 66655444 &&
tmp2.another_field == 221122,
......@@ -466,22 +466,22 @@ void test_db::key_i64_general() {
}
void test_db::key_i64_remove_all() {
uint32_t res = 0;
uint64_t key;
key = N(alice);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 1, "remove alice");
key = N(bob);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 1, "remove bob");
key = N(carol);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 1, "remove carol");
key = N(dave);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 1, "remove dave");
......@@ -492,19 +492,19 @@ void test_db::key_i64_remove_all() {
res = back_i64( current_receiver(), current_receiver(), N(test_table), &tmp, sizeof(test_model) );
eosio_assert(res == 0, "back_i64_i64 remove");
key = N(alice);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 0, "remove alice 1");
key = N(bob);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 0, "remove bob 1");
key = N(carol);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 0, "remove carol 1");
key = N(dave);
res = remove_i64(current_receiver(), N(test_table), &key);
eosio_assert(res == 0, "remove dave 1");
......@@ -546,7 +546,7 @@ void test_db::key_i64_not_found() {
}
void test_db::key_i64_front_back() {
uint32_t res = 0;
test_model dave { N(dave), 46, 6535354};
......@@ -598,7 +598,7 @@ void test_db::key_i64_front_back() {
key = N(dave);
remove_i64(current_receiver(), N(b), &key);
res = front_i64( current_receiver(), current_receiver(), N(b), &tmp, sizeof(test_model) );
eosio_assert(res == 0, "key_i64_front 9");
res = back_i64( current_receiver(), current_receiver(), N(b), &tmp, sizeof(test_model) );
......@@ -628,7 +628,7 @@ uint32_t store_set_in_table(uint64_t table_name)
{
uint32_t res = 0;
TestModel128x2 alice0{0, 500, N(alice0), table_name};
TestModel128x2 alice1{1, 400, N(alice1), table_name};
TestModel128x2 alice2{2, 300, N(alice2), table_name};
......@@ -714,7 +714,7 @@ void store_set_in_table(TestModel3xi64* records, int len, uint64_t table_name) {
//TODO fix things
#if 0
void test_db::key_i64i64i64_general() {
uint32_t res = 0;
TestModel3xi64 records[] = {
......@@ -784,7 +784,7 @@ void test_db::key_i64i64i64_general() {
V={4}; LOAD_OK(primary, i64i64i64, N(table2), 7, "i64x3 LOAD primary 4");
V={5}; LOAD_OK(primary, i64i64i64, N(table2), 9, "i64x3 LOAD primary 5");
V={6}; LOAD_ER(primary, i64i64i64, N(table2), "i64x3 LOAD primary fail 6");
V={11,0}; LOAD_OK(secondary, i64i64i64, N(table2), 7, "i64x3 LOAD secondary 0");
V={11,1}; LOAD_OK(secondary, i64i64i64, N(table2), 0, "i64x3 LOAD secondary 1");
V={11,2}; LOAD_OK(secondary, i64i64i64, N(table2),10, "i64x3 LOAD secondary 2");
......@@ -886,7 +886,7 @@ void test_db::key_i64i64i64_general() {
v2.new_field = 555;
res = update_i64i64i64(current_receiver(), N(table2), &v2, sizeof(TestModel3xi64_V2));
eosio_assert(res == 1, "store v2");
eosio_assert(res == 1, "store v2");
res = LOAD(primary, i64i64i64, N(table2), v2);
eosio_assert(res == sizeof(TestModel3xi64_V2), "load v2 updated");
......@@ -899,7 +899,7 @@ void test_db::key_i64i64i64_general() {
return 0;
}
#endif
#endif
void test_db::key_i128i128_general() {
uint32_t res = 0;
......@@ -928,7 +928,7 @@ return;
my_memset(&tmp, 0, sizeof(TestModel128x2));
tmp.price = 4;
res = load_secondary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert( res == sizeof(TestModel128x2) &&
tmp.number == 13 &&
......@@ -944,7 +944,7 @@ return;
tmp.extra == N(alice0) &&
tmp.table_name == N(table5),
"front primary load");
res = previous_primary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert(res == -1, "previous primary fail");
......@@ -963,7 +963,7 @@ return;
tmp.extra == N(bob0) &&
tmp.table_name == N(table5),
"front secondary ok");
res = previous_secondary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert(res == -1, "previous secondary fail");
......@@ -982,7 +982,7 @@ return;
tmp.extra == N(dave3) &&
tmp.table_name == N(table5),
"back primary ok");
res = next_primary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert(res == -1, "next primary fail");
......@@ -1001,12 +1001,12 @@ return;
tmp.extra == N(carol0) &&
tmp.table_name == N(table5),
"back secondary ok");
res = next_secondary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert(res == -1, "next secondary fail");
res = previous_secondary_i128i128( current_receiver(), current_receiver(), N(table5), &tmp, sizeof(TestModel128x2) );
eosio_assert( res == sizeof(TestModel128x2) &&
tmp.number == 21 &&
tmp.price == 800 &&
......@@ -1052,7 +1052,7 @@ return;
tmp2.extra == N(carol0) &&
tmp2.table_name == N(table5),
"ub secondary ok");
tmp2.new_field = 123456;
res = update_i128i128(current_receiver(), N(table5), &tmp2, sizeof(TestModel128x2_V2));
eosio_assert( res == 1, "update_i128i128 ok");
......@@ -1526,7 +1526,7 @@ void test_db::primary_i64_general()
// nothing after charlie
uint64_t prim = 0;
int end_itr = db_next_i64(charlie_itr, &prim);
eosio_assert(end_itr == -1, "primary_i64_general - db_next_i64");
eosio_assert(end_itr < 0, "primary_i64_general - db_next_i64");
// prim didn't change
eosio_assert(prim == 0, "primary_i64_general - db_next_i64");
}
......@@ -1548,23 +1548,22 @@ void test_db::primary_i64_general()
eosio_assert(itr_prev == itr_prev_expected && prim == N(alice), "primary_i64_general - db_previous_i64");
itr_prev = db_previous_i64(itr_prev, &prim);
itr_prev_expected = -1;
eosio_assert(itr_prev == itr_prev_expected && prim == N(alice), "primary_i64_general - db_previous_i64");
eosio_assert(itr_prev < 0 && prim == N(alice), "primary_i64_general - db_previous_i64");
}
// remove
{
int itr = db_find_i64(current_receiver(), current_receiver(), table1, N(alice));
eosio_assert(itr != -1, "primary_i64_general - db_find_i64");
eosio_assert(itr >= 0, "primary_i64_general - db_find_i64");
db_remove_i64(itr);
itr = db_find_i64(current_receiver(), current_receiver(), table1, N(alice));
eosio_assert(itr == -1, "primary_i64_general - db_find_i64");
eosio_assert(itr < 0, "primary_i64_general - db_find_i64");
}
// get
{
int itr = db_find_i64(current_receiver(), current_receiver(), table1, N(bob));
eosio_assert(itr != -1, "");
eosio_assert(itr >= 0, "");
int buffer_len = 5;
char value[50];
auto len = db_get_i64(itr, value, buffer_len);
......@@ -1572,7 +1571,7 @@ void test_db::primary_i64_general()
std::string s(value);
eosio_assert(len == strlen("bob's info"), "primary_i64_general - db_get_i64");
eosio_assert(s == "bob's", "primary_i64_general - db_get_i64");
buffer_len = 20;
db_get_i64(itr, value, buffer_len);
value[buffer_len] = '\0';
......@@ -1583,9 +1582,9 @@ void test_db::primary_i64_general()
// update
{
int itr = db_find_i64(current_receiver(), current_receiver(), table1, N(bob));
eosio_assert(itr != -1, "");
eosio_assert(itr >= 0, "");
const char* new_value = "bob's new info";
int new_value_len = strlen(new_value);
int new_value_len = strlen(new_value);
db_update_i64(itr, current_receiver(), new_value, new_value_len);
char ret_value[50];
auto len = db_get_i64(itr, ret_value, new_value_len);
......@@ -1625,7 +1624,7 @@ void test_db::primary_i64_lowerbound()
}
{
int lb = db_lowerbound_i64(current_receiver(), current_receiver(), table, N(kevin));
eosio_assert(lb == -1, err.c_str());
eosio_assert(lb < 0, err.c_str());
}
}
......@@ -1647,11 +1646,11 @@ void test_db::primary_i64_upperbound()
}
{
int ub = db_upperbound_i64(current_receiver(), current_receiver(), table, N(joe));
eosio_assert(ub == -1, err.c_str());
eosio_assert(ub < 0, err.c_str());
}
{
int ub = db_upperbound_i64(current_receiver(), current_receiver(), table, N(kevin));
eosio_assert(ub == -1, err.c_str());
eosio_assert(ub < 0, err.c_str());
}
}
......@@ -1683,62 +1682,62 @@ void test_db::idx64_general()
{
secondary_type sec = 0;
int itr = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec, 999);
eosio_assert(itr == -1 && sec == 0, "idx64_general - db_idx64_find_primary");
eosio_assert(itr < 0 && sec == 0, "idx64_general - db_idx64_find_primary");
itr = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec, 110);
eosio_assert(itr != -1 && sec == N(joe), "idx64_general - db_idx64_find_primary");
eosio_assert(itr >= 0 && sec == N(joe), "idx64_general - db_idx64_find_primary");
uint64_t prim_next = 0;
int itr_next = db_idx64_next(itr, &prim_next);
eosio_assert(itr_next == -1 && prim_next == 0, "idx64_general - db_idx64_find_primary");
eosio_assert(itr_next < 0 && prim_next == 0, "idx64_general - db_idx64_find_primary");
}
// iterate forward starting with charlie
{
secondary_type sec = 0;
int itr = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec, 234);
eosio_assert(itr != -1 && sec == N(charlie), "idx64_general - db_idx64_find_primary");
eosio_assert(itr >= 0 && sec == N(charlie), "idx64_general - db_idx64_find_primary");
uint64_t prim_next = 0;
int itr_next = db_idx64_next(itr, &prim_next);
eosio_assert(itr_next != -1 && prim_next == 976, "idx64_general - db_idx64_next");
eosio_assert(itr_next >= 0 && prim_next == 976, "idx64_general - db_idx64_next");
secondary_type sec_next = 0;
int itr_next_expected = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec_next, prim_next);
eosio_assert(itr_next == itr_next_expected && sec_next == N(emily), "idx64_general - db_idx64_next");
itr_next = db_idx64_next(itr_next, &prim_next);
eosio_assert(itr_next != -1 && prim_next == 110, "idx64_general - db_idx64_next");
eosio_assert(itr_next >= 0 && prim_next == 110, "idx64_general - db_idx64_next");
itr_next_expected = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec_next, prim_next);
eosio_assert(itr_next == itr_next_expected && sec_next == N(joe), "idx64_general - db_idx64_next");
itr_next = db_idx64_next(itr_next, &prim_next);
eosio_assert(itr_next == -1 && prim_next == 110, "idx64_general - db_idx64_next");
eosio_assert(itr_next < 0 && prim_next == 110, "idx64_general - db_idx64_next");
}
// iterate backward staring with second bob
{
secondary_type sec = 0;
int itr = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec, 781);
eosio_assert(itr != -1 && sec == N(bob), "idx64_general - db_idx64_find_primary");
eosio_assert(itr >= 0 && sec == N(bob), "idx64_general - db_idx64_find_primary");
uint64_t prim_prev = 0;
int itr_prev = db_idx64_previous(itr, &prim_prev);
eosio_assert(itr_prev != -1 && prim_prev == 540, "idx64_general - db_idx64_previous");
eosio_assert(itr_prev >= 0 && prim_prev == 540, "idx64_general - db_idx64_previous");
secondary_type sec_prev = 0;
int itr_prev_expected = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec_prev, prim_prev);
eosio_assert(itr_prev == itr_prev_expected && sec_prev == N(bob), "idx64_general - db_idx64_previous");
itr_prev = db_idx64_previous(itr_prev, &prim_prev);
eosio_assert(itr_prev != -1 && prim_prev == 650, "idx64_general - db_idx64_previous");
eosio_assert(itr_prev >= 0 && prim_prev == 650, "idx64_general - db_idx64_previous");
itr_prev_expected = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec_prev, prim_prev);
eosio_assert(itr_prev == itr_prev_expected && sec_prev == N(allyson), "idx64_general - db_idx64_previous");
itr_prev = db_idx64_previous(itr_prev, &prim_prev);
eosio_assert(itr_prev != -1 && prim_prev == 265, "idx64_general - db_idx64_previous");
eosio_assert(itr_prev >= 0 && prim_prev == 265, "idx64_general - db_idx64_previous");
itr_prev_expected = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec_prev, prim_prev);
eosio_assert(itr_prev == itr_prev_expected && sec_prev == N(alice), "idx64_general - db_idx64_previous");
itr_prev = db_idx64_previous(itr_prev, &prim_prev);
eosio_assert(itr_prev == -1 && prim_prev == 265, "idx64_general - db_idx64_previous");
eosio_assert(itr_prev < 0 && prim_prev == 265, "idx64_general - db_idx64_previous");
}
// find_secondary
......@@ -1746,15 +1745,15 @@ void test_db::idx64_general()
uint64_t prim = 0;
auto sec = N(bob);
int itr = db_idx64_find_secondary(current_receiver(), current_receiver(), table, &sec, &prim);
eosio_assert(itr != -1 && prim == 540, "idx64_general - db_idx64_find_secondary");
eosio_assert(itr >= 0 && prim == 540, "idx64_general - db_idx64_find_secondary");
sec = N(emily);
itr = db_idx64_find_secondary(current_receiver(), current_receiver(), table, &sec, &prim);
eosio_assert(itr != -1 && prim == 976, "idx64_general - db_idx64_find_secondary");
eosio_assert(itr >= 0 && prim == 976, "idx64_general - db_idx64_find_secondary");
sec = N(frank);
itr = db_idx64_find_secondary(current_receiver(), current_receiver(), table, &sec, &prim);
eosio_assert(itr == -1 && prim == 976, "idx64_general - db_idx64_find_secondary");
eosio_assert(itr < 0 && prim == 976, "idx64_general - db_idx64_find_secondary");
}
// update and remove
......@@ -1769,7 +1768,7 @@ void test_db::idx64_general()
eosio_assert(sec_itr == itr && sec == new_name, "idx64_general - db_idx64_update");
db_idx64_remove(itr);
int itrf = db_idx64_find_primary(current_receiver(), current_receiver(), table, &sec, ssn);
eosio_assert(itrf == -1, "idx64_general - db_idx64_remove");
eosio_assert(itrf < 0, "idx64_general - db_idx64_remove");
}
}
......@@ -1807,7 +1806,7 @@ void test_db::idx64_lowerbound()
uint64_t lb_prim = 0;
int lb = db_idx64_lowerbound(current_receiver(), current_receiver(), table, &lb_sec, &lb_prim);
eosio_assert(lb_prim == 0 && lb_sec == N(kevin), err.c_str());
eosio_assert(lb == -1, "");
eosio_assert(lb < 0, "");
}
}
......@@ -1838,14 +1837,13 @@ void test_db::idx64_upperbound()
const uint64_t ssn = 110;
int ub = db_idx64_upperbound(current_receiver(), current_receiver(), table, &ub_sec, &ub_prim);
eosio_assert(ub_prim == 0 && ub_sec == N(joe), err.c_str());
eosio_assert(ub == -1, err.c_str());
eosio_assert(ub < 0, err.c_str());
}
{
secondary_type ub_sec = N(kevin);
uint64_t ub_prim = 0;
int ub = db_idx64_upperbound(current_receiver(), current_receiver(), table, &ub_sec, &ub_prim);
eosio_assert(ub_prim == 0 && ub_sec == N(kevin), err.c_str());
eosio_assert(ub == -1, err.c_str());
eosio_assert(ub < 0, err.c_str());
}
}
add_wast_executable(TARGET test_api_multi_index
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosiolib/eosio.hpp>
#include "../test_api/test_api.hpp"
#include "test_multi_index.cpp"
extern "C" {
void init() {
}
void apply( unsigned long long code, unsigned long long action ) {
WASM_TEST_HANDLER(test_multi_index, idx64_general);
WASM_TEST_HANDLER(test_multi_index, idx64_store_only);
WASM_TEST_HANDLER(test_multi_index, idx64_check_without_storing);
WASM_TEST_HANDLER(test_multi_index, idx128_autoincrement_test);
WASM_TEST_HANDLER(test_multi_index, idx128_autoincrement_test_part1);
WASM_TEST_HANDLER(test_multi_index, idx128_autoincrement_test_part2);
WASM_TEST_HANDLER(test_multi_index, idx256_general);
//unhandled test call
eosio_assert(false, "Unknown Test");
}
}
#include <eosiolib/multi_index.hpp>
#include "../test_api/test_api.hpp"
#include <eosiolib/print.hpp>
namespace _test_multi_index {
using eosio::key256;
struct record_idx64 {
uint64_t id;
uint64_t sec;
auto primary_key()const { return id; }
uint64_t get_secondary()const { return sec; }
EOSLIB_SERIALIZE( record_idx64, (id)(sec) )
};
struct record_idx128 {
uint64_t id;
uint128_t sec;
auto primary_key()const { return id; }
uint128_t get_secondary()const { return sec; }
EOSLIB_SERIALIZE( record_idx128, (id)(sec) )
};
struct record_idx256 {
uint64_t id;
key256 sec;
auto primary_key()const { return id; }
const key256& get_secondary()const { return sec; }
EOSLIB_SERIALIZE( record_idx256, (id)(sec) )
};
template<uint64_t TableName>
void idx64_store_only()
{
using namespace eosio;
typedef record_idx64 record;
record records[] = {{265, N(alice)},
{781, N(bob)},
{234, N(charlie)},
{650, N(allyson)},
{540, N(bob)},
{976, N(emily)},
{110, N(joe)}
};
size_t num_records = sizeof(records)/sizeof(records[0]);
// Construct and fill table using multi_index
multi_index<TableName, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint64_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
auto payer = current_receiver();
for (size_t i = 0; i < num_records; ++i) {
table.emplace( payer, [&]( auto& r ) {
r.id = records[i].id;
r.sec = records[i].sec;
});
}
}
template<uint64_t TableName>
void idx64_check_without_storing()
{
using namespace eosio;
typedef record_idx64 record;
// Load table using multi_index
multi_index<TableName, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint64_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
auto payer = current_receiver();
auto secondary_index = table.template get_index<N(bysecondary)>();
// find by primary key
{
auto ptr = table.find(999);
eosio_assert(ptr == nullptr, "idx64_general - table.find() of non-existing primary key");
ptr = table.find(976);
eosio_assert(ptr != nullptr && ptr->sec == N(emily), "idx64_general - table.find() of existing primary key");
// Workaround: would prefer to instead receive iterator (rather than pointer) from find().
auto itr = table.lower_bound(976);
eosio_assert(itr != table.end() && itr->id == 976 && itr->sec == N(emily), "idx64_general - iterator to existing object in primary index");
++itr;
eosio_assert(itr == table.end(), "idx64_general - increment primary iterator to end");
}
// iterate forward starting with charlie
{
auto itr = secondary_index.lower_bound(N(charlie));
eosio_assert(itr != secondary_index.end() && itr->sec == N(charlie), "idx64_general - secondary_index.lower_bound()");
++itr;
eosio_assert(itr != secondary_index.end() && itr->id == 976 && itr->sec == N(emily), "idx64_general - increment secondary iterator");
++itr;
eosio_assert(itr != secondary_index.end() && itr->id == 110 && itr->sec == N(joe), "idx64_general - increment secondary iterator again");
++itr;
eosio_assert(itr == secondary_index.end(), "idx64_general - increment secondary iterator to end");
}
// iterate backward staring with second bob
{
auto ptr = table.find(781);
eosio_assert(ptr != nullptr && ptr->sec == N(bob), "idx64_general - table.find() of existing primary key");
// Workaround: need to add find_primary wrapper support in secondary indices of multi_index
auto itr = secondary_index.upper_bound(ptr->sec); --itr;
eosio_assert(itr->id == 781 && itr->sec == N(bob), "idx64_general - iterator to existing object in secondary index");
--itr;
eosio_assert(itr != secondary_index.end() && itr->id == 540 && itr->sec == N(bob), "idx64_general - decrement secondary iterator");
--itr;
eosio_assert(itr != secondary_index.end() && itr->id == 650 && itr->sec == N(allyson), "idx64_general - decrement secondary iterator again");
--itr;
eosio_assert(itr == secondary_index.begin() && itr->id == 265 && itr->sec == N(alice), "idx64_general - decrement secondary iterator to beginning");
}
// update and remove
{
const uint64_t ssn = 421;
const auto& new_person = table.emplace( payer, [&]( auto& r ) {
r.id = ssn;
r.sec = N(bob);
});
table.update(new_person, payer, [&]( auto& r ) {
r.sec = N(billy);
});
auto ptr = table.find(ssn);
eosio_assert(ptr != nullptr && ptr->sec == N(billy), "idx64_general - table.update()");
table.remove(*ptr);
auto ptr2 = table.find(ssn);
eosio_assert( ptr2 == nullptr, "idx64_general - table.remove()");
}
}
} /// _test_multi_index
void test_multi_index::idx64_store_only()
{
_test_multi_index::idx64_store_only<N(indextable1)>();
}
void test_multi_index::idx64_check_without_storing()
{
_test_multi_index::idx64_check_without_storing<N(indextable1)>();
}
void test_multi_index::idx64_general()
{
_test_multi_index::idx64_store_only<N(indextable2)>();
_test_multi_index::idx64_check_without_storing<N(indextable2)>();
}
void test_multi_index::idx128_autoincrement_test()
{
using namespace eosio;
using namespace _test_multi_index;
typedef record_idx128 record;
const uint64_t table_name = N(indextable3);
auto payer = current_receiver();
multi_index<table_name, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint128_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
for( int i = 0; i < 5; ++i ) {
table.emplace( payer, [&]( auto& r ) {
r.id = table.available_primary_key();
r.sec = 1000 - static_cast<uint128_t>(r.id);
});
}
uint64_t expected_key = 4;
for( const auto& r : table.get_index<N(bysecondary)>() )
{
eosio_assert( r.primary_key() == expected_key, "idx128_autoincrement_test - unexpected primary key" );
--expected_key;
}
auto ptr = table.find(3);
eosio_assert( ptr != nullptr, "idx128_autoincrement_test - could not find object with primary key of 3" );
table.update(*ptr, payer, [&]( auto& r ) {
r.id = 100;
});
eosio_assert( table.available_primary_key() == 101, "idx128_autoincrement_test - next_primary_key was not correct after record update" );
}
void test_multi_index::idx128_autoincrement_test_part1()
{
using namespace eosio;
using namespace _test_multi_index;
typedef record_idx128 record;
const uint64_t table_name = N(indextable4);
auto payer = current_receiver();
multi_index<table_name, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint128_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
for( int i = 0; i < 3; ++i ) {
table.emplace( payer, [&]( auto& r ) {
r.id = table.available_primary_key();
r.sec = 1000 - static_cast<uint128_t>(r.id);
});
}
table.remove(table.get(0));
uint64_t expected_key = 2;
for( const auto& r : table.get_index<N(bysecondary)>() )
{
eosio_assert( r.primary_key() == expected_key, "idx128_autoincrement_test_part1 - unexpected primary key" );
--expected_key;
}
}
void test_multi_index::idx128_autoincrement_test_part2()
{
using namespace eosio;
using namespace _test_multi_index;
typedef record_idx128 record;
const uint64_t table_name = N(indextable4);
auto payer = current_receiver();
{
multi_index<table_name, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint128_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
eosio_assert( table.available_primary_key() == 3, "idx128_autoincrement_test_part2 - did not recover expected next primary key");
}
multi_index<table_name, record,
indexed_by< N(bysecondary), const_mem_fun<record, uint128_t, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
table.emplace( payer, [&]( auto& r) {
r.id = 0;
r.sec = 1000;
});
// Done this way to make sure that table._next_primary_key is not incorrectly set to 1.
for( int i = 3; i < 5; ++i ) {
table.emplace( payer, [&]( auto& r ) {
auto itr = table.available_primary_key();
print(itr, "\n");
r.id = itr;
r.sec = 1000 - static_cast<uint128_t>(r.id);
});
}
uint64_t expected_key = 4;
for( const auto& r : table.get_index<N(bysecondary)>() )
{
eosio_assert( r.primary_key() == expected_key, "idx128_autoincrement_test_part2 - unexpected primary key" );
--expected_key;
}
auto ptr = table.find(3);
eosio_assert( ptr != nullptr, "idx128_autoincrement_test_part2 - could not find object with primary key of 3" );
table.update(*ptr, payer, [&]( auto& r ) {
r.id = 100;
});
eosio_assert( table.available_primary_key() == 101, "idx128_autoincrement_test_part2 - next_primary_key was not correct after record update" );
}
void test_multi_index::idx256_general()
{
using namespace eosio;
using namespace _test_multi_index;
typedef record_idx256 record;
const uint64_t table_name = N(indextable5);
auto payer = current_receiver();
print("Testing key256 secondary index.\n");
multi_index<table_name, record,
indexed_by< N(bysecondary), const_mem_fun<record, const key256&, &record::get_secondary> >
> table( current_receiver(), current_receiver() );
auto fourtytwo = key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 42ULL);
auto onetwothreefour = key256::make_from_word_sequence<uint64_t>(1ULL, 2ULL, 3ULL, 4ULL);
const auto& entry1 = table.emplace( payer, [&]( auto& o ) {
o.id = 1;
o.sec = fourtytwo;
});
const auto& entry2 = table.emplace( payer, [&]( auto& o ) {
o.id = 2;
o.sec = onetwothreefour;
});
const auto& entry3 = table.emplace( payer, [&]( auto& o ) {
o.id = 3;
o.sec = fourtytwo;
});
const auto* e = table.find( 2 );
print("Items sorted by primary key:\n");
for( const auto& item : table ) {
print(" ID=", item.primary_key(), ", secondary=", item.sec, "\n");
}
{
auto itr = table.begin();
eosio_assert( itr->primary_key() == 1 && itr->get_secondary() == fourtytwo, "idx256_general - primary key sort" );
++itr;
eosio_assert( itr->primary_key() == 2 && itr->get_secondary() == onetwothreefour, "idx256_general - primary key sort" );
++itr;
eosio_assert( itr->primary_key() == 3 && itr->get_secondary() == fourtytwo, "idx256_general - primary key sort" );
++itr;
eosio_assert( itr == table.end(), "idx256_general - primary key sort" );
}
auto secidx = table.get_index<N(bysecondary)>();
auto lower1 = secidx.lower_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 40ULL));
print("First entry with a secondary key of at least 40 has ID=", lower1->id, ".\n");
eosio_assert( lower1->id == 1, "idx256_general - lower_bound" );
auto lower2 = secidx.lower_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 50ULL));
print("First entry with a secondary key of at least 50 has ID=", lower2->id, ".\n");
eosio_assert( lower2->id == 2, "idx256_general - lower_bound" );
if( &*lower2 == e ) {
print("Previously found entry is the same as the one found earlier with a primary key value of 2.\n");
}
print("Items sorted by secondary key (key256):\n");
for( const auto& item : secidx ) {
print(" ID=", item.primary_key(), ", secondary=");
cout << item.sec << "\n";
}
{
auto itr = secidx.begin();
eosio_assert( itr->primary_key() == 1, "idx256_general - secondary key sort" );
++itr;
eosio_assert( itr->primary_key() == 3, "idx256_general - secondary key sort" );
++itr;
eosio_assert( itr->primary_key() == 2, "idx256_general - secondary key sort" );
++itr;
eosio_assert( itr == secidx.end(), "idx256_general - secondary key sort" );
}
auto upper = secidx.upper_bound(key256::make_from_word_sequence<uint64_t>(0ULL, 0ULL, 0ULL, 42ULL));
print("First entry with a secondary key greater than 42 has ID=", upper->id, ".\n");
eosio_assert( upper->id == 2, "idx256_general - upper_bound" );
print("Removed entry with ID=", lower1->id, ".\n");
table.remove( *lower1 );
print("Items sorted by primary key:\n");
for( const auto& item : table ) {
print(" ID=", item.primary_key(), ", secondary=", item.sec, "\n");
}
{
auto itr = table.begin();
eosio_assert( itr->primary_key() == 2 && itr->get_secondary() == onetwothreefour, "idx256_general - primary key sort after remove" );
++itr;
eosio_assert( itr->primary_key() == 3 && itr->get_secondary() == fourtytwo, "idx256_general - primary key sort after remove" );
++itr;
eosio_assert( itr == table.end(), "idx256_general - primary key sort after remove" );
}
}
......@@ -118,13 +118,13 @@ void apply_context::require_authorization( const account_name& account )const {
wdump((act));
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}", ("account",account));
}
void apply_context::require_authorization(const account_name& account,
void apply_context::require_authorization(const account_name& account,
const permission_name& permission)const {
for( const auto& auth : act.authorization )
if( auth.actor == account ) {
if( auth.permission == permission ) return;
}
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}/${permission}",
EOS_ASSERT( false, tx_missing_auth, "missing authority of ${account}/${permission}",
("account",account)("permission",permission) );
}
......@@ -162,7 +162,7 @@ void apply_context::require_read_lock(const account_name& account, const scope_n
bool apply_context::has_recipient( account_name code )const {
for( auto a : _notified )
if( a == code )
if( a == code )
return true;
return false;
}
......@@ -304,17 +304,17 @@ void apply_context::update_db_usage( const account_name& payer, int64_t delta )
}
int apply_context::get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const
int apply_context::get_action( uint32_t type, uint32_t index, char* buffer, size_t buffer_size )const
{
const transaction& trx = trx_meta.trx();
const action* act = nullptr;
if( type == 0 ) {
if( index >= trx.context_free_actions.size() )
if( index >= trx.context_free_actions.size() )
return -1;
act = &trx.context_free_actions[index];
}
else if( type == 1 ) {
if( index >= trx.actions.size() )
if( index >= trx.actions.size() )
return -1;
act = &trx.actions[index];
}
......@@ -336,7 +336,7 @@ int apply_context::get_context_free_data( uint32_t index, char* buffer, size_t b
if( buffer_size < s )
memcpy( buffer, trx_meta.context_free_data.data(), buffer_size );
else
else
memcpy( buffer, trx_meta.context_free_data.data(), s );
return s;
......@@ -379,7 +379,7 @@ void apply_context::db_update_i64( int iterator, account_name payer, const char*
if( payer == account_name() ) payer = obj.payer;
if( account_name(obj.payer) == payer ) {
update_db_usage( obj.payer, buffer_size + 200 - old_size );
update_db_usage( obj.payer, buffer_size - old_size );
} else {
update_db_usage( obj.payer, -(old_size+200) );
update_db_usage( payer, (buffer_size+200) );
......@@ -404,94 +404,122 @@ void apply_context::db_remove_i64( int iterator ) {
});
mutable_db.remove( obj );
keyval_cache.remove( iterator, obj );
keyval_cache.remove( iterator );
}
int apply_context::db_get_i64( int iterator, char* buffer, size_t buffer_size ) {
const key_value_object& obj = keyval_cache.get( iterator );
memcpy( buffer, obj.value.data(), std::min(obj.value.size(), buffer_size) );
return obj.value.size();
}
int apply_context::db_next_i64( int iterator, uint64_t& primary ) {
const auto& obj = keyval_cache.get( iterator );
if( iterator < -1 ) return -1; // cannot increment past end iterator of table
const auto& obj = keyval_cache.get( iterator ); // Check for iterator != -1 happens in this call
const auto& idx = db.get_index<contracts::key_value_index, contracts::by_scope_primary>();
auto itr = idx.iterator_to( obj );
++itr;
if( itr == idx.end() ) return -1;
if( itr->t_id != obj.t_id ) return -1;
if( itr == idx.end() || itr->t_id != obj.t_id ) return keyval_cache.get_end_iterator_by_table_id(obj.t_id);
primary = itr->primary_key;
return keyval_cache.add( *itr );
}
int apply_context::db_previous_i64( int iterator, uint64_t& primary ) {
const auto& obj = keyval_cache.get(iterator);
const auto& idx = db.get_index<contracts::key_value_index, contracts::by_scope_primary>();
if( iterator < -1 ) // is end iterator
{
auto tab = keyval_cache.find_table_by_end_iterator(iterator);
FC_ASSERT( tab, "not a valid end iterator" );
auto itr = idx.upper_bound(tab->id);
if( itr == idx.begin() ) return -1; // Empty table
--itr;
if( itr->t_id != tab->id ) return -1; // Empty table
primary = itr->primary_key;
return keyval_cache.add(*itr);
}
const auto& obj = keyval_cache.get(iterator);
auto itr = idx.iterator_to(obj);
if (itr == idx.end() || itr == idx.begin()) return -1;
if( itr == idx.begin() ) return -1; // cannot decrement past beginning iterator of table
--itr;
if (itr->t_id != obj.t_id) return -1;
if( itr->t_id != obj.t_id ) return -1; // cannot decrement past beginning iterator of table
primary = itr->primary_key;
return keyval_cache.add(*itr);
}
int apply_context::db_find_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) {
require_read_lock( code, scope );
require_read_lock( code, scope ); // redundant?
const auto* tab = find_table( code, scope, table );
if( !tab ) return -1;
validate_table_key(*tab, contracts::table_key_type::type_i64);
auto table_end_itr = keyval_cache.cache_table( *tab );
const key_value_object* obj = db.find<key_value_object, contracts::by_scope_primary>( boost::make_tuple( tab->id, id ) );
if( !obj ) return -1;
if( !obj ) return table_end_itr;
keyval_cache.cache_table( *tab );
return keyval_cache.add( *obj );
}
int apply_context::db_lowerbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) {
require_read_lock( code, scope );
require_read_lock( code, scope ); // redundant?
const auto* tab = find_table( code, scope, table );
if( !tab ) return -1;
validate_table_key(*tab, contracts::table_key_type::type_i64);
auto table_end_itr = keyval_cache.cache_table( *tab );
const auto& idx = db.get_index<contracts::key_value_index, contracts::by_scope_primary>();
auto itr = idx.lower_bound( boost::make_tuple( tab->id, id ) );
if( itr == idx.end() ) return -1;
if( itr->t_id != tab->id ) return -1;
if( itr == idx.end() ) return table_end_itr;
if( itr->t_id != tab->id ) return table_end_itr;
keyval_cache.cache_table( *tab );
return keyval_cache.add( *itr );
}
int apply_context::db_upperbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) {
require_read_lock( code, scope );
require_read_lock( code, scope ); // redundant?
const auto* tab = find_table( code, scope, table );
if( !tab ) return -1;
validate_table_key(*tab, contracts::table_key_type::type_i64);
auto table_end_itr = keyval_cache.cache_table( *tab );
const auto& idx = db.get_index<contracts::key_value_index, contracts::by_scope_primary>();
auto itr = idx.upper_bound( boost::make_tuple( tab->id, id ) );
if( itr == idx.end() ) return -1;
if( itr->t_id != tab->id ) return -1;
if( itr == idx.end() ) return table_end_itr;
if( itr->t_id != tab->id ) return table_end_itr;
keyval_cache.cache_table( *tab );
return keyval_cache.add( *itr );
}
int apply_context::db_end_i64( uint64_t code, uint64_t scope, uint64_t table ) {
require_read_lock( code, scope ); // redundant?
const auto* tab = find_table( code, scope, table );
if( !tab ) return -1;
validate_table_key(*tab, contracts::table_key_type::type_i64);
return keyval_cache.cache_table( *tab );
}
template<>
contracts::table_key_type apply_context::get_key_type<contracts::key_value_object>() {
return contracts::table_key_type::type_i64;
......
......@@ -1048,6 +1048,7 @@ void chain_controller::_initialize_indexes() {
_db.add_index<contracts::key_value_index>();
_db.add_index<contracts::index64_index>();
_db.add_index<contracts::index128_index>();
_db.add_index<contracts::index256_index>();
_db.add_index<contracts::keystr_value_index>();
......
......@@ -9,6 +9,9 @@
#include <chainbase/chainbase.hpp>
#include <array>
#include <type_traits>
namespace eosio { namespace chain { namespace contracts {
enum table_key_type {
......@@ -58,7 +61,7 @@ namespace eosio { namespace chain { namespace contracts {
struct by_scope_secondary;
struct by_scope_tertiary;
struct key_value_object : public chainbase::object<key_value_object_type, key_value_object> {
OBJECT_CTOR(key_value_object, (value))
......@@ -126,12 +129,39 @@ namespace eosio { namespace chain { namespace contracts {
> index_index;
};
/*
template <typename T, typename = void>
struct _is_array : std::false_type {};
template <typename T>
struct _is_array<T, std::enable_if<std::is_same<size_t, decltype(SecondaryKey::arr_size)>::type> : std::true_type {};
template<SecondaryKey>
inline
typename std::enable_if<_is_array<SecondaryKey>::value, size_t>::type
get_key_memory_usage() {
return SecondaryKey::arr_size;
}
*/
template<typename SecondaryKey>
inline
//typename std::enable_if<!_is_array<SecondaryKey>::value, size_t>::type
size_t
get_key_memory_usage() {
return sizeof(SecondaryKey);
}
typedef secondary_index<uint64_t,index64_object_type>::index_object index64_object;
typedef secondary_index<uint64_t,index64_object_type>::index_index index64_index;
typedef secondary_index<uint128_t,index128_object_type>::index_object index128_object;
typedef secondary_index<uint128_t,index128_object_type>::index_index index128_index;
typedef std::array<uint128_t, 2> key256_t;
typedef secondary_index<key256_t,index256_object_type>::index_object index256_object;
typedef secondary_index<key256_t,index256_object_type>::index_index index256_index;
/*
struct index64_object : public chainbase::object<index64_object_type, index64_object> {
OBJECT_CTOR(index64_object)
......@@ -353,6 +383,7 @@ CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::key64x64x64_value_object, eosi
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::index64_object, eosio::chain::contracts::index64_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::index128_object, eosio::chain::contracts::index128_index)
CHAINBASE_SET_INDEX_TYPE(eosio::chain::contracts::index256_object, eosio::chain::contracts::index256_index)
FC_REFLECT(eosio::chain::contracts::table_id_object, (id)(code)(scope)(table)(key_type) )
FC_REFLECT(eosio::chain::contracts::key_value_object, (id)(t_id)(primary_key)(value)(payer) )
......
......@@ -28,7 +28,6 @@ using field_name = fixed_string16;
using table_name = name;
using action_name = eosio::chain::action_name;
struct type_def {
type_def() = default;
type_def(const type_name& new_type_name, const type_name& type)
......@@ -44,7 +43,7 @@ struct field_def {
field_def(const field_name& name, const type_name& type)
:name(name), type(type)
{}
field_name name;
type_name type;
......@@ -291,4 +290,3 @@ FC_REFLECT( eosio::chain::contracts::unlinkauth , (account
FC_REFLECT( eosio::chain::contracts::postrecovery , (account)(data)(memo) )
FC_REFLECT( eosio::chain::contracts::passrecovery , (account) )
FC_REFLECT( eosio::chain::contracts::vetorecovery , (account) )
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <array>
#include <algorithm>
#include <type_traits>
#include <fc/exception/exception.hpp>
namespace eosio {
template<size_t Size>
class fixed_key;
template<size_t Size>
bool operator==(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator!=(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
template<size_t Size>
bool operator<(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
/**
* @defgroup fixed_key fixed size key sorted lexicographically
* @ingroup types
* @{
*/
template<size_t Size>
class fixed_key {
private:
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same< bool_pack<bs..., true>, bool_pack<true, bs...> >;
public:
typedef uint128_t word_t;
static constexpr size_t num_words() { return (Size + sizeof(word_t) - 1) / sizeof(word_t); }
static constexpr size_t padded_bytes() { return num_words() * sizeof(word_t) - Size; }
/**
* @brief Default constructor to fixed_key object
*
* @details Default constructor to fixed_key object which initializes all bytes to zero
*/
fixed_key() : _data() {}
/**
* @brief Constructor to fixed_key object from array of num_words() words
*
* @details Constructor to fixed_key object from array of num_words() words
* @param arr data
*/
fixed_key(const word_t (&arr)[num_words()])
{
std::copy(arr, arr + num_words(), _data.begin());
}
/**
* @brief Constructor to fixed_key object from std::array of num_words() words
*
* @details Constructor to fixed_key object from std::array of num_words() words
* @param arr data
*/
fixed_key(const std::array<word_t, num_words()>& arr)
{
std::copy(arr.begin(), arr.end(), _data.begin());
}
#if 0
// Cannot infer constructor template arguments in C++ versions prior to C++17
/**
* @brief Constructor to fixed_key object from sequence of words supplied as arguments
*
* @details Constructor to fixed_key object from sequence of words (of small sizes allowed) supplied as arguments
*/
template<typename FirstWord, typename... Rest>
fixed_key(typename std::enable_if<std::is_integral<FirstWord>::value &&
!std::is_same<FirstWord, bool>::value &&
sizeof(FirstWord) <= sizeof(word_t) &&
all_true<(std::is_same<FirstWord, Rest>::value)...>::value,
FirstWord>::type first_word,
Rest... rest)
{
static_assert( sizeof(word_t) == (sizeof(word_t)/sizeof(FirstWord)) * sizeof(FirstWord),
"size of the backing word size is not divisble by the size of the words supplied as arguments" );
static_assert( sizeof(FirstWord) * (1 + sizeof...(Rest)) <= Size, "too many words supplied to fixed_key constructor" );
auto itr = _data.begin();
word_t temp_word = 0;
const size_t sub_word_shift = 8 * sizeof(FirstWord);
const size_t num_sub_words = sizeof(word_t) / sizeof(FirstWord);
auto sub_words_left = num_sub_words;
for( auto&& w : { first_word, rest... } ) {
if( sub_words_left > 1 ) {
temp_word |= static_cast<word_t>(w);
temp_word <<= sub_word_shift;
--sub_words_left;
continue;
}
FC_ASSERT( sub_words_left == 1, "unexpected error in fixed_key constructor" );
temp_word |= static_cast<word_t>(w);
sub_words_left = num_sub_words;
*itr = temp_word;
temp_word = 0;
++itr;
}
if( sub_words_left != num_sub_words ) {
if( sub_words_left > 1 )
temp_word <<= 8 * (sub_words_left-1);
*itr = temp_word;
}
}
#endif
template<typename FirstWord, typename... Rest>
static
fixed_key<Size>
make_from_word_sequence(typename std::enable_if<std::is_integral<FirstWord>::value &&
!std::is_same<FirstWord, bool>::value &&
sizeof(FirstWord) <= sizeof(word_t) &&
all_true<(std::is_same<FirstWord, Rest>::value)...>::value,
FirstWord>::type first_word,
Rest... rest)
{
static_assert( sizeof(word_t) == (sizeof(word_t)/sizeof(FirstWord)) * sizeof(FirstWord),
"size of the backing word size is not divisble by the size of the words supplied as arguments" );
static_assert( sizeof(FirstWord) * (1 + sizeof...(Rest)) <= Size, "too many words supplied to fixed_key constructor" );
fixed_key<Size> key;
auto itr = key._data.begin();
word_t temp_word = 0;
const size_t sub_word_shift = 8 * sizeof(FirstWord);
const size_t num_sub_words = sizeof(word_t) / sizeof(FirstWord);
auto sub_words_left = num_sub_words;
for( auto&& w : { first_word, rest... } ) {
if( sub_words_left > 1 ) {
temp_word |= static_cast<word_t>(w);
temp_word <<= sub_word_shift;
--sub_words_left;
continue;
}
FC_ASSERT( sub_words_left == 1, "unexpected error in fixed_key constructor" );
temp_word |= static_cast<word_t>(w);
sub_words_left = num_sub_words;
*itr = temp_word;
temp_word = 0;
++itr;
}
if( sub_words_left != num_sub_words ) {
if( sub_words_left > 1 )
temp_word <<= 8 * (sub_words_left-1);
*itr = temp_word;
}
return key;
}
const auto& get_array()const { return _data; }
auto data() { return _data.data(); }
auto data()const { return _data.data(); }
auto size()const { return _data.size(); }
std::array<uint8_t, Size> extract_as_byte_array()const {
std::array<uint8_t, Size> arr;
const size_t num_sub_words = sizeof(word_t);
auto arr_itr = arr.begin();
auto data_itr = _data.begin();
for( size_t counter = _data.size(); counter > 0; --counter, ++data_itr ) {
size_t sub_words_left = num_sub_words;
if( counter == 1 ) { // If last word in _data array...
sub_words_left -= padded_bytes();
}
auto temp_word = *data_itr;
for( ; sub_words_left > 0; --sub_words_left ) {
*(arr_itr + sub_words_left - 1) = static_cast<uint8_t>(temp_word & 0xFF);
temp_word >>= 8;
}
arr_itr += num_sub_words;
}
return arr;
}
// Comparison operators
friend bool operator== <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator!= <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator> <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
friend bool operator< <>(const fixed_key<Size> &c1, const fixed_key<Size> &c2);
private:
std::array<word_t, num_words()> _data;
};
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 == c2, return true, otherwise false
*/
template<size_t Size>
bool operator==(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data == c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 != c2, return true, otherwise false
*/
template<size_t Size>
bool operator!=(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data != c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 > c2, return true, otherwise false
*/
template<size_t Size>
bool operator>(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data > c2._data;
}
/**
* @brief Compares two fixed_key variables c1 and c2
*
* @details Lexicographically compares two fixed_key variables c1 and c2
* @return if c1 < c2, return true, otherwise false
*/
template<size_t Size>
bool operator<(const fixed_key<Size> &c1, const fixed_key<Size> &c2) {
return c1._data < c2._data;
}
/// @} fixed_key
typedef fixed_key<32> key256;
}
......@@ -122,6 +122,7 @@ namespace eosio { namespace chain {
key64x64_value_object_type,
index64_object_type,
index128_object_type,
index256_object_type,
action_permission_object_type,
global_property_object_type,
dynamic_global_property_object_type,
......@@ -164,7 +165,7 @@ namespace eosio { namespace chain {
using uint128_t = __uint128_t;
using bytes = vector<char>;
} } // eosio::chain
......@@ -180,6 +181,7 @@ FC_REFLECT_ENUM(eosio::chain::object_type,
(key64x64_value_object_type)
(index64_object_type)
(index128_object_type)
(index256_object_type)
(action_permission_object_type)
(global_property_object_type)
(dynamic_global_property_object_type)
......
此差异已折叠。
......@@ -107,6 +107,7 @@ class sha256
uint64_t hash64(const char* buf, size_t len);
} // fc
namespace std
{
template<>
......@@ -117,6 +118,7 @@ namespace std
return *((size_t*)&s);
}
};
}
namespace boost
......
......@@ -318,7 +318,8 @@ namespace eosio { namespace testing {
if (tbl) {
const auto *obj = db.find<contracts::key_value_object, contracts::by_scope_primary>(boost::make_tuple(tbl->id, asset_symbol.value()));
if (obj) {
fc::datastream<const char *> ds(obj->value.data(), obj->value.size());
//balance is the second field after symbol, so skip the symbol
fc::datastream<const char *> ds(obj->value.data()+sizeof(symbol), obj->value.size()-sizeof(symbol));
fc::raw::unpack(ds, result);
}
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -21,6 +21,9 @@
#include "test_wasts.hpp"
#include <array>
#include <utility>
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::chain::contracts;
......@@ -430,7 +433,7 @@ BOOST_FIXTURE_TEST_CASE( lotso_globals, tester ) try {
//add a few immutable ones for good measure
for(unsigned int i = 0; i < 10; ++i)
ss << "(global $g" << i+200 << " i32 (i32.const 0))";
set_code(N(globals),
string(ss.str() + ")")
.c_str());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册