codex-contracts-eth/contracts/libs/Mappings.sol

185 lines
4.5 KiB
Solidity

// SPDX-License-Identifier: MIT
// heavily inspired by: https://bitbucket.org/rhitchens2/soliditystoragepatterns/src/master/OneToMany.sol
pragma solidity ^0.8.8;
import "./EnumerableSetExtensions.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
library Mappings {
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSetExtensions for EnumerableSetExtensions.ClearableBytes32Set;
type KeyId is bytes32;
type ValueId is bytes32;
struct Mapping {
EnumerableSet.Bytes32Set _keyIds;
EnumerableSet.Bytes32Set _valueIds;
mapping(KeyId => Key) _keys;
mapping(ValueId => Value) _values;
}
struct Key {
EnumerableSetExtensions.ClearableBytes32Set _values;
}
struct Value {
KeyId _keyId;
}
function exists(Mapping storage map, KeyId key)
internal
view
returns (bool)
{
return map._keyIds.contains(KeyId.unwrap(key));
}
function exists(Mapping storage map, KeyId key, ValueId value)
internal
view
returns (bool)
{
bytes32 val = ValueId.unwrap(value);
return map._keys[key]._values.contains(val) &&
map._valueIds.contains(val);
}
function keys(Mapping storage map)
internal
view
returns(KeyId[] memory)
{
return _toKeyIds(map._keyIds.values());
}
function values(Mapping storage map,
KeyId key)
internal
view
returns(ValueId[] memory)
{
require(exists(map, key), "key does not exist");
return _toValueIds(map._keys[key]._values.values());
}
function count(Mapping storage map,
KeyId key)
internal
view
returns(uint256)
{
require(exists(map, key), "key does not exist");
return map._keys[key]._values.length();
}
function count(Mapping storage map)
internal
view
returns(uint256)
{
return map._valueIds.length();
}
function insertKey(Mapping storage map, KeyId key)
internal
returns (bool)
{
require(!exists(map, key), "key already exists");
return map._keyIds.add(KeyId.unwrap(key));
// NOTE: map._keys[key]._values contains a default EnumerableSet.Bytes32Set
}
function insertValue(Mapping storage map, KeyId key, ValueId value)
internal
returns (bool success)
{
require(exists(map, key), "key does not exists");
require(!exists(map, key, value), "value already exists");
success = map._valueIds.add(ValueId.unwrap(value));
assert (success); // value addition failure
map._values[value]._keyId = key;
success = map._keys[key]._values.add(ValueId.unwrap(value));
}
function insert(Mapping storage map, KeyId key, ValueId value)
internal
returns (bool success)
{
if (!exists(map, key)) {
success = insertKey(map, key);
assert (success); // key insertion failure
}
if (!exists(map, key, value)) {
success = insertValue(map, key, value);
}
}
function deleteKey(Mapping storage map, KeyId key)
internal
returns (bool success)
{
require(exists(map, key), "key does not exist");
require(count(map, key) == 0, "references values");
success = map._keyIds.remove(KeyId.unwrap(key)); // Note that this will fail automatically if the key doesn't exist
assert(success); // key removal failure
delete map._keys[key];
}
function deleteValue(Mapping storage map, KeyId key, ValueId value)
internal
returns (bool success)
{
require(exists(map, key), "key does not exist");
require(exists(map, key, value), "value does not exist");
success = map._valueIds.remove(ValueId.unwrap(value));
assert (success); // value removal failure
delete map._values[value];
success = map._keys[key]._values.remove(ValueId.unwrap(value));
}
function clear(Mapping storage map, KeyId key)
internal
returns (bool success)
{
require(exists(map, key), "key does not exist");
map._keys[key]._values.clear();
success = deleteKey(map, key);
}
function _toKeyIds(bytes32[] memory array)
private
pure
returns (KeyId[] memory result)
{
// solhint-disable-next-line no-inline-assembly
assembly {
result := array
}
}
function _toValueIds(bytes32[] memory array)
private
pure
returns (ValueId[] memory result)
{
// solhint-disable-next-line no-inline-assembly
assembly {
result := array
}
}
function toKeyId(ValueId valueId) internal pure returns (KeyId) {
return KeyId.wrap(ValueId.unwrap(valueId));
}
function toKeyId(address addr) internal pure returns (KeyId) {
return KeyId.wrap(bytes32(uint(uint160(addr))));
}
}