[Ethereum] How to join strings with numbers in Solidity

soliditystring

I'd like to format url with number parameters as following:

http://someurl.com/?param1=100&param2=200

The problem is, it's always truncated after first number parameter e.g:

http://someurl.com/?param1=100. Not sure why.

In my contract I'm using strings library https://github.com/Arachnid/solidity-stringutils

contract code:

import "strings.sol";

contract MyContract {
    using strings for *;

    function bytes32ToString (bytes32 data) constant returns (string) {
        bytes memory bytesString = new bytes(32);
        for (uint j=0; j<32; j++) {
            byte char = byte(bytes32(uint(data) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[j] = char;
            }
        }
        return string(bytesString);
    }

    function uintToBytes(uint v) constant private returns (bytes32 ret) {
        if (v == 0) {
            ret = '0';
        }
        else {
            while (v > 0) {
                ret = bytes32(uint(ret) / (2 ** 8));
                ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
                v /= 10;
            }
        }
        return ret;
    }

    function uintToString(uint v) constant private returns (string ret) {
        return bytes32ToString(uintToBytes(v));
    }

    function formatUrl(uint param1, uint param2) constant returns(string) {
        var parts = new strings.slice[](4);
        parts[0] = "http://someurl.com/?param1=".toSlice();
        parts[1] = uintToString(param1).toSlice();
        parts[2] = "&?param2=".toSlice();
        parts[3] = uintToString(param2).toSlice();

        return "".toSlice().join(parts);
    }
}

So calling formatUrl(100, 200) returns "http://someurl.com/?param1=100"

If I changed function like this:

function formatUrl(uint param1, uint param2) constant returns(string) {
        var parts = new strings.slice[](4);
        parts[0] = "http://someurl.com/?param1=".toSlice();
        parts[3] = uintToString(param1).toSlice();
        parts[1] = "&?param2=".toSlice();
        parts[2] = uintToString(param2).toSlice();

        return "".toSlice().join(parts);
    }

I get output: "http://someurl.com/?param1=&?param2=200"

It always ends after inserting first number converted to string.

Best Answer

Okay, so I eventually figured out. The problem was in bytes32ToString function. It always returns 32 character long string, where rest of unused chars are simply zero bytes and zero byte means end of a string. So that's why I couldn't join anything after such string, because it had zero bytes in it.

So this is edited bytes32ToString function. It doesn't return any zero bytes. There might exist more effective way tho. Improvements appreciated.

function bytes32ToString (bytes32 x) constant returns (string) {
    bytes memory bytesString = new bytes(32);
    uint charCount = 0;
    for (uint j = 0; j < 32; j++) {
        byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
        if (char != 0) {
            bytesString[charCount] = char;
            charCount++;
        }
    }
    bytes memory resultBytes = new bytes(charCount);
    for (j = 0; j < charCount; j++) {
        resultBytes[j] = bytesString[j];
    }

    return string(resultBytes);
}
Related Topic