Michael.W基于Foundry精读Openzeppelin第20期——EnumerableMap.sol

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 EnumerableMap.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/utils/structs/EnumerableMap.sol

EnumerableMap库提供了Bytes32ToBytes32Map、UintToUintMap、UintToAddressMap、AddressToUintMap和Bytes32ToUintMap五种可迭代元素的map,分别适用于(bytes32, bytes32)、(uint256, uint256)、(uint256, address)、(address, uint256)和(bytes32, uint256)类型的键值对。每种map都提供了增添/更新/删除键值对及由键查值等操作。所有操作的时间复杂度均为O(1)。

1. 目标合约

封装EnumerableMap library成为一个可调用合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/utils/structs/MockEnumerableMap.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol";

contract MockBytes32ToBytes32Map {
    using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map;

    EnumerableMap.Bytes32ToBytes32Map _bytes32ToBytes32Map;

    function set(bytes32 key, bytes32 value) external returns (bool) {
        return _bytes32ToBytes32Map.set(key, value);
    }

    function remove(bytes32 key) external returns (bool) {
        return _bytes32ToBytes32Map.remove(key);
    }

    function contains(bytes32 key) external view returns (bool) {
        return _bytes32ToBytes32Map.contains(key);
    }

    function length() external view returns (uint) {
        return _bytes32ToBytes32Map.length();
    }

    function at(uint index) external view returns (bytes32, bytes32) {
        return _bytes32ToBytes32Map.at(index);
    }

    function tryGet(bytes32 key) external view returns (bool, bytes32){
        return _bytes32ToBytes32Map.tryGet(key);
    }

    function get(bytes32 key) external view returns (bytes32) {
        return _bytes32ToBytes32Map.get(key);
    }

    function get(bytes32 key, string memory errorMessage) external view returns (bytes32) {
        return _bytes32ToBytes32Map.get(key, errorMessage);
    }
}

contract MockUintToUintMap {
    using EnumerableMap for EnumerableMap.UintToUintMap;

    EnumerableMap.UintToUintMap _uintToUintMap;

    function set(uint key, uint value) external returns (bool) {
        return _uintToUintMap.set(key, value);
    }

    function remove(uint key) external returns (bool) {
        return _uintToUintMap.remove(key);
    }

    function contains(uint key) external view returns (bool) {
        return _uintToUintMap.contains(key);
    }

    function length() external view returns (uint) {
        return _uintToUintMap.length();
    }

    function at(uint index) external view returns (uint, uint) {
        return _uintToUintMap.at(index);
    }

    function tryGet(uint key) external view returns (bool, uint){
        return _uintToUintMap.tryGet(key);
    }

    function get(uint key) external view returns (uint) {
        return _uintToUintMap.get(key);
    }

    function get(uint key, string memory errorMessage) external view returns (uint) {
        return _uintToUintMap.get(key, errorMessage);
    }
}

contract MockUintToAddressMap {
    using EnumerableMap for EnumerableMap.UintToAddressMap;

    EnumerableMap.UintToAddressMap _uintToAddressMap;

    function set(uint key, address value) external returns (bool) {
        return _uintToAddressMap.set(key, value);
    }

    function remove(uint key) external returns (bool) {
        return _uintToAddressMap.remove(key);
    }

    function contains(uint key) external view returns (bool) {
        return _uintToAddressMap.contains(key);
    }

    function length() external view returns (uint) {
        return _uintToAddressMap.length();
    }

    function at(uint index) external view returns (uint, address) {
        return _uintToAddressMap.at(index);
    }

    function tryGet(uint key) external view returns (bool, address){
        return _uintToAddressMap.tryGet(key);
    }

    function get(uint key) external view returns (address) {
        return _uintToAddressMap.get(key);
    }

    function get(uint key, string memory errorMessage) external view returns (address) {
        return _uintToAddressMap.get(key, errorMessage);
    }
}

contract MockAddressToUintMap {
    using EnumerableMap for EnumerableMap.AddressToUintMap;

    EnumerableMap.AddressToUintMap _addressToUintMap;

    function set(address key, uint value) external returns (bool) {
        return _addressToUintMap.set(key, value);
    }

    function remove(address key) external returns (bool) {
        return _addressToUintMap.remove(key);
    }

    function contains(address key) external view returns (bool) {
        return _addressToUintMap.contains(key);
    }

    function length() external view returns (uint) {
        return _addressToUintMap.length();
    }

    function at(uint index) external view returns (address, uint) {
        return _addressToUintMap.at(index);
    }

    function tryGet(address key) external view returns (bool, uint){
        return _addressToUintMap.tryGet(key);
    }

    function get(address key) external view returns (uint) {
        return _addressToUintMap.get(key);
    }

    function get(address key, string memory errorMessage) external view returns (uint) {
        return _addressToUintMap.get(key, errorMessage);
    }
}

