-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathStringUtils.sol
260 lines (233 loc) · 9.76 KB
/
StringUtils.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
// https://github.com/nalinbhardwaj/ethdosnumber/blob/main/ethdos-contracts/src/HexStrings.sol
library StringUtils {
bytes16 internal constant ALPHABET = "0123456789abcdef";
/// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
/// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length);
for (uint256 i = buffer.length; i > 0; i--) {
buffer[i - 1] = ALPHABET[value & 0xf];
value >>= 4;
}
return string(buffer);
}
function toString(uint256 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(bytes32 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(address account) internal pure returns (string memory) {
return toString(abi.encodePacked(account));
}
function stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function toString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
}
return string(str);
}
function convertPackedByteToString(uint256 packedByte, uint256 maxBytes, uint256 packSize)
internal
pure
returns (string memory extractedString)
{
uint256[] memory packedBytes = new uint256[](1);
packedBytes[0] = packedByte;
return convertPackedBytesToString(packedBytes, maxBytes, packSize);
}
// Unpacks uint256s into bytes and then extracts the non-zero characters
// Only extracts contiguous non-zero characters and ensures theres only 1 such state
// Note that unpackedLen may be more than packedBytes.length * 8 since there may be 0s
// TODO: Remove console.logs and define this as a pure function instead of a view
function convertPackedBytesToString(uint256[] memory packedBytes, uint256 maxBytes, uint256 packSize)
internal
pure
returns (string memory extractedString)
{
uint8 state = 0;
// bytes: 0 0 0 0 y u s h _ g 0 0 0
// state: 0 0 0 0 1 1 1 1 1 1 2 2 2
bytes memory nonzeroBytesArray = new bytes(packedBytes.length * 7);
uint256 nonzeroBytesArrayIndex = 0;
for (uint16 i = 0; i < packedBytes.length; i++) {
uint256 packedByte = packedBytes[i];
uint8[] memory unpackedBytes = new uint8[](packSize);
for (uint256 j = 0; j < packSize; j++) {
unpackedBytes[j] = uint8(packedByte >> (j * 8));
}
for (uint256 j = 0; j < packSize; j++) {
uint256 unpackedByte = unpackedBytes[j]; //unpackedBytes[j];
// console.log(i, j, state, unpackedByte);
if (unpackedByte != 0) {
nonzeroBytesArray[nonzeroBytesArrayIndex] = bytes1(uint8(unpackedByte));
nonzeroBytesArrayIndex++;
if (state % 2 == 0) {
state += 1;
}
} else {
if (state % 2 == 1) {
state += 1;
}
}
packedByte = packedByte >> 8;
}
}
require(state >= 1, "Invalid final state of packed bytes in email, or value is 0!");
require(nonzeroBytesArrayIndex <= maxBytes, "Packed bytes more than allowed max length!");
string memory returnValue = removeTrailingZeros(string(nonzeroBytesArray));
return returnValue;
// Have to end at the end of the email -- state cannot be 1 since there should be an email footer
}
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
for (i = 0; i < 32 && input[i] != 0; i++) {}
bytes memory resultBytes = new bytes(i);
for (i = 0; i < 32 && input[i] != 0; i++) {
resultBytes[i] = input[i];
}
return string(resultBytes);
}
// sliceArray is used to slice an array of uint256s from start-end into a new array of uint256s
function sliceArray(uint256[] memory input, uint256 start, uint256 end) internal pure returns (uint256[] memory) {
require(start <= end && end <= input.length, "Invalid slice indices");
uint256[] memory result = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
result[i - start] = input[i];
}
return result;
}
// stringToUint is used to convert a string like "45" to a uint256 4
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (b[i] >= 0x30 && b[i] <= 0x39) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
// TODO: Currently truncates decimals
if (b[i] == 0x2E) {
return result;
}
}
return result;
}
// getDomainFromEmail is used to extract the domain from an email i.e. the part after the @
function getDomainFromEmail(string memory fromEmail) internal pure returns (string memory) {
bytes memory emailBytes = bytes(fromEmail);
uint256 atIndex;
for (uint256 i = 0; i < emailBytes.length; i++) {
if (emailBytes[i] == "@") {
atIndex = i;
break;
}
}
bytes memory domainBytes = new bytes(emailBytes.length - atIndex - 1);
for (uint256 j = 0; j < domainBytes.length; j++) {
domainBytes[j] = emailBytes[atIndex + 1 + j];
}
return bytes32ToString(bytes32(bytes(domainBytes)));
}
function removeTrailingZeros(string memory input) public pure returns (string memory) {
bytes memory inputBytes = bytes(input);
uint256 endIndex = inputBytes.length;
for (uint256 i = 0; i < inputBytes.length; i++) {
if (inputBytes[i] == 0) {
endIndex = i;
break;
}
}
bytes memory resultBytes = new bytes(endIndex);
for (uint256 i = 0; i < endIndex; i++) {
resultBytes[i] = inputBytes[i];
}
return string(resultBytes);
}
// Upper/lower string utils from https://github.com/willitscale/solidity-util/blob/master/lib/Strings.sol
/**
* Upper
*
* Converts all the values of a string to their corresponding upper case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to upper case
* @return string
*/
function upper(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _upper(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Lower
*
* Converts all the values of a string to their corresponding lower case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to lower case
* @return string
*/
function lower(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _lower(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Upper
*
* Convert an alphabetic character to upper case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to upper case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a lower case otherwise returns the original value
*/
function _upper(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x61 && _b1 <= 0x7A) {
return bytes1(uint8(_b1) - 32);
}
return _b1;
}
/**
* Lower
*
* Convert an alphabetic character to lower case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to lower case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a upper case otherwise returns the original value
*/
function _lower(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x41 && _b1 <= 0x5A) {
return bytes1(uint8(_b1) + 32);
}
return _b1;
}
}