Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compact string encoding library #320

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

d1ll0n
Copy link

@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;

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.

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