contract MockBytes32ToUintMap {
    using EnumerableMap for EnumerableMap.Bytes32ToUintMap;

    EnumerableMap.Bytes32ToUintMap _bytes32ToUintMap;

    function set(bytes32 key, uint value) external returns (bool) {
        return _bytes32ToUintMap.set(key, value);
    }

    function remove(bytes32 key) external returns (bool) {
        return _bytes32ToUintMap.remove(key);
    }

    function contains(bytes32 key) external view returns (bool) {
        return _bytes32ToUintMap.contains(key);
    }

    function length() external view returns (uint) {
        return _bytes32ToUintMap.length();
    }

    function at(uint index) external view returns (bytes32, uint) {
        return _bytes32ToUintMap.at(index);
    }

    function tryGet(bytes32 key) external view returns (bool, uint){
        return _bytes32ToUintMap.tryGet(key);
    }

    function get(bytes32 key) external view returns (uint) {
        return _bytes32ToUintMap.get(key);
    }

    function get(bytes32 key, string memory errorMessage) external view returns (uint) {
        return _bytes32ToUintMap.get(key, errorMessage);
    }
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/utils/structs/EnumerableMap.t.sol

2. 代码精读

2.1 Bytes32ToBytes32Map体系

结构体Bytes32ToBytes32Map是由一个存储key值的EnumerableSet.Bytes32Set和一个存储value值的mapping(bytes32 => bytes32)构成:

		// key: bytes32类型 value:bytes32类型
    struct Bytes32ToBytes32Map {
        // 利用EnumerableSet.Bytes32Set来存储全部keys
        // ps:EnumerableSet库详解见:https://learnblockchain.cn/article/6272
        EnumerableSet.Bytes32Set _keys;
        // 通过key来映射value的mapping
        mapping(bytes32 => bytes32) _values;
    }
2.1.1 set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) && remove(Bytes32ToBytes32Map storage map, bytes32 key)
  • set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value):增添或更新键值对。如果输入key为已存在key(为更新键值对),返回false;如果输入key不存在(为新增键值对),返回true。时间复杂度为O(1);

  • remove(Bytes32ToBytes32Map storage map, bytes32 key):删除键值对。如果输入key为已存在key(删除键值对),返回true;如果输入key不存在,返回false。时间复杂度为O(1)。

    function set(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        bytes32 value
    ) internal returns (bool) {
    	// Bytes32ToBytes32Map._values中更新key-value
        map._values[key] = value;
        //调用EnumerableSet库中对应的add()方法,在EnumerableSet.Bytes32Set中增添key。如果该key已存在于set中,返回false。否则返回true
        return map._keys.add(key);
    }

    function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
        // Bytes32ToBytes32Map._values中删除key-value
        delete map._values[key];
        // 调用EnumerableSet库中对应的remove()方法,从EnumerableSet.Bytes32Set中删除key。如果该key已存在于set中,返回true。否则返回false
        return map._keys.remove(key);
    }
2.1.2 contains(Bytes32ToBytes32Map storage map, bytes32 key) && length(Bytes32ToBytes32Map storage map) && at(Bytes32ToBytes32Map storage map, uint256 index)
  • contains(Bytes32ToBytes32Map storage map, bytes32 key):查询输入key是否存在于Bytes32ToBytes32Map中。如果输入key为已存在key,返回true;如果输入key不存在,返回false。时间复杂度为O(1);
  • length(Bytes32ToBytes32Map storage map):返回Bytes32ToBytes32Map中已存在的键值对数量。时间复杂度为O(1);
  • at(Bytes32ToBytes32Map storage map, uint256 index):返回Bytes32ToBytes32Map中索引为index的key和对应的value。函数内部没有对index是否越界进行检查,所以在调用时请确保传入的index小于整个Bytes32ToBytes32Map的长度。时间复杂度为O(1)。注:index其实是指Bytes32ToBytes32Map.Bytes32Set的key的index。当每次进行key的remove操作时,set中的key顺序都会发生变化。所以不要盲目地认为该index就是键值对的添加顺序的index。
    function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
    	// 调用EnumerableSet库中对应的contains()方法,从EnumerableSet.Bytes32Set中查询输入key是否存在。如果该key已存在于set中,返回true。否则返回false
        return map._keys.contains(key);
    }

    function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
        // 调用EnumerableSet库中对应的length()方法,返回已存在于map中的key的数量。key的数量其实就是map中键值对的数量
        return map._keys.length();
    }

    function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
    	// 调用EnumerableSet库中对应的at()方法,返回已存在于set中对应index位置的key
        bytes32 key = map._keys.at(index);
        // 返回key,并通过key找到对应的value一起返回
        return (key, map._values[key]);
    }
