I have a uint8 array containing ASCII codes for characters and a string variable, and I wish to make a comparison between them. For example:
uint8[3] memory foo = [98, 97, 122]; // baz
string memory bar = "baz";
bool result = keccak256(abi.encodePacked(foo)) == keccak256(abi.encodePacked(bytes(bar))); // false
Here I want the comparison to succeed, but it's a failure because encodePacked
will keep the padding of all the uint8 elements in the array when encoding it.
How can I do it instead?
You are currently comparing encoded value abi.encodePacked(foo))
to hashed value keccak256(abi.encodePacked(bytes(bar))
, which would never equal.
The uint8
fixed-size array is stored in memory in three separate slots - one for each item - and each of the items is ordered right to left (little endian).
0x
0000000000000000000000000000000000000000000000000000000000000062
0000000000000000000000000000000000000000000000000000000000000061
000000000000000000000000000000000000000000000000000000000000007a
But the string
literal is stored as a dynamic-size byte array ordered left to right (big endian):
0x
0000000000000000000000000000000000000000000000000000000000000020 # pointer
0000000000000000000000000000000000000000000000000000000000000003 # length
62617a0000000000000000000000000000000000000000000000000000000000 # value
So because the actual data is stored differently, you cannot perform a simple byte comparison of both arrays.
You can, however, loop through all items of the array and compare each item separately.
pragma solidity ^0.8;
contract MyContract {
function compare() external pure returns (bool) {
uint8[3] memory foo = [98, 97, 122]; // baz
string memory bar = "baz";
// typecast the `string` to `bytes` dynamic-length array
// so that you can use its `.length` member property
// and access its items individually (see `barBytes[i]` below, not possible with `bar[i]`)
bytes memory barBytes = bytes(bar);
// prevent accessing out-of-bounds index in the following loop
// as well as false positive if `foo` contains just the beginning of `bar` but not the whole string
if (foo.length != barBytes.length) {
return false;
}
// loop through each item of `foo`
for (uint i; i < foo.length; i++) {
uint8 barItemDecimal = uint8(barBytes[i]);
// and compare it to each decimal value of `bar` character
if (foo[i] != barItemDecimal) {
return false;
}
}
// all items have equal values
return true;
}
}