[toc]
引入模块
use 0x1::aptos_hash;
use 0x1::error;
use 0x1::math64;
use 0x1::simple_map;
use 0x1::table_with_length;
use 0x1::type_info;
use 0x1::vector;结构体 Entry
SmartTable 条目包含键和值。
struct Entry<K, V> has copy, drop, store
Fields
hash: u64
key: K
value: V结构体 SmartTable
struct SmartTable<K, V> has store
Fields
buckets: table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>
num_buckets: u64
level: u8
size: u64
split_load_threshold: u8
target_bucket_size: u64常数
const ENOT_EMPTY: u64 = 3;不能销毁非空的哈希映射。
const ENOT_FOUND: u64 = 1;在智能表中未找到键。
const EALREADY_EXIST: u64 = 4;键已存在。
const EEXCEED_MAX_BUCKET_SIZE: u64 = 7;目标桶大小无效。
const EINVALID_LOAD_THRESHOLD_PERCENT: u64 = 5;触发分割的负载阈值百分比无效。
const EINVALID_TARGET_BUCKET_SIZE: u64 = 6;智能表容量必须大于0。
const EZERO_CAPACITY: u64 = 2;函数
new
创建一个具有默认配置的空 SmartTable。
public fun new<K: copy, drop, store, V: store>(): smart_table::SmartTable<K, V>实现
public fun new<K: copy + drop + store, V: store>(): SmartTable<K, V> {
new_with_config<K, V>(0, 0, 0)
}new_with_config
使用自定义配置创建一个空 SmartTable。num_initial_buckets: 初始化时桶的数量。0 表示使用默认值。split_load_threshold: 一旦达到,将触发分割的百分比数字。0 表示使用默认值。target_bucket_size: 每个桶的目标条目数,不保证。0 表示未设置,将由合约代码动态分配。
public fun new_with_config<K: copy, drop, store, V: store>(num_initial_buckets: u64, split_load_threshold: u8, target_bucket_size: u64): smart_table::SmartTable<K, V>实现
public fun new_with_config<K: copy + drop + store, V: store>(
num_initial_buckets: u64,
split_load_threshold: u8,
target_bucket_size: u64
): SmartTable<K, V> {
assert!(split_load_threshold <= 100, error::invalid_argument(EINVALID_LOAD_THRESHOLD_PERCENT));
let buckets = table_with_length::new();
table_with_length::add(&mut buckets, 0, vector::empty());
let table = SmartTable {
buckets,
num_buckets: 1,
level: 0,
size: 0,
// 默认的分割负载阈值是 75%。
split_load_threshold: if (split_load_threshold == 0) { 75 } else { split_load_threshold },
target_bucket_size,
};
// 默认的初始桶数是 2。
if (num_initial_buckets == 0) {
num_initial_buckets = 2;
};
while (num_initial_buckets > 1) {
num_initial_buckets = num_initial_buckets - 1;
split_one_bucket(&mut table);
};
table
}destroy_empty
销毁空表。如果不是空的,则中止。
public fun destroy_empty<K, V>(table: smart_table::SmartTable<K, V>)实现
public fun destroy_empty<K, V>(table: SmartTable<K, V>) {
assert!(table.size == 0, error::invalid_argument(ENOT_EMPTY));
let i = 0;
while (i < table.num_buckets) {
vector::destroy_empty(table_with_length::remove(&mut table.buckets, i));
i = i + 1;
};
let SmartTable { buckets, num_buckets: _, level: _, size: _, split_load_threshold: _, target_bucket_size: _ } = table;
table_with_length::destroy_empty(buckets);
}destroy
当 V 有 drop 时,完全销毁表。
public fun destroy<K: drop, V: drop>(table: smart_table::SmartTable<K, V>)实现
public fun destroy<K: drop, V: drop>(table: SmartTable<K, V>) {
clear(&mut table);
destroy_empty(table);
}clear
当 T 有 drop 时,完全清除表。
public fun clear<K: drop, V: drop>(table: &mut smart_table::SmartTable<K, V>)实现
public fun clear<K: drop, V: drop>(table: &mut SmartTable<K, V>) {
*table_with_length::borrow_mut(&mut table.buckets, 0) = vector::empty();
let i = 1;
while (i < table.num_buckets) {
table_with_length::remove(&mut table.buckets, i);
i = i + 1;
};
table.num_buckets = 1;
table.level = 0;
table.size = 0;
}add
在哈希映射中添加 (key, value) 对,如果当前负载因子超过阈值,可能会增加一个桶。注意它可能不会分割实际溢出的桶。相反,它由 num_buckets 和 level 决定。对于标准线性哈希算法,它被存储为一个变量,但这里的 num_buckets 可以利用。如果键已存在,则中止。注意:当触发桶分割时,此方法可能会偶尔消耗更多的 gas。
public fun add<K, V>(table: &mut smart_table::SmartTable<K, V>, key: K, value: V)实现
public fun add<K, V>(table: &mut SmartTable<K, V>, key: K, value: V) {
let hash = sip_hash_from_value(&key);
let index = bucket_index(table.level, table.num_buckets, hash);
let bucket = table_with_length::borrow_mut(&mut table.buckets, index);
// 我们在这里为每个桶设置了一个上限限制,一个上限(10000),通常没人会达到。
assert!(vector::length(bucket) <= 10000, error::permission_denied(EEXCEED_MAX_BUCKET_SIZE));
assert!(vector::all(bucket, | entry | {
let e: &Entry<K, V> = entry;
&e.key != &key
}), error::invalid_argument(EALREADY_EXIST));
let e = Entry { hash, key, value };
if (table.target_bucket_size == 0) {
let estimated_entry_size = max(size_of_val(&e), 1);
table.target_bucket_size = max(1024 /* free_write_quota */ / estimated_entry_size, 1);
};
vector::push_back(bucket, e);
table.size = table.size + 1;
if (load_factor(table) >= (table.split_load_threshold as u64)) {
split_one_bucket(table);
}
}add_all
将多个键/值对添加到智能表中。键不能已经存在。
public fun add_all<K, V>(table: &mut smart_table::SmartTable<K, V>, keys: vector<K>, values: vector<V>)实现
public fun add_all<K, V>(table: &mut SmartTable<K, V>, keys: vector<K>, values: vector<V>) {
vector::zip(keys, values, |key, value| { add(table, key, value); });
}unzip_entries
fun unzip_entries<K: copy, V: copy>(entries: &vector<smart_table::Entry<K, V>>): (vector<K>, vector<V>)实现
inline fun unzip_entries<K: copy, V: copy>(entries: &vector<Entry<K, V>>): (vector<K>, vector<V>) {
let keys = vector[];
let values = vector[];
vector::for_each_ref(entries, |e|{
let entry: &Entry<K, V> = e;
vector::push_back(&mut keys, entry.key);
vector::push_back(&mut values, entry.value);
});
(keys, values)
}to_simple_map
将智能表转换为 simple_map,这通常应该由视图函数调用,以获得整个表的原子视图。免责声明:这个函数可能成本很高,因为智能表的大小可能非常庞大。请自行斟酌使用。
public fun to_simple_map<K: copy, drop, store, V: copy, store>(table: &smart_table::SmartTable<K, V>): simple_map::SimpleMap<K, V>实现
public fun to_simple_map<K: store + copy + drop, V: store + copy>(
table: &SmartTable<K, V>,
): SimpleMap<K, V> {
let i = 0;
let res = simple_map::new<K, V>();
while (i < table.num_buckets) {
let (keys, values) = unzip_entries(table_with_length::borrow(&table.buckets, i));
simple_map::add_all(&mut res, keys, values);
i = i + 1;
};
res
}split_one_bucket
决定下一个要分割的桶,并将其分割为两个,桶内的元素也一并分割。
fun split_one_bucket<K, V>(table: &mut smart_table::SmartTable<K, V>)实现
fun split_one_bucket<K, V>(table: &mut SmartTable<K, V>) {
let new_bucket_index = table.num_buckets;
// 下一个要分割的桶是 num_bucket 没有最高有效位。
let to_split = new_bucket_index ^ (1 << table.level);
table.num_buckets = new_bucket_index + 1;
// 如果整个级别已经分割过一次,那么提升级别。
if (to_split + 1 == 1 << table.level) {
table.level = table.level + 1;
};
let old_bucket = table_with_length::borrow_mut(&mut table.buckets, to_split);
// 分割桶,[0..p) 保留在旧桶中,[p..len) 移至新桶
let p = vector::partition(old_bucket, |e| {
let entry: &Entry<K, V> = e; // 显式类型以满足编译器
bucket_index(table.level, table.num_buckets, entry.hash) != new_bucket_index
});
let new_bucket = vector::trim_reverse(old_bucket, p);
table_with_length::add(&mut table.buckets, new_bucket_index, new_bucket);
}bucket_index
返回预期的桶索引以找到哈希。基本上,它根据目标桶索引与下一个要分割的桶的索引的比较,使用不同的基数 1 << level 与 1 << (level + 1) 在模运算中。
fun bucket_index(level: u8, num_buckets: u64, hash: u64): u64实现
fun bucket_index(level: u8, num_buckets: u64, hash: u64): u64 {
let index = hash % (1 << (level + 1));
if (index < num_buckets) {
// 在现有的桶中
index
} else {
// 在未分割的桶中
index % (1 << level)
}
}borrow
获取 key 映射到的 value 的不可变引用。如果没有 key 的条目,则中止。
public fun borrow<K: drop, V>(table: &smart_table::SmartTable<K, V>, key: K): &V实现
public fun borrow<K: drop, V>(table: &SmartTable<K, V>, key: K): &V {
let index = bucket_index(table.level, table.num_buckets, sip_hash_from_value(&key));
let bucket = table_with_length::borrow(&table.buckets, index);
let i = 0;
let len = vector::length(bucket);
while (i < len) {
let entry = vector::borrow(bucket, i);
if (&entry.key == &key) {
return &entry.value
};
i = i + 1;
};
abort error::invalid_argument(ENOT_FOUND)
}borrow_with_default
获取 key 映射到的值的不可变引用。如果没有键的条目,则返回指定的默认值。
public fun borrow_with_default<K: copy, drop, V>(table: &smart_table::SmartTable<K, V>, key: K, default: &V): &V实现
public fun borrow_with_default<K: copy + drop, V>(table: &SmartTable<K, V>, key: K, default: &V): &V {
if (!contains(table, copy key)) {
default
} else {
borrow(table, copy key)
}
}borrow_mut
获取 key 映射到的值的可变引用。如果没有键的条目,则中止。
public fun borrow_mut<K: drop, V>(table: &mut smart_table::SmartTable<K, V>, key: K): &mut V实现
public fun borrow_mut<K: drop, V>(table: &mut SmartTable<K, V>, key: K): &mut V {
let index = bucket_index(table.level, table.num_buckets, sip_hash_from_value(&key));
let bucket = table_with_length::borrow_mut(&mut table.buckets, index);
let i = 0;
let len = vector::length(bucket);
while (i < len) {
let entry = vector::borrow_mut(bucket, i);
if (&entry.key == &key) {
return &mut entry.value
};
i = i + 1;
};
abort error::invalid_argument(ENOT_FOUND)
}borrow_mut_with_default
获取 key 映射到的值的可变引用。如果没有键的条目,则首先插入 (key, default) 对。
public fun borrow_mut_with_default<K: copy, drop, V: drop>(table: &mut smart_table::SmartTable<K, V>, key: K, default: V): &mut V实现
public fun borrow_mut_with_default<K: copy + drop, V: drop>(
table: &mut SmartTable<K, V>,
key: K,
default: V
): &mut V {
if (!contains(table, copy key)) {
add(table, copy key, default)
};
borrow_mut(table, key)
}contains
如果表包含键的条目,则返回 true。
public fun contains<K: drop, V>(table: &smart_table::SmartTable<K, V>, key: K): bool实现
public fun contains<K: drop, V>(table: &SmartTable<K, V>, key: K): bool {
let hash = sip_hash_from_value(&key);
let index = bucket_index(table.level, table.num_buckets, hash);
let bucket = table_with_length::borrow(&table.buckets, index);
vector::any(bucket, | entry | {
let e: &Entry<K, V> = entry;
e.hash == hash && &e.key == &key
})
}remove
从表中移除并返回 key 映射到的值。如果没有键的条目,则中止。
public fun remove<K: copy, drop, V>(table: &mut smart_table::SmartTable<K, V>, key: K): V实现
public fun remove<K: copy + drop, V>(table: &mut SmartTable<K, V>, key: K): V {
let index = bucket_index(table.level, table.num_buckets, sip_hash_from_value(&key));
let bucket = table_with_length::borrow_mut(&mut table.buckets, index);
let i = 0;
let len = vector::length(bucket);
while (i < len) {
let entry = vector::borrow(bucket, i);
if (&entry.key == &key) {
let Entry { hash: _, key: _, value } = vector::swap_remove(bucket, i);
table.size = table.size - 1;
return value
};
i = i + 1;
};
abort error::invalid_argument(ENOT_FOUND)
}upsert
如果键没有条目,则插入 (key, value) 对。否则,将键的条目值更新为 value。
public fun upsert<K: copy, drop , V: drop>(table: &mut smart_table::SmartTable<K, V>, key: K, value: V)实现
public fun upsert<K: copy + drop, V: drop>(table: &mut SmartTable<K, V>, key: K, value: V) {
if (!contains(table, copy key)) {
add(table, copy key, value)
} else {
let ref = borrow_mut(table, key);
*ref = value;
};
}length
返回表的长度,即条目数。
public fun length<K, V>(table: &smart_table::SmartTable<K, V>): u64实现
public fun length<K, V>(table: &SmartTable<K, V>): u64 {
table.size
}load_factor
返回哈希表的负载因子。
public fun load_factor<K, V>(table: &smart_table::SmartTable<K, V>): u64实现
public fun load_factor<K, V>(table: &SmartTable<K, V>): u64 {
table.size * 100 / table.num_buckets / table.target_bucket_size
}update_split_load_threshold
更新 split_load_threshold。
public fun update_split_load_threshold<K, V>(table: &mut smart_table::SmartTable<K, V>, split_load_threshold: u8)实现
public fun update_split_load_threshold<K, V>(table: &mut SmartTable<K, V>, split_load_threshold: u8) {
assert!(
split_load_threshold <= 100 && split_load_threshold > 0,
error::invalid_argument(EINVALID_LOAD_THRESHOLD_PERCENT)
);
table.split_load_threshold = split_load_threshold;
}update_target_bucket_size
更新 target_bucket_size。
public fun update_target_bucket_size<K, V>(table: &mut smart_table::SmartTable<K, V>, target_bucket_size: u64)实现
public fun update_target_bucket_size<K, V>(table: &mut SmartTable<K, V>, target_bucket_size: u64) {
assert!(target_bucket_size > 0, error::invalid_argument(EINVALID_TARGET_BUCKET_SIZE));
table.target_bucket_size = target_bucket_size;
}for_each_ref
对表中的每个键值对的引用应用函数。
public fun for_each_ref<K, V>(table: &smart_table::SmartTable<K, V>, f: |(&K, &V)|())实现
public inline fun for_each_ref<K, V>(table: &SmartTable<K, V>, f: |&K, &V|) {
let i = 0;
while (i < aptos_std::smart_table::num_buckets(table)) {
vector::for_each_ref(
aptos_std::table_with_length::borrow(aptos_std::smart_table::borrow_buckets(table), i),
|elem| {
let (key, value) = aptos_std::smart_table::borrow_kv(elem);
f(key, value)
}
);
i = i + 1;
}
}for_each_mut
对表中每个键值对的可变引用应用函数。
public fun for_each_mut<K, V>(table: &mut smart_table::SmartTable<K, V>, f: |(&K, &mut V)|())实现
public inline fun for_each_mut<K, V>(table: &mut SmartTable<K, V>, f: |&K, &mut V|) {
let i = 0;
while (i < aptos_std::smart_table::num_buckets(table)) {
vector::for_each_mut(
table_with_length::borrow_mut(aptos_std::smart_table::borrow_buckets_mut(table), i),
|elem| {
let (key, value) = aptos_std::smart_table::borrow_kv_mut(elem);
f(key, value)
}
);
i = i + 1;
};
}map_ref
在不修改表的情况下,对表中的键值对引用应用函数。
public fun map_ref<K: copy, drop, store, V1, V2: store>(table: &smart_table::SmartTable<K, V1>, f: |&V1|V2): smart_table::SmartTable<K, V2>实现
public inline fun map_ref<K: copy + drop + store, V1, V2: store>(
table: &SmartTable<K, V1>,
f: |&V1|V2
): SmartTable<K, V2> {
let new_table = new<K, V2>();
for_each_ref(table, |key, value| add(&mut new_table, *key, f(value)));
new_table
}any
如果表中的任何键值对满足条件,则返回 true。
public fun any<K, V>(table: &smart_table::SmartTable<K, V>, p: |(&K, &V)|bool): bool实现
public inline fun any<K, V>(
table: &SmartTable<K, V>,
p: |&K, &V|bool
): bool {
let found = false;
let i = 0;
while (i < aptos_std::smart_table::num_buckets(table)) {
found = vector::any(table_with_length::borrow(aptos_std::smart_table::borrow_buckets(table), i), |elem| {
let (key, value) = aptos_std::smart_table::borrow_kv(elem);
p(key, value)
});
if (found) break;
i = i + 1;
};
found
}borrow_kv
辅助函数用于规避内联函数的作用域问题。
public fun borrow_kv<K, V>(e: &smart_table::Entry<K, V>): (&K, &V)实现
public fun borrow_kv<K, V>(e: &Entry<K, V>): (&K, &V) {
(&e.key, &e.value)
}borrow_kv_mut
public fun borrow_kv_mut<K, V>(e: &mut smart_table::Entry<K, V>): (&mut K, &mut V)实现
public fun borrow_kv_mut<K, V>(e: &mut Entry<K, V>): (&mut K, &mut V) {
(&mut e.key, &mut e.value)
}num_buckets
public fun num_buckets<K, V>(table: &smart_table::SmartTable<K, V>): u64实现
public fun num_buckets<K, V>(table: &SmartTable<K, V>): u64 {
table.num_buckets
}borrow_buckets
public fun borrow_buckets<K, V>(table: &smart_table::SmartTable<K, V>): &table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>实现
public fun borrow_buckets<K, V>(table: &SmartTable<K, V>): &TableWithLength<u64, vector<Entry<K, V>>> {
&table.buckets
}borrow_buckets_mut
public fun borrow_buckets_mut<K, V>(table: &mut smart_table::SmartTable<K, V>): &mut table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>实现
public fun borrow_buckets_mut<K, V>(table: &mut SmartTable<K, V>): &mut TableWithLength<u64, vector<Entry<K, V>>> {
&mut table.buckets
}规范(Specification)
结构体 SmartTable
struct SmartTable<K, V> has store
buckets: table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>
num_buckets: u64
level: u8
size: u64
split_load_threshold: u8
target_bucket_size: u64函数 new_with_config
public fun new_with_config<K: copy, drop, store, V: store>(num_initial_buckets: u64, split_load_threshold: u8, target_bucket_size: u64): smart_table::SmartTable<K, V>pragma verify = false;
函数 destroy
public fun destroy<K: drop, V: drop>(table: smart_table::SmartTable<K, V>)pragma verify = false;
函数 clear
public fun clear<K: drop, V: drop>(table: &mut smart_table::SmartTable<K, V>)pragma verify = false;
函数 add_all
public fun add_all<K, V>(table: &mut smart_table::SmartTable<K, V>, keys: vector<K>, values: vector<V>)pragma verify = false;
函数 to_simple_map
public fun to_simple_map<K: copy, drop, store, V: copy, store>(table: &smart_table::SmartTable<K, V>): simple_map::SimpleMap<K, V>pragma verify = false;
函数 split_one_bucket
fun split_one_bucket<K, V>(table: &mut smart_table::SmartTable<K, V>)pragma verify = false;
函数 bucket_index
fun bucket_index(level: u8, num_buckets: u64, hash: u64): u64pragma verify = false;
函数 borrow_with_default
public fun borrow_with_default<K: copy, drop, V>(table: &smart_table::SmartTable<K, V>, key: K, default: &V): &Vpragma verify = false;
函数 load_factor
public fun load_factor<K, V>(table: &smart_table::SmartTable<K, V>): u64pragma verify = false;
函数 update_split_load_threshold
public fun update_split_load_threshold<K, V>(table: &mut smart_table::SmartTable<K, V>, split_load_threshold: u8)pragma verify = false;
函数 update_target_bucket_size
public fun update_target_bucket_size<K, V>(table: &mut smart_table::SmartTable<K, V>, target_bucket_size: u64)pragma verify = false;
函数 borrow_kv
public fun borrow_kv<K, V>(e: &smart_table::Entry<K, V>): (&K, &V)pragma verify = false;
函数 borrow_kv_mut
public fun borrow_kv_mut<K, V>(e: &mut smart_table::Entry<K, V>): (&mut K, &mut V)pragma verify = false;
函数 num_buckets
public fun num_buckets<K, V>(table: &smart_table::SmartTable<K, V>): u64pragma verify = false;
函数 borrow_buckets
public fun borrow_buckets<K, V>(table: &smart_table::SmartTable<K, V>): &table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>pragma verify = false;
函数 borrow_buckets_mut
public fun borrow_buckets_mut<K, V>(table: &mut smart_table::SmartTable<K, V>): &mut table_with_length::TableWithLength<u64, vector<smart_table::Entry<K, V>>>pragma verify = false;
原生函数(native functions)
native fun spec_len<K, V>(t: SmartTable<K, V>): num;
native fun spec_contains<K, V>(t: SmartTable<K, V>, k: K): bool;
native fun spec_set<K, V>(t: SmartTable<K, V>, k: K, v: V): SmartTable<K, V>;
native fun spec_remove<K, V>(t: SmartTable<K, V>, k: K): SmartTable<K, V>;
native fun spec_get<K, V>(t: SmartTable<K, V>, k: K): V;