|
| 1 | +/** |
| 2 | + * @title String utility functions for Solidity contracts. |
| 3 | + * @author Nick Johnson <[email protected]> |
| 4 | + * |
| 5 | + * @dev All functions are UTF-8 friendly, if input strings are valid UTF-8. |
| 6 | + * Offsets and sizes are specified in bytes, not characters, and so will |
| 7 | + * not respect UTF-8 character boundaries; be careful to only pass values |
| 8 | + * that you know are between characters. |
| 9 | + */ |
| 10 | +contract StringUtils { |
| 11 | + function readWord(bytes a, uint idx) private returns (bytes32 word) { |
| 12 | + assembly { |
| 13 | + word := mload(add(add(a, idx), 32)) |
| 14 | + } |
| 15 | + } |
| 16 | + |
| 17 | + /** |
| 18 | + * @dev Compares two strings, returning a negative number if a is smaller, |
| 19 | + * a positive number if a is larger, and zero if the strings are equal. |
| 20 | + * @param a The first string to compare. |
| 21 | + * @param b The second string to compare. |
| 22 | + * @return An integer whose sign indicates the value of the comparison. |
| 23 | + */ |
| 24 | + function strcmp(string a, string b) internal returns (int) { |
| 25 | + uint shortest = bytes(a).length; |
| 26 | + if (bytes(b).length < bytes(a).length) |
| 27 | + shortest = bytes(b).length; |
| 28 | + |
| 29 | + for (uint idx = 0; idx < shortest; idx += 32) { |
| 30 | + var diff = int( |
| 31 | + uint(readWord(bytes(a), idx)) - uint(readWord(bytes(b), idx))); |
| 32 | + if (diff != 0) |
| 33 | + return diff; |
| 34 | + } |
| 35 | + return int(bytes(a).length - bytes(b).length); |
| 36 | + } |
| 37 | + |
| 38 | + /** |
| 39 | + * @dev Finds an occurrence of a substring in a string, returning its index, |
| 40 | + * or -1 if the substring is not found. |
| 41 | + * @param haystack The string to search. |
| 42 | + * @param needle The string to look for. |
| 43 | + * @param idx The string index at which to start searching. |
| 44 | + * @return The index of the first character of the substring, or -1 if not |
| 45 | + * found. |
| 46 | + */ |
| 47 | + function strstr(string haystack, string needle, uint idx) internal |
| 48 | + returns (int) |
| 49 | + { |
| 50 | + uint needleSize = bytes(needle).length; |
| 51 | + bytes32 hash; |
| 52 | + assembly { |
| 53 | + hash := sha3(add(needle, 32), needleSize) |
| 54 | + } |
| 55 | + for(; idx <= bytes(haystack).length - needleSize; idx++) { |
| 56 | + bytes32 testHash; |
| 57 | + assembly { |
| 58 | + testHash := sha3(add(add(haystack, idx), 32), needleSize) |
| 59 | + } |
| 60 | + if (hash == testHash) |
| 61 | + return int(idx); |
| 62 | + } |
| 63 | + return -1; |
| 64 | + } |
| 65 | + |
| 66 | + /** |
| 67 | + * @dev Copies part of one string into another. If the requested range |
| 68 | + * extends past the end of the source or target strings, the range will |
| 69 | + * be truncated. If src and dest are the same, the ranges must either |
| 70 | + * not overlap, or idx must be less than start. |
| 71 | + * @param dest The destination string to copy into. |
| 72 | + * @param idx The start index in the destination string. |
| 73 | + * @param src The string to copy from. |
| 74 | + * @param start The index into the source string to start copying. |
| 75 | + * @param len The number of bytes to copy. |
| 76 | + */ |
| 77 | + function strncpy(string dest, uint idx, string src, uint start, uint len) |
| 78 | + internal |
| 79 | + { |
| 80 | + if (idx + len > bytes(dest).length) |
| 81 | + len = bytes(dest).length - idx; |
| 82 | + if (start > bytes(src).length) |
| 83 | + return; |
| 84 | + if (start + len > bytes(src).length) |
| 85 | + len = bytes(src).length - start; |
| 86 | + |
| 87 | + // From here, we treat idx and start as memory offsets for dest and idx. |
| 88 | + // Skip over the first word, which contains the length of each string. |
| 89 | + idx += 32; |
| 90 | + start += 32; |
| 91 | + |
| 92 | + // Copy word-length chunks while possible |
| 93 | + for(; len >= 32; len -= 32) { |
| 94 | + assembly { |
| 95 | + mstore(add(dest, idx), mload(add(src, start))) |
| 96 | + } |
| 97 | + idx += 32; |
| 98 | + start += 32; |
| 99 | + } |
| 100 | + |
| 101 | + // Copy remaining bytes |
| 102 | + uint mask = 256 ** (32 - len) - 1; |
| 103 | + assembly { |
| 104 | + let destaddr := add(dest, idx) |
| 105 | + let srcpart := and(mload(add(src, start)), bnot(mask)) |
| 106 | + let destpart := and(mload(destaddr), mask) |
| 107 | + mstore(destaddr, or(destpart, srcpart)) |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * @dev Returns a substring starting at idx and continuing until the first |
| 113 | + * occurrence of delim. If delim is not found, returns the remainder of |
| 114 | + * the string. |
| 115 | + * @param str The string to return a substring of. |
| 116 | + * @param delim The delimiter to search for. |
| 117 | + * @param idx The start index. |
| 118 | + * @return A newly allocated string consisting of bytes between idx and the |
| 119 | + * first occurrence of delim. |
| 120 | + */ |
| 121 | + function strsep(string str, string delim, uint idx) internal |
| 122 | + returns (string ret) |
| 123 | + { |
| 124 | + int endIdx = strstr(str, delim, idx); |
| 125 | + if (endIdx == -1) { |
| 126 | + endIdx = int(bytes(str).length); |
| 127 | + } |
| 128 | + ret = new string(uint(endIdx) - idx); |
| 129 | + strncpy(ret, 0, str, idx, uint(endIdx) - idx); |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * @dev Returns the length of a string, in characters. |
| 134 | + * @param str The string to return the length of. |
| 135 | + * @return The length of the string, in characters. |
| 136 | + */ |
| 137 | + function strchrlen(string str) internal returns (uint len) { |
| 138 | + bytes memory strdata = bytes(str); |
| 139 | + for (uint i = 0; i < strdata.length; i++) |
| 140 | + // Don't count continuation bytes, of the form 0b10xxxxxx |
| 141 | + if (strdata[i] & 0xC0 != 0x80) |
| 142 | + len += 1; |
| 143 | + } |
| 144 | +} |
0 commit comments