ShortStrings.sol 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.8;
  3. import "./StorageSlot.sol";
  4. type ShortString is bytes32;
  5. /**
  6. * @dev This library provides functions to convert short memory strings
  7. * into a `ShortString` type that can be used as an immutable variable.
  8. * Strings of arbitrary length can be optimized if they are short enough by
  9. * the addition of a storage variable used as fallback.
  10. *
  11. * Usage example:
  12. *
  13. * ```solidity
  14. * contract Named {
  15. * using ShortStrings for *;
  16. *
  17. * ShortString private immutable _name;
  18. * string private _nameFallback;
  19. *
  20. * constructor(string memory contractName) {
  21. * _name = contractName.toShortStringWithFallback(_nameFallback);
  22. * }
  23. *
  24. * function name() external view returns (string memory) {
  25. * return _name.toStringWithFallback(_nameFallback);
  26. * }
  27. * }
  28. * ```
  29. */
  30. library ShortStrings {
  31. bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
  32. error StringTooLong(string str);
  33. error InvalidShortString();
  34. /**
  35. * @dev Encode a string of at most 31 chars into a `ShortString`.
  36. *
  37. * This will trigger a `StringTooLong` error is the input string is too long.
  38. */
  39. function toShortString(string memory str) internal pure returns (ShortString) {
  40. bytes memory bstr = bytes(str);
  41. if (bstr.length > 31) {
  42. revert StringTooLong(str);
  43. }
  44. return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
  45. }
  46. /**
  47. * @dev Decode a `ShortString` back to a "normal" string.
  48. */
  49. function toString(ShortString sstr) internal pure returns (string memory) {
  50. uint256 len = byteLength(sstr);
  51. // using `new string(len)` would work locally but is not memory safe.
  52. string memory str = new string(32);
  53. /// @solidity memory-safe-assembly
  54. assembly {
  55. mstore(str, len)
  56. mstore(add(str, 0x20), sstr)
  57. }
  58. return str;
  59. }
  60. /**
  61. * @dev Return the length of a `ShortString`.
  62. */
  63. function byteLength(ShortString sstr) internal pure returns (uint256) {
  64. uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
  65. if (result > 31) {
  66. revert InvalidShortString();
  67. }
  68. return result;
  69. }
  70. /**
  71. * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
  72. */
  73. function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
  74. if (bytes(value).length < 32) {
  75. return toShortString(value);
  76. } else {
  77. StorageSlot.getStringSlot(store).value = value;
  78. return ShortString.wrap(_FALLBACK_SENTINEL);
  79. }
  80. }
  81. /**
  82. * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
  83. */
  84. function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
  85. if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
  86. return toString(value);
  87. } else {
  88. return store;
  89. }
  90. }
  91. /**
  92. * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
  93. *
  94. * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
  95. * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
  96. */
  97. function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
  98. if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
  99. return byteLength(value);
  100. } else {
  101. return bytes(store).length;
  102. }
  103. }
  104. }