2.1.3 tryGet(Bytes32ToBytes32Map storage map, bytes32 key) && get(Bytes32ToBytes32Map storage map, bytes32 key) && get(Bytes32ToBytes32Map storage map, bytes32 key, string memory errorMessage)
  • tryGet(Bytes32ToBytes32Map storage map, bytes32 key):返回输入key是否存在于Bytes32ToBytes32Map中及输入key对应的value。时间复杂度为O(1)。注:如果key不存在于Bytes32ToBytes32Map中,不会revert,而是第一个参数返回false且对应值为bytes32(0);
  • get(Bytes32ToBytes32Map storage map, bytes32 key):返回输入key对应的value。时间复杂度为O(1)。注:如果key不存在于Bytes32ToBytes32Map中,会revert;
  • get(Bytes32ToBytes32Map storage map, bytes32 key, string memory errorMessage):返回输入key对应的value(已弃用)。时间复杂度为O(1)。注:该方法与上面的get方法功能完全一致,只是本方法提供自定义revert msg的功能。但是不建议使用该方法,因为字符串参数会增加额外的memory的消耗。如果当key不存在时一定要使用自定义revert msg,建议使用tryGet()进行组合。
    function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
    	// 通过key找到value
        bytes32 value = map._values[key];
        // 如果value为0,可能存在两种情况:1. 已存在key对应的value就是0、2. key不存在
        if (value == bytes32(0)) {
        	// 返回该key是否存在于map中和0值
            return (contains(map, key), bytes32(0));
        } else {
        	// 如果value不为0说明该key存在于map中,返回true和对应value值
            return (true, value);
        }
    }

    function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
    	// 通过key找到value
        bytes32 value = map._values[key];
        // 如果value为0且map中不包含该key,直接revert
        require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
        // 返回value值
        return value;
    }

    function get(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (bytes32) {
    	// 通过key找到value
        bytes32 value = map._values[key];
        // 如果value为0且map中不包含该key,直接以传入的errorMessage revert
        require(value != 0 || contains(map, key), errorMessage);
        // 返回value值
        return value;
    }
2.1.4 foundry代码验证
contract EnumerableMapTest is Test {
    MockBytes32ToBytes32Map mbtbm = new MockBytes32ToBytes32Map();

    function test_Bytes32ToBytes32Map_Operations() external {
        // empty
        assertEq(mbtbm.length(), 0);
        assertFalse(mbtbm.contains(0));

        // set
        assertTrue(mbtbm.set(0, 'v_0'));
        assertEq(mbtbm.length(), 1);
        assertTrue(mbtbm.set('a', 'v_a'));
        assertEq(mbtbm.length(), 2);
        // set key 'a' again
        assertFalse(mbtbm.set('a', 'v_a_new'));
        assertEq(mbtbm.length(), 2);
        (bytes32 key, bytes32 value) = mbtbm.at(0);
        assertEq(0, key);
        assertEq('v_0', value);
        (key, value) = mbtbm.at(1);
        assertEq('a', key);
        assertEq('v_a_new', value);

        assertTrue(mbtbm.set('b', 'v_b'));
        assertTrue(mbtbm.set('c', 'v_c'));
        assertEq(mbtbm.length(), 4);

        // remove
        // key array: [0,'a','b','c']
        assertTrue(mbtbm.contains('a'));
        assertTrue(mbtbm.remove('a'));
        assertFalse(mbtbm.contains('a'));
        assertEq(mbtbm.length(), 3);
        // remove key 'a' again
        assertFalse(mbtbm.remove('a'));
        assertEq(mbtbm.length(), 3);
        // key array after remove: [0,'c','b']
        (key, value) = mbtbm.at(0);
        assertEq(0, key);
        assertEq('v_0', value);
        (key, value) = mbtbm.at(1);
        assertEq('c', key);
        assertEq('v_c', value);
        (key, value) = mbtbm.at(2);
        assertEq('b', key);
        assertEq('v_b', value);

        // check tryGet()/get()/get() with error msg
        bytes32[3] memory keys = [bytes32(0), 'b', 'c'];
        bytes32[3] memory values = [bytes32('v_0'), 'v_b', 'v_c'];
        // case 1: key exists
        bool exist;
        for (uint i; i < 3; ++i) {
            // tryGet()
            (exist, value) = mbtbm.tryGet(keys[i]);
            assertTrue(exist);
            assertEq(value, values[i]);
            // get()
            assertEq(mbtbm.get(keys[i]), values[i]);
            // get() with error msg
            assertEq(mbtbm.get(keys[i], "revert msg: key not exist"), values[i]);
        }

        // case 2: key doesn't exist
        bytes32 keyNotExist = 'key not exist';
        (exist, value) = mbtbm.tryGet(keyNotExist);
        assertFalse(exist);
        assertEq(value, 0);
        // get()
        vm.expectRevert("EnumerableMap: nonexistent key");
        mbtbm.get(keyNotExist);
        // get() with error msg
        vm.expectRevert("revert msg: key not exist");
        mbtbm.get(keyNotExist, "revert msg: key not exist");

        // revert if out of bounds
        vm.expectRevert();
        mbtbm.at(1024);
    }
}

2.2 UintToUintMap体系

结构体UintToUintMap的内部直接封装了一个Bytes32ToBytes32Map:

	// key: uint256类型 value:uint256类型
    struct UintToUintMap {
    	// 内部直接封装了一个Bytes32ToBytes32Map
        Bytes32ToBytes32Map _inner;
    }
2.2.1 set(UintToUintMap storage map, uint256 key, uint256 value) && remove(UintToUintMap storage map, uint256 key)
  • set(UintToUintMap storage map, uint256 key, uint256 value):增添或更新键值对。如果输入key为已存在key(为更新键值对),返回false;如果输入key不存在(为新增键值对),返回true。时间复杂度为O(1);

  • remove(UintToUintMap storage map, uint256 key):删除键值对。如果输入key为已存在key(删除键值对),返回true;如果输入key不存在,返回false。时间复杂度为O(1)。

    function set(
        UintToUintMap storage map,
        uint256 key,
        uint256 value
    ) internal returns (bool) {
    	// 将传入的key和value都转成bytes32类型并调用底层的Bytes32ToBytes32Map的set()方法
        return set(map._inner, bytes32(key), bytes32(value));
    }

    function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的remove()方法
        return remove(map._inner, bytes32(key));
    }
2.2.2 contains(UintToUintMap storage map, uint256 key) && length(UintToUintMap storage map) && at(UintToUintMap storage map, uint256 index)
  • contains(UintToUintMap storage map, uint256 key):查询输入key是否存在于UintToUintMap中。如果输入key为已存在key,返回true;如果输入key不存在,返回false。时间复杂度为O(1);
  • length(UintToUintMap storage map): 返回UintToUintMap中已存在的键值对数量。时间复杂度为O(1);
  • at(UintToUintMap storage map, uint256 index):返回UintToUintMap中索引为index的key和对应的value。函数内部没有对index是否越界进行检查,所以在调用时请确保传入的index小于整个UintToUintMap的长度。时间复杂度为O(1)。注:index其实是指Bytes32ToBytes32Map.Bytes32Set的key的index。当每次进行key的remove操作时,set中的key顺序都会发生变化。所以不要盲目地认为该index就是键值对的添加顺序的index。
    function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的contains()方法
        return contains(map._inner, bytes32(key));
    }

    function length(UintToUintMap storage map) internal view returns (uint256) {
        // 调用底层的Bytes32ToBytes32Map的length()方法
        return length(map._inner);
    }

    function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
   		// 调用底层的Bytes32ToBytes32Map的at()方法,得到底层返回的bytes32类型的key和value值
        (bytes32 key, bytes32 value) = at(map._inner, index);
        // 将bytes32类型的key和value转换成uint256类型并返回
        return (uint256(key), uint256(value));
    }
2.2.3 tryGet(UintToUintMap storage map, uint256 key) && get(UintToUintMap storage map, uint256 key) && get(UintToUintMap storage map, uint256 key, string memory errorMessage)
  • tryGet(UintToUintMap storage map, uint256 key):返回输入key是否存在于UintToUintMap中及输入key对应的value。时间复杂度为O(1)。注:如果key不存在于UintToUintMap中,不会revert,而是第一个参数返回false且对应值为uint256(0);
  • get(UintToUintMap storage map, uint256 key):返回输入key对应的value。时间复杂度为O(1)。注:如果key不存在于UintToUintMap中,会revert;
  • get(UintToUintMap storage map, uint256 key, string memory errorMessage):返回输入key对应的value(已弃用)。时间复杂度为O(1)。注:该方法与上面的get方法功能完全一致,只是本方法提供自定义revert msg的功能。但是不建议使用该方法,因为字符串参数会增加额外的memory的消耗。如果当key不存在时一定要使用自定义revert msg,建议使用tryGet()进行组合。
    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
    	// 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的tryGet()方法
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        // 将底层返回的传入key是否存在及bytes32类型的value转换成uint256类型返回
        return (success, uint256(value));
    }

    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, bytes32(key)));
    }

    function get(
        UintToUintMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (uint256) {
    	// 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, bytes32(key), errorMessage));
    }
2.2.4 foundry代码验证
contract EnumerableMapTest is Test {
    MockUintToUintMap mutum = new MockUintToUintMap();

    function test_UintToUintMap_Operations() external {
        // empty
        assertEq(mutum.length(), 0);
        assertFalse(mutum.contains(0));

        // set
        assertTrue(mutum.set(0, 1));
        assertEq(mutum.length(), 1);
        assertTrue(mutum.set(1, 2));
        assertEq(mutum.length(), 2);
        // set key 1 again
        assertFalse(mutum.set(1, 2 + 1));
        assertEq(mutum.length(), 2);
        (uint key, uint value) = mutum.at(0);
        assertEq(0, key);
        assertEq(1, value);
        (key, value) = mutum.at(1);
        assertEq(1, key);
        assertEq(3, value);

        assertTrue(mutum.set(2, 4));
        assertTrue(mutum.set(3, 8));
        assertEq(mutum.length(), 4);

        // remove
        // key array: [0,1,2,3]
        assertTrue(mutum.contains(1));
        assertTrue(mutum.remove(1));
        assertFalse(mutum.contains(1));
        assertEq(mutum.length(), 3);
        // remove key 1 again
        assertFalse(mutum.remove(1));
        assertEq(mutum.length(), 3);
        // key array after remove: [0,3,2]
        (key, value) = mutum.at(0);
        assertEq(0, key);
        assertEq(1, value);
        (key, value) = mutum.at(1);
        assertEq(3, key);
        assertEq(8, value);
        (key, value) = mutum.at(2);
        assertEq(2, key);
        assertEq(4, value);

        // check tryGet()/get()/get() with error msg
        uint[3] memory keys = [uint(0), 2, 3];
        uint[3] memory values = [uint(1), 4, 8];
        // case 1: key exists
        bool exist;
        for (uint i; i < 3; ++i) {
            // tryGet()
            (exist, value) = mutum.tryGet(keys[i]);
            assertTrue(exist);
            assertEq(value, values[i]);
            // get()
            assertEq(mutum.get(keys[i]), values[i]);
            // get() with error msg
            assertEq(mutum.get(keys[i], "revert msg: key not exist"), values[i]);
        }

        // case 2: key doesn't exist
        uint keyNotExist = 1024;
        (exist, value) = mutum.tryGet(keyNotExist);
        assertFalse(exist);
        assertEq(value, 0);
        // get()
        vm.expectRevert("EnumerableMap: nonexistent key");
        mutum.get(keyNotExist);
        // get() with error msg
        vm.expectRevert("revert msg: key not exist");
        mutum.get(keyNotExist, "revert msg: key not exist");

        // revert if out of bounds
        vm.expectRevert();
        mutum.at(1024);
    }
}

2.3 UintToAddressMap体系

结构体UintToAddressMap的内部直接封装了一个Bytes32ToBytes32Map:

	// key: uint256类型 value:address类型
    struct UintToAddressMap {
        // 内部直接封装了一个Bytes32ToBytes32Map
        Bytes32ToBytes32Map _inner;
    }
2.3.1 set(UintToAddressMap storage map, uint256 key, address value) && remove(UintToAddressMap storage map, uint256 key)
  • set(UintToAddressMap storage map, uint256 key, address value):增添或更新键值对。如果输入key为已存在key(为更新键值对),返回false;如果输入key不存在(为新增键值对),返回true。时间复杂度为O(1);

  • remove(UintToAddressMap storage map, uint256 key):删除键值对。如果输入key为已存在key(删除键值对),返回true;如果输入key不存在,返回false。时间复杂度为O(1)。

    function set(
        UintToAddressMap storage map,
        uint256 key,
        address value
    ) internal returns (bool) {
        // 将传入的key和value都转成bytes32类型并调用底层的Bytes32ToBytes32Map的set()方法
        return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

    function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的remove()方法
        return remove(map._inner, bytes32(key));
    }
2.3.2 contains(UintToAddressMap storage map, uint256 key) && length(UintToAddressMap storage map) && at(UintToAddressMap storage map, uint256 index)
  • contains(UintToAddressMap storage map, uint256 key):查询输入key是否存在于UintToAddressMap中。如果输入key为已存在key,返回true;如果输入key不存在,返回false。时间复杂度为O(1);
  • length(UintToAddressMap storage map):返回UintToAddressMap中已存在的键值对数量。时间复杂度为O(1);
  • at(UintToAddressMap storage map, uint256 index):返回UintToAddressMap中索引为index的key和对应的value。函数内部没有对index是否越界进行检查,所以在调用时请确保传入的index小于整个UintToAddressMap的长度。时间复杂度为O(1)。注:index其实是指Bytes32ToBytes32Map.Bytes32Set的key的index。当每次进行key的remove操作时,set中的key顺序都会发生变化。所以不要盲目地认为该index就是键值对的添加顺序的index。
    function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的contains()方法
        return contains(map._inner, bytes32(key));
    }

    function length(UintToAddressMap storage map) internal view returns (uint256) {
        // 调用底层的Bytes32ToBytes32Map的length()方法
        return length(map._inner);
    }
    
    function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
    	// 调用底层的Bytes32ToBytes32Map的at()方法,得到底层返回的bytes32类型的key和value值
        (bytes32 key, bytes32 value) = at(map._inner, index);
        // 将bytes32类型的key和value分别转换成uint256和address类型并返回
        return (uint256(key), address(uint160(uint256(value))));
    }
2.3.3 tryGet(UintToAddressMap storage map, uint256 key) && get(UintToAddressMap storage map, uint256 key) && get(UintToAddressMap storage map, uint256 key, string memory errorMessage)
  • tryGet(UintToAddressMap storage map, uint256 key):返回输入key是否存在于UintToAddressMap中及输入key对应的value。时间复杂度为O(1)。注:如果key不存在于UintToAddressMap中,不会revert,而是第一个参数返回false且对应值为address(0);
  • get(UintToAddressMap storage map, uint256 key):返回输入key对应的value。时间复杂度为O(1)。注:如果key不存在于UintToAddressMap中,会revert;
  • get(UintToAddressMap storage map, uint256 key, string memory errorMessage):返回输入key对应的value(已弃用)。时间复杂度为O(1)。注:该方法与上面的get方法功能完全一致,只是本方法提供自定义revert msg的功能。但是不建议使用该方法,因为字符串参数会增加额外的memory的消耗。如果当key不存在时一定要使用自定义revert msg,建议使用tryGet()进行组合。
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
    	// 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的tryGet()方法
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        // 将底层返回的传入key是否存在及bytes32类型的value转换成address类型返回
        return (success, address(uint160(uint256(value))));
    }

    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成address类型返回
        return address(uint160(uint256(get(map._inner, bytes32(key)))));
    }

    function get(
        UintToAddressMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (address) {
    	// 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成address类型返回
        return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
    }
2.3.4 foundry代码验证
contract EnumerableMapTest is Test {
    MockUintToAddressMap mutam = new MockUintToAddressMap();

    function test_UintToAddressMap_Operations() external {
        // empty
        assertEq(mutam.length(), 0);
        assertFalse(mutam.contains(0));

        // set
        assertTrue(mutam.set(0, address(0)));
        assertEq(mutam.length(), 1);
        assertTrue(mutam.set(1, address(1)));
        assertEq(mutam.length(), 2);
        // set key 1 again
        assertFalse(mutam.set(1, address(1 + 1)));
        assertEq(mutam.length(), 2);
        (uint key, address value) = mutam.at(0);
        assertEq(0, key);
        assertEq(address(0), value);
        (key, value) = mutam.at(1);
        assertEq(1, key);
        assertEq(address(2), value);

        assertTrue(mutam.set(2, address(2)));
        assertTrue(mutam.set(3, address(3)));
        assertEq(mutam.length(), 4);

        // remove
        // key array: [0,1,2,3]
        assertTrue(mutam.contains(1));
        assertTrue(mutam.remove(1));
        assertFalse(mutam.contains(1));
        assertEq(mutam.length(), 3);
        // remove key 1 again
        assertFalse(mutam.remove(1));
        assertEq(mutam.length(), 3);
        // key array after remove: [0,3,2]
        (key, value) = mutam.at(0);
        assertEq(0, key);
        assertEq(address(0), value);
        (key, value) = mutam.at(1);
        assertEq(3, key);
        assertEq(address(3), value);
        (key, value) = mutam.at(2);
        assertEq(2, key);
        assertEq(address(2), value);

        // check tryGet()/get()/get() with error msg
        uint[3] memory keys = [uint(0), 2, 3];
        address[3] memory values = [address(0), address(2), address(3)];
        // case 1: key exists
        bool exist;
        for (uint i; i < 3; ++i) {
            // tryGet()
            (exist, value) = mutam.tryGet(keys[i]);
            assertTrue(exist);
            assertEq(value, values[i]);
            // get()
            assertEq(mutam.get(keys[i]), values[i]);
            // get() with error msg
            assertEq(mutam.get(keys[i], "revert msg: key not exist"), values[i]);
        }

        // case 2: key doesn't exist
        uint keyNotExist = 1024;
        (exist, value) = mutam.tryGet(keyNotExist);
        assertFalse(exist);
        assertEq(value, address(0));
        // get()
        vm.expectRevert("EnumerableMap: nonexistent key");
        mutam.get(keyNotExist);
        // get() with error msg
        vm.expectRevert("revert msg: key not exist");
        mutam.get(keyNotExist, "revert msg: key not exist");

        // revert if out of bounds
        vm.expectRevert();
        mutam.at(1024);
    }
}

2.4 AddressToUintMap体系

结构体AddressToUintMap的内部直接封装了一个Bytes32ToBytes32Map:

	// key: address类型 value:uint256类型
    struct AddressToUintMap {
        // 内部直接封装了一个Bytes32ToBytes32Map
        Bytes32ToBytes32Map _inner;
    }
2.4.1 set(AddressToUintMap storage map, address key, uint256 value) && remove(AddressToUintMap storage map, address key)
  • set(AddressToUintMap storage map, address key, uint256 value):增添或更新键值对。如果输入key为已存在key(为更新键值对),返回false;如果输入key不存在(为新增键值对),返回true。时间复杂度为O(1);

  • remove(AddressToUintMap storage map, address key):删除键值对。如果输入key为已存在key(删除键值对),返回true;如果输入key不存在,返回false。时间复杂度为O(1)。

    function set(
        AddressToUintMap storage map,
        address key,
        uint256 value
    ) internal returns (bool) {
        // 将传入的key和value都转成bytes32类型并调用底层的Bytes32ToBytes32Map的set()方法
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
    }

    function remove(AddressToUintMap storage map, address key) internal returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的remove()方法
        return remove(map._inner, bytes32(uint256(uint160(key))));
    }
2.4.2 contains(AddressToUintMap storage map, address key) && length(AddressToUintMap storage map) && at(AddressToUintMap storage map, uint256 index)
  • contains(AddressToUintMap storage map, address key):查询输入key是否存在于AddressToUintMap中。如果输入key为已存在key,返回true;如果输入key不存在,返回false。时间复杂度为O(1);
  • length(AddressToUintMap storage map):返回AddressToUintMap中已存在的键值对数量。时间复杂度为O(1);
  • at(AddressToUintMap storage map, uint256 index):返回AddressToUintMap中索引为index的key和对应的value。函数内部没有对index是否越界进行检查,所以在调用时请确保传入的index小于整个UintToAddressMap的长度。时间复杂度为O(1)。注:index其实是指Bytes32ToBytes32Map.Bytes32Set的key的index。当每次进行key的remove操作时,set中的key顺序都会发生变化。所以不要盲目地认为该index就是键值对的添加顺序的index。
    function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的contains()方法
        return contains(map._inner, bytes32(uint256(uint160(key))));
    }

    function length(AddressToUintMap storage map) internal view returns (uint256) {
        // 调用底层的Bytes32ToBytes32Map的length()方法
        return length(map._inner);
    }

    function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
       	// 调用底层的Bytes32ToBytes32Map的at()方法,得到底层返回的bytes32类型的key和value值
        (bytes32 key, bytes32 value) = at(map._inner, index);
        // 将bytes32类型的key和value分别转换成address和uint256类型并返回
        return (address(uint160(uint256(key))), uint256(value));
    }
2.4.3 tryGet(AddressToUintMap storage map, address key) && get(AddressToUintMap storage map, address key) && get(AddressToUintMap storage map, address key, string memory errorMessage)
  • tryGet(AddressToUintMap storage map, address key):返回输入key是否存在于AddressToUintMap中及输入key对应的value。时间复杂度为O(1)。注:如果key不存在于AddressToUintMap中,不会revert,而是第一个参数返回false且对应值为uint256(0);
  • get(AddressToUintMap storage map, address key):返回输入key对应的value。时间复杂度为O(1)。注:如果key不存在于AddressToUintMap中,会revert;
  • get(AddressToUintMap storage map, address key, string memory errorMessage):返回输入key对应的value(已弃用)。时间复杂度为O(1)。注:该方法与上面的get方法功能完全一致,只是本方法提供自定义revert msg的功能。但是不建议使用该方法,因为字符串参数会增加额外的memory的消耗。如果当key不存在时一定要使用自定义revert msg,建议使用tryGet()进行组合。
    function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的tryGet()方法
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        // 将底层返回的传入key是否存在及bytes32类型的value转换成uint256类型返回
        return (success, uint256(value));
    }

    function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
    }

    function get(
        AddressToUintMap storage map,
        address key,
        string memory errorMessage
    ) internal view returns (uint256) {
    	// 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
    }
2.4.4 foundry代码验证
contract EnumerableMapTest is Test {
    MockAddressToUintMap matum = new MockAddressToUintMap();

    function test_AddressToUintMap_Operations() external {
        // empty
        assertEq(matum.length(), 0);
        assertFalse(matum.contains(address(0)));

        // set
        assertTrue(matum.set(address(0), 0));
        assertEq(matum.length(), 1);
        assertTrue(matum.set(address(1), 1));
        assertEq(matum.length(), 2);
        // set key address(1) again
        assertFalse(matum.set(address(1), 1 + 1));
        assertEq(matum.length(), 2);
        (address key, uint value) = matum.at(0);
        assertEq(address(0), key);
        assertEq(0, value);
        (key, value) = matum.at(1);
        assertEq(address(1), key);
        assertEq(2, value);

        assertTrue(matum.set(address(2), 2));
        assertTrue(matum.set(address(3), 3));
        assertEq(matum.length(), 4);

        // remove
        // key array: [address(0),address(1),address(2),address(3)]
        assertTrue(matum.contains(address(1)));
        assertTrue(matum.remove(address(1)));
        assertFalse(matum.contains(address(1)));
        assertEq(matum.length(), 3);
        // remove key address(1) again
        assertFalse(matum.remove(address(1)));
        assertEq(matum.length(), 3);
        // key array after remove: [address(0),address(3),address(2)]
        (key, value) = matum.at(0);
        assertEq(address(0), key);
        assertEq(0, value);
        (key, value) = matum.at(1);
        assertEq(address(3), key);
        assertEq(3, value);
        (key, value) = matum.at(2);
        assertEq(address(2), key);
        assertEq(2, value);

        // check tryGet()/get()/get() with error msg
        address[3] memory keys = [address(0), address(2), address(3)];
        uint[3] memory values = [uint(0), 2, 3];
        // case 1: key exists
        bool exist;
        for (uint i; i < 3; ++i) {
            // tryGet()
            (exist, value) = matum.tryGet(keys[i]);
            assertTrue(exist);
            assertEq(value, values[i]);
            // get()
            assertEq(matum.get(keys[i]), values[i]);
            // get() with error msg
            assertEq(matum.get(keys[i], "revert msg: key not exist"), values[i]);
        }

        // case 2: key doesn't exist
        address keyNotExist = address(1024);
        (exist, value) = matum.tryGet(keyNotExist);
        assertFalse(exist);
        assertEq(value, 0);
        // get()
        vm.expectRevert("EnumerableMap: nonexistent key");
        matum.get(keyNotExist);
        // get() with error msg
        vm.expectRevert("revert msg: key not exist");
        matum.get(keyNotExist, "revert msg: key not exist");

        // revert if out of bounds
        vm.expectRevert();
        matum.at(1024);
    }
}

2.5 Bytes32ToUintMap体系

结构体Bytes32ToUintMap的内部直接封装了一个Bytes32ToBytes32Map:

	// key: bytes32类型 value:uint256类型
    struct Bytes32ToUintMap {
        // 内部直接封装了一个Bytes32ToBytes32Map
        Bytes32ToBytes32Map _inner;
    }
2.5.1 set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) && remove(Bytes32ToUintMap storage map, bytes32 key)
  • set(Bytes32ToUintMap storage map, bytes32 key, uint256 value):增添或更新键值对。如果输入key为已存在key(为更新键值对),返回false;如果输入key不存在(为新增键值对),返回true。时间复杂度为O(1);

  • remove(Bytes32ToUintMap storage map, bytes32 key):删除键值对。如果输入key为已存在key(删除键值对),返回true;如果输入key不存在,返回false。时间复杂度为O(1)。

    function set(
        Bytes32ToUintMap storage map,
        bytes32 key,
        uint256 value
    ) internal returns (bool) {
        // 将传入的key和value都转成bytes32类型并调用底层的Bytes32ToBytes32Map的set()方法
        return set(map._inner, key, bytes32(value));
    }

    function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
        // 将传入的key转成bytes32类型并调用底层的Bytes32ToBytes32Map的remove()方法
        return remove(map._inner, key);
    }
2.5.2 contains(Bytes32ToUintMap storage map, bytes32 key) && length(Bytes32ToUintMap storage map) && at(Bytes32ToUintMap storage map, uint256 index)
  • contains(Bytes32ToUintMap storage map, bytes32 key):查询输入key是否存在于Bytes32ToUintMap中。如果输入key为已存在key,返回true;如果输入key不存在,返回false。时间复杂度为O(1);
  • length(Bytes32ToUintMap storage map):返回Bytes32ToUintMap中已存在的键值对数量。时间复杂度为O(1);
  • at(Bytes32ToUintMap storage map, uint256 index):返回Bytes32ToUintMap中索引为index的key和对应的value。函数内部没有对index是否越界进行检查,所以在调用时请确保传入的index小于整个Bytes32ToUintMap的长度。时间复杂度为O(1)。注:index其实是指Bytes32ToBytes32Map.Bytes32Set的key的index。当每次进行key的remove操作时,set中的key顺序都会发生变化。所以不要盲目地认为该index就是键值对的添加顺序的index。
    function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
    	// 直接调用底层的Bytes32ToBytes32Map的contains()方法
        return contains(map._inner, key);
    }

    function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
        // 调用底层的Bytes32ToBytes32Map的length()方法
        return length(map._inner);
    }

    function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
       	// 调用底层的Bytes32ToBytes32Map的at()方法,得到底层返回的bytes32类型的key和value值
        (bytes32 key, bytes32 value) = at(map._inner, index);
        // 直接返回key,并将value转换成uint256类型返回
        return (key, uint256(value));
    }
2.5.3 tryGet(Bytes32ToUintMap storage map, bytes32 key) && get(Bytes32ToUintMap storage map, bytes32 key) && get(Bytes32ToUintMap storage map, bytes32 key, string memory errorMessage)
  • tryGet(Bytes32ToUintMap storage map, bytes32 key):返回输入key是否存在于Bytes32ToUintMap中及输入key对应的value。时间复杂度为O(1)。注:如果key不存在于Bytes32ToUintMap中,不会revert,而是第一个参数返回false且对应值为uint256(0);
  • get(Bytes32ToUintMap storage map, bytes32 key):返回输入key对应的value。时间复杂度为O(1)。注:如果key不存在于Bytes32ToUintMap中,会revert;
  • get(Bytes32ToUintMap storage map, bytes32 key, string memory errorMessage):返回输入key对应的value(已弃用)。时间复杂度为O(1)。注:该方法与上面的get方法功能完全一致,只是本方法提供自定义revert msg的功能。但是不建议使用该方法,因为字符串参数会增加额外的memory的消耗。如果当key不存在时一定要使用自定义revert msg,建议使用tryGet()进行组合。
    function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
        // 直接调用底层的Bytes32ToBytes32Map的tryGet()方法
        (bool success, bytes32 value) = tryGet(map._inner, key);
        // 直接返回key是否存在,并将value转换成uint256类型返回
        return (success, uint256(value));
    }

    function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
        // 直接调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, key));
    }

    function get(
        Bytes32ToUintMap storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (uint256) {
    	// 直接调用底层的Bytes32ToBytes32Map的get()方法,得到的bytes32类型结果转换成uint256类型返回
        return uint256(get(map._inner, key, errorMessage));
    }
2.5.4 foundry代码验证
contract EnumerableMapTest is Test {
    MockBytes32ToUintMap mbtum = new MockBytes32ToUintMap();

    function test_Bytes32ToUintMap_Operations() external {
        // empty
        assertEq(mbtum.length(), 0);
        assertFalse(mbtum.contains(0));

        // set
        assertTrue(mbtum.set(0, 0));
        assertEq(mbtum.length(), 1);
        assertTrue(mbtum.set('a', 1));
        assertEq(mbtum.length(), 2);
        // set key 'a' again
        assertFalse(mbtum.set('a', 97));
        assertEq(mbtum.length(), 2);
        (bytes32 key, uint value) = mbtum.at(0);
        assertEq(0, key);
        assertEq(0, value);
        (key, value) = mbtum.at(1);
        assertEq('a', key);
        assertEq(97, value);

        assertTrue(mbtum.set('b', 98));
        assertTrue(mbtum.set('c', 99));
        assertEq(mbtum.length(), 4);

        // remove
        // key array: [0,'a','b','c']
        assertTrue(mbtum.contains('a'));
        assertTrue(mbtum.remove('a'));
        assertFalse(mbtum.contains('a'));
        assertEq(mbtum.length(), 3);
        // remove key 'a' again
        assertFalse(mbtum.remove('a'));
        assertEq(mbtum.length(), 3);
        // key array after remove: [0,'c','b']
        (key, value) = mbtum.at(0);
        assertEq(0, key);
        assertEq(0, value);
        (key, value) = mbtum.at(1);
        assertEq('c', key);
        assertEq(99, value);
        (key, value) = mbtum.at(2);
        assertEq('b', key);
        assertEq(98, value);

        // check tryGet()/get()/get() with error msg
        bytes32[3] memory keys = [bytes32(0), 'b', 'c'];
        uint[3] memory values = [uint(0), 98, 99];
        // case 1: key exists
        bool exist;
        for (uint i; i < 3; ++i) {
            // tryGet()
            (exist, value) = mbtum.tryGet(keys[i]);
            assertTrue(exist);
            assertEq(value, values[i]);
            // get()
            assertEq(mbtum.get(keys[i]), values[i]);
            // get() with error msg
            assertEq(mbtum.get(keys[i], "revert msg: key not exist"), values[i]);
        }

        // case 2: key doesn't exist
        bytes32 keyNotExist = 'key not exist';
        (exist, value) = mbtum.tryGet(keyNotExist);
        assertFalse(exist);
        assertEq(value, 0);
        // get()
        vm.expectRevert("EnumerableMap: nonexistent key");
        mbtum.get(keyNotExist);
        // get() with error msg
        vm.expectRevert("revert msg: key not exist");
        mbtum.get(keyNotExist, "revert msg: key not exist");

        // revert if out of bounds
        vm.expectRevert();
        mbtum.at(1024);
    }
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/60956.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Python将图像转换为PDF:一次性解决您的批量转换需求

导语&#xff1a; 在数字化时代&#xff0c;我们经常需要处理大量的图像文件。将这些图像转换为PDF格式可以方便地存档、分享和打印。本文将介绍如何使用Python编程语言将图像批量转换为PDF&#xff0c;并提供了一个简单易用的图形界面来跟踪转换进度。 准备工作 在开始之前…

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…

kibana-7.17.3版本安装及汉化

1、官网下载地址&#xff1a;https://www.elastic.co/cn/downloads/kibana 选择安装系统类型和历史版本kibana安装版本要和es版本对应 2、上传安装包然后解压 tar -zxf kibana-7.17.3-linux-x86_64.tar.gz 3、更改目录属主 chown elk. kibana-7.17.3-linux-x86_64 -R …

【QT调用ST-link-使用QT编写程序-调用ST-LINK_CLI.exe-烧写STM32F4xxx-基础样例】

【QT结合ST-link&#xff0c;使用QT编写程序&#xff0c;调用ST-LINK_CLI.exe,烧写STM32F4xxx-基础样例】 1、前言2、实验环境3、先前了解-自我总结4、实验过程&#xff08;0&#xff09;硬件连接与供电&#xff08;1&#xff09;安装&使用STM32 ST-LINK Utility&#xff0…

65 # 实现 http-server 里的 gzip 压缩

用 zlib 来实现 gzip 压缩 服务端优化都是&#xff1a;压缩 缓存 前端可以通过 webpack 插件进行压缩 gzip 根据替换来实现的&#xff0c;重复率越高&#xff0c;压缩后的结果越小 const zlib require("zlib"); const fs require("fs"); const path …

BI报表工具有哪些作用?奥威BI全面剖析数据

BI报表工具有哪些作用&#xff1f;主要的作用是通过整合多业务来源数据&#xff0c;全面分析挖掘数据&#xff0c;来帮助企业实现数据化运营、支持智能决策、实现数据资产沉淀和增值、进行数据挖掘和预测分析、提高数据可读性和数据可视化程度等&#xff0c;从而提高企业的竞争…

ORCA优化器浅析——CQueryContext对优化器的要求

从ORCA优化器浅析——重要主流程概述中可以知道进入真正优化器引擎执行流程之前需要对优化器提出要求&#xff0c;比如后面会提到的required columns、required sort orders等。而CQueryContext即是承载这些内容的类。首先CQueryContext类是通过PqcGenerate函数构造的&#xff…

LabVIEW开发3D颈动脉图像边缘检测

LabVIEW开发3D颈动脉图像边缘检测 近年来&#xff0c;超声图像在医学领域对疾病诊断具有重要意义。边缘检测是图像处理技术的重要组成部分。边缘包含图像信息。边缘检测的主要目的是根据强度和纹理等属性识别图像中均匀区域的边界。超声&#xff08;US&#xff09;图像存在视觉…

SpringCloud入门Day01-服务注册与发现、服务通信、负载均衡与算法

SpringCloudNetflix入门 一、应用架构的演变 伴随互联网的发展&#xff0c;使用互联网的人群越来越多&#xff0c;软件应用的体量越来越大和复杂。而传统单体应用 可能不足以支撑大数据量以及发哦并发场景应用的框架也随之进行演变从最开始的单体应用架构到分布式&#xff08…

利用OpenCV实现图像拼接

一、介绍 图像拼接. 二、分步实现 要实现图像拼接&#xff0c;简单来说有以下几步&#xff1a; 对每幅图进行特征点提取对对特征点进行匹配进行图像配准把图像拷贝到另一幅图像的特定位置对重叠边界进行特殊处理 PS&#xff1a;需要使用低版本的opencv&#xff0c;否则无法使…

socker套接字

1.打印错误信息 2.socketaddr_in结构体 结构体&#xff1a; &#xff08;部分库代码&#xff09; (宏中的##) 3.manual TCP: SOCK_STREAM &#xff1a; 提供有序地&#xff0c;可靠的&#xff0c;全双工的&#xff0c;基于连接的流式服务 UDP: 面向数据报

[Docker实现测试部署CI/CD----自由风格和流水线的CD操作(6)]

目录 12、自由风格的CD操作发布 V1.0.0 版本修改代码并推送GitLab 中项目打 Tag 发布 V2.0.0 版本Jenkins 配置 tag 参数添加 Git 参数添加 checkout 命令修改构建命令配置修改 SSH 配置 部署 v1.0.0重新构建工程构建结果 部署 v2.0.0重新构建工程访问 部署v3.0.0 13、流水线任…

计算机成下一个土木了吗?

前些年抓住了互联网行业的红利期&#xff0c;进入大厂的员工&#xff0c;基本可以实现在一线城市买房扎根。 但反观现在&#xff0c;“被毕业、逃离互联网、躺平算了...”却成了这个行业的主旋律&#xff0c;不少人在谈论润到国企和外企去了&#xff0c;也放低了对工资的预期&…

Java SpringBoot集成Activiti7工作流

Activiti7 Java SpringBoot集成Activiti7工作流介绍项目集成引入依赖YML配置文件配置类 启动项目生成表结构Activiti的数据库支持 Activiti数据表介绍项目Demo地址&#xff1a; Java SpringBoot集成Activiti7工作流 本文项目Demo地址附在文章后方 官网主页&#xff1a;http://a…

【C++】右值引用

文章目录 右值引用值得形式返回对象的缺陷移动语句移动赋值 右值引用 能够取地址、能够被修改的被称之为左值。 不能够取地址、不能够被修改、以及将亡值被称之为右值。 普通类型的变量&#xff0c;因为有名字&#xff0c;可以取地址&#xff0c;都认为是左值。const修饰的常量…

Flutter游戏引擎Flame系列笔记 - 1.Flame引擎概述

Flutter游戏引擎Flame系列笔记 1.Flame引擎概述 - 文章信息 - Author: 李俊才(jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/132119035 【介绍】…

ThinkPHP v6.0.8 CacheStore 反序列化漏洞

漏洞说明 1. 漏洞原理&#xff1a;ThinkPHP 6.0.8 CacheStore 会触发POP利用链子&#xff0c;造成任意命令执行 2. 组件描述&#xff1a; ThinkPHP是一个免费开源的&#xff0c;快速、简单的面向对象的轻量级PHP开发框架 3. 影响版本&#xff1a;V6.0.8 漏洞复现 1. 环境安…

QtAV for ubuntu16.04

下载ubuntu https://releases.ubuntu.com/16.04/ubuntu-16.04.7-desktop-amd64.iso 下载ffmpeg https://ffmpeg.org/download.html 下载QtAV https://github.com/wang-bin/QtAV/releases 更新 sudo apt update 安装库 sudo apt-get install libglu1-mesa-dev freeglut3-dev…

解密爬虫ip是如何被识别屏蔽的

在当今信息化的时代&#xff0c;网络爬虫已经成为许多企业、学术机构和个人不可或缺的工具。然而&#xff0c;随着网站安全防护的升级&#xff0c;爬虫ip往往容易被识别并屏蔽&#xff0c;给爬虫工作增加了许多困扰。在这里&#xff0c;作为一家专业的爬虫ip供应商&#xff0c;…

K8s中的Secret

Secret作用&#xff1a;加密数据存在etcd里面&#xff0c;让pod容器以挂载Volume方式进行访问。场景&#xff1a;凭据