-
Notifications
You must be signed in to change notification settings - Fork 707
Expand file tree
/
Copy pathLibString.sol
More file actions
76 lines (59 loc) · 3.15 KB
/
LibString.sol
File metadata and controls
76 lines (59 loc) · 3.15 KB
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
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Efficient library for creating string representations of integers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol)
library LibString {
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) return toString(uint256(value));
unchecked {
str = toString(uint256(-value));
/// @solidity memory-safe-assembly
assembly {
// Note: This is only safe because we over-allocate memory
// and write the string from right to left in toString(uint256),
// and thus can be sure that sub(str, 1) is an unused memory location.
let originalStr := str // Save the original pointer before shifting.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(originalStr, add(length, 1)) // Update the string length at the original pointer.
}
}
}
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
// to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
// trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
let newFreeMemoryPointer := add(mload(0x40), 160)
// Update the free memory pointer to avoid overriding our string.
mstore(0x40, newFreeMemoryPointer)
// Assign str to the end of the zone of newly allocated memory.
str := sub(newFreeMemoryPointer, 32)
// Clean the last word of memory it may not be overwritten.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
// Move the pointer 1 byte to the left.
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing temp until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
// Compute and cache the final total length of the string.
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 32)
// Store the string's length at the start of memory allocated for our string.
mstore(str, length)
}
}
}