## 背景
我们Datona Labs在开发和测试Solidity数据访问合约(S-DAC:Smart-Data-Access-Contract)模板过程中,经常需要使用只有很小数值的小数组(数组元素个数少)。在本示例中,研究了使用值数组(Value Array)是否比引用数组(Reference Array)更高效。
## 讨论
Solidity支持内存(memory)中的分配数组,这些数组会很浪费空间(参考 [文档](https://learnblockchain.cn/docs/solidity/types.html#arrays)),而存储(*storage*)中的数组则会消耗大量的gas来分配和访问存储。但是Solidity所运行的[以太坊虚拟机(EVM)](https://learnblockchain.cn/2019/04/09/easy-evm)有一个256位(32字节)机器字长。正是后一个特性使我们能够考虑使用值数组(Value Array)。在机器字长的语言中,例如32位(4字节),值数组(Value Array)不太可能实用。
我们可以使用值数组(Value Array)减少存储空间和gas消耗吗?
> 译者注:机器字长 是指每一个指令处理的数据长度。
## 比较值数组与引用数组
### 引用数组(Reference Array)
在 Solidity 中,数组通常是引用类型。这意味着每当在程序中遇到变量符号时,都会使用指向数组的指针,不过也有一些例外情况会生成一个拷贝(参考[文档-引用类型](https://learnblockchain.cn/docs/solidity/types.html#reference-types))。在以下代码中,将10个元素的 8位uint `users` 的数组传递给`setUser`函数,该函数设置users数组中的一个元素:
“`js
contract TestReferenceArray {
function test() public pure {
uint8[10] memory users;
setUser(users, 5, 123);
require(users[5] == 123);
}
function setUser(uint8[10] memory users, uint index, uint8 ev)
public pure {
users[index] = ev;
}
}
“`
函数返回后,`users`数组元素将被更改。
### 值数组(Value Arrays)
值数组是以[值类型](https://learnblockchain.cn/docs/solidity/types.html#value-types)保存的数组。这意味着在程序中遇到变量符号,就会使用其值。
“`javascript
contract TestValueArray {
function test() public pure {
uint users;
users = setUser(users, 5, 12345);
require(users == …);
}
function setUser(uint users, uint index, uint ev) public pure
returns (uint) {
return …;
}
}
“`
请注意,在函数返回之后,函数的users参数将保持不变,因为它是通过值传递的,为了获得更改后的值,需要将函数返回值赋值给users变量。
### Solidity bytes32 值数组
Solidity 在 bytesX(X=1..32)类型中提供了一个部分值数组。这些字节元素可以使用数组方式访问单独读取,例如:
“`
…
bytes32 bs = “hello”;
byte b = bs[0];
require(bs[0] == ‘h’);
…
“`
但不幸的是,在[Solidity 目前的版本](https://learnblockchain.cn/docs/solidity/types.html#index-7)中,我们无法使用数组访问方式写入某个字节:
“`
…
bytes32 bs = “hello”;
bs[0] = ‘c’; // 不可以实现
…
“`
让我们使用Solidity的 [using for](https://learnblockchain.cn/docs/solidity/contracts.html#using-for) 导入库的方式为bytes32类型添加新能力:
“`js
library bytes32lib {
uint constant bits = 8;
uint constant elements = 32;
function set(bytes32 va, uint index, byte ev) internal pure
returns (bytes32) {
require(index < elements);
index = (elements – 1 – index) * bits;
return bytes32((uint(va) & ~(0x0FF << index)) |
(uint(uint8(ev)) << index));
}
}
“`
这个库提供了set()函数,它允许调用者将bytes32变量中的任何字节设置为想要的字节值。根据你的需求,你可能希望为你使用的其他bytesX类型生成类似的库。
### 测试一把
让我们导入该库并测试它:
“`javascript
import “bytes32lib.sol”;
contract TestBytes32 {
using bytes32lib for bytes32;
function test1() public pure {
bytes32 va = “hello”;
require(va[0] == ‘h’);
// 类似 va[0] = ‘c’; 的功能
va = va.set(0, ‘c’);
require(va[0] == ‘c’);
}
}
“`
在这里,你可以清楚地看到set()函数的返回值被分配回参数变量。如果缺少赋值,则变量将保持不变,require()就是来验证它。
## 可能的固定长度值数组
在Solidity机器字长为256位(32字节),我们可以考虑以下可能的值数组。
### 固定长度值数组
这些是以些Solidity[可用整型](https://learnblockchain.cn/docs/solidity/types.html#integers)匹配的固定长度的值数组:
“`
固定长度值数组
类型 类型名 描述
uint128[2] uint128a2 2个128位元素的值数组
uint64[4] uint64a4 4个64位元素的值数组
uint32[8] uint32a8 8个32位元素的值数组
uint16[16] uint16a16 16个16位…
- 原文地址:https://medium.com/coinmonks/value-arrays-in-solidity-32ca65135d5b
- 译文出自:区块链开发网翻译计划
- 译者:Tiny熊
- 本文永久链接:learnblockchain.cn/article…
背景
我们Datona Labs在开发和测试Solidity数据访问合约(S-DAC:Smart-Data-Access-Contract)模板过程中,经常需要使用只有很小数值的小数组(数组元素个数少)。在本示例中,研究了使用值数组(Value Array)是否比引用数组(Reference Array)更高效。
讨论
Solidity支持内存(memory)中的分配数组,这些数组会很浪费空间(参考 文档),而存储(storage)中的数组则会消耗大量的gas来分配和访问存储。但是Solidity所运行的以太坊虚拟机(EVM)有一个256位(32字节)机器字长。正是后一个特性使我们能够考虑使用值数组(Value Array)。在机器字长的语言中,例如32位(4字节),值数组(Value Array)不太可能实用。
我们可以使用值数组(Value Array)减少存储空间和gas消耗吗?
译者注:机器字长 是指每一个指令处理的数据长度。
比较值数组与引用数组
引用数组(Reference Array)
在 Solidity 中,数组通常是引用类型。这意味着每当在程序中遇到变量符号时,都会使用指向数组的指针,不过也有一些例外情况会生成一个拷贝(参考文档-引用类型)。在以下代码中,将10个元素的 8位uint users
的数组传递给setUser
函数,该函数设置users数组中的一个元素:
contract TestReferenceArray {
function test() public pure {
uint8[10] memory users;
setUser(users, 5, 123);
require(users[5] == 123);
}
function setUser(uint8[10] memory users, uint index, uint8 ev)
public pure {
users[index] = ev;
}
}
函数返回后,users
数组元素将被更改。
值数组(Value Arrays)
值数组是以值类型保存的数组。这意味着在程序中遇到变量符号,就会使用其值。
contract TestValueArray {
function test() public pure {
uint users;
users = setUser(users, 5, 12345);
require(users == ...);
}
function setUser(uint users, uint index, uint ev) public pure
returns (uint) {
return ...;
}
}
请注意,在函数返回之后,函数的users参数将保持不变,因为它是通过值传递的,为了获得更改后的值,需要将函数返回值赋值给users变量。
Solidity bytes32 值数组
Solidity 在 bytesX(X=1..32)类型中提供了一个部分值数组。这些字节元素可以使用数组方式访问单独读取,例如:
...
bytes32 bs = "hello";
byte b = bs[0];
require(bs[0] == 'h');
...
但不幸的是,在Solidity 目前的版本中,我们无法使用数组访问方式写入某个字节:
...
bytes32 bs = "hello";
bs[0] = 'c'; // 不可以实现
...
让我们使用Solidity的 using for 导入库的方式为bytes32类型添加新能力:
library bytes32lib {
uint constant bits = 8;
uint constant elements = 32;
function set(bytes32 va, uint index, byte ev) internal pure
returns (bytes32) {
require(index < elements);
index = (elements - 1 - index) * bits;
return bytes32((uint(va) & ~(0x0FF << index)) |
(uint(uint8(ev)) << index));
}
}
这个库提供了set()函数,它允许调用者将bytes32变量中的任何字节设置为想要的字节值。根据你的需求,你可能希望为你使用的其他bytesX类型生成类似的库。
测试一把
让我们导入该库并测试它:
import "bytes32lib.sol";
contract TestBytes32 {
using bytes32lib for bytes32;
function test1() public pure {
bytes32 va = "hello";
require(va[0] == 'h');
// 类似 va[0] = 'c'; 的功能
va = va.set(0, 'c');
require(va[0] == 'c');
}
}
在这里,你可以清楚地看到set()函数的返回值被分配回参数变量。如果缺少赋值,则变量将保持不变,require()就是来验证它。
可能的固定长度值数组
在Solidity机器字长为256位(32字节),我们可以考虑以下可能的值数组。
固定长度值数组
这些是以些Solidity可用整型匹配的固定长度的值数组:
固定长度值数组
类型 类型名 描述
uint128[2] uint128a2 2个128位元素的值数组
uint64[4] uint64a4 4个64位元素的值数组
uint32[8] uint32a8 8个32位元素的值数组
uint16[16] uint16a16 16个16位...
剩余50%的内容订阅专栏后可查看
- 发表于 2020-08-20 18:53
- 阅读 ( 3088 )
- 学分 ( 257 )
- 分类:Solidity
- 专栏:全面掌握Solidity智能合约开发