Search code examples
ethereumsolidity

How to compare an ascii string with a uint8 array in Solidity?


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?


Solution

  • 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;
        }
    }