Skip to content

Add compact string encoding library#320

Open
d1ll0n wants to merge 5 commits intotransmissions11:mainfrom
d1ll0n:packed-strings
Open

Add compact string encoding library#320
d1ll0n wants to merge 5 commits intotransmissions11:mainfrom
d1ll0n:packed-strings

Conversation

@d1ll0n
Copy link
Copy Markdown

@d1ll0n d1ll0n commented Sep 18, 2022

Description

Adds utils/PackedStringLib.sol - a library for efficiently encoding strings that are <=31 bytes.

  • Encodes input strings by putting the length in the first byte and the body in the following 31 bytes.
  • Decodes strings by clearing two words in memory (to ensure the string has leading and trailing zeroes) and then writing the packed form to the last byte of the length buffer (first word), which also places the right-padded body of the string where it needs to be.

Solidity's built-in mechanism for accessing storage strings is efficient when strings can be any size, but in practice most tend to be quite small and static. This means that all of the additional code for determining the type of string (single or multiple word), looping over words and hashing each index to get the slot, goes totally unused. Surprisingly, this can add up to several hundred (or even thousand!) bytes depending on your optimization settings, and can really eat into available contract space.

Below is a comparison of the simplest use-case (retrieving a contract's name) with packed vs. standard strings and constant/immutable vs. storage.

contracts

I tried to stick to the coding conventions as much as possible, but this file has a lot more comments than most - had a bit of an ADHD moment. @transmissions11 if you prefer to keep to the clean aesthetic I'm happy to remove them.

I'm also a little new to Forge, so I'd appreciate a second opinion on whether there are any gaps in the testing.

Checklist

Ensure you completed all of the steps below before submitting your pull request:

  • Ran forge snapshot?
  • Ran npm run lint?
  • Ran forge test?

/// @dev Pack a 0-31 byte string into a bytes32.
/// @dev Will revert if string exceeds 31 bytes.
function packString(string memory unpackedString) internal pure returns (bytes32 packedString) {
uint256 length = bytes(unpackedString).length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you do something like bytes32(bytes(unpackedString))?

The conversion would truncate the string. So you'd probably need the if (length > 31) check if truncation is not good enough.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also would be nice to define a custom type type ShortString is bytes32;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants