{"id":16742957,"url":"https://github.com/dk1a/solidity-stringutils","last_synced_at":"2025-09-09T12:48:25.602Z","repository":{"id":64469371,"uuid":"575140781","full_name":"dk1a/solidity-stringutils","owner":"dk1a","description":"StrSlice \u0026 Slice library for Solidity","archived":false,"fork":false,"pushed_at":"2023-01-18T20:50:37.000Z","size":241,"stargazers_count":21,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-01T10:21:41.590Z","etag":null,"topics":["ethereum","library","slice","smart-contracts","solidity","string"],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dk1a.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-06T21:07:33.000Z","updated_at":"2023-11-20T16:19:34.000Z","dependencies_parsed_at":"2023-02-10T17:50:21.498Z","dependency_job_id":null,"html_url":"https://github.com/dk1a/solidity-stringutils","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dk1a%2Fsolidity-stringutils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dk1a%2Fsolidity-stringutils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dk1a%2Fsolidity-stringutils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dk1a%2Fsolidity-stringutils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dk1a","download_url":"https://codeload.github.com/dk1a/solidity-stringutils/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244165176,"owners_count":20409045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ethereum","library","slice","smart-contracts","solidity","string"],"created_at":"2024-10-13T01:25:27.217Z","updated_at":"2025-03-21T22:31:36.955Z","avatar_url":"https://github.com/dk1a.png","language":"Solidity","readme":"# StrSlice \u0026 Slice library for Solidity\n\n- Types: [StrSlice](src/StrSlice.sol) for strings, [Slice](src/Slice.sol) for bytes, [StrChar](src/StrChar.sol) for characters\n- [Gas efficient](https://github.com/dk1a/solidity-stringutils-gas)\n- Versioned releases, available for both foundry and hardhat\n- Simple imports, you only need e.g. `StrSlice` and `toSlice`\n- `StrSlice` enforces UTF-8 character boundaries; `StrChar` validates character encoding\n- Clean, well-documented and thoroughly-tested source code\n- Optional [PRBTest](https://github.com/paulrberg/prb-test) extension with assertions like `assertContains` and `assertLt` for both slices and native `bytes`, `string`\n- `Slice` and `StrSlice` are value types, not structs\n- Low-level functions like [memchr](src/utils/memchr.sol), [memcmp, memmove etc](src/utils/mem.sol)\n\n## Install\n\n### Node\n```sh\nyarn add @dk1a/solidity-stringutils\n```\n\n### Forge\n```sh\nforge install --no-commit dk1a/solidity-stringutils\n```\n\n## StrSlice\n\n```solidity\nimport { StrSlice, toSlice } from \"@dk1a/solidity-stringutils/src/StrSlice.sol\";\n\nusing { toSlice } for string;\n\n/// @dev Returns the content of brackets, or empty string if not found\nfunction extractFromBrackets(string memory stuffInBrackets) pure returns (StrSlice extracted) {\n    StrSlice s = stuffInBrackets.toSlice();\n    bool found;\n\n    (found, , s) = s.splitOnce(toSlice(\"(\"));\n    if (!found) return toSlice(\"\");\n\n    (found, s, ) = s.rsplitOnce(toSlice(\")\"));\n    if (!found) return toSlice(\"\");\n\n    return s;\n}\n/*\nassertEq(\n    extractFromBrackets(\"((1 + 2) + 3) + 4\"),\n    toSlice(\"(1 + 2) + 3\")\n);\n*/\n```\n\nSee [ExamplesTest](test/Examples.t.sol).\n\nInternally `StrSlice` uses `Slice` and extends it with logic for multibyte UTF-8 where necessary.\n\n| Method           | Description                                      |\n| ---------------- | ------------------------------------------------ |\n| `len`            | length in **bytes**                              |\n| `isEmpty`        | true if len == 0                                 |\n| `toString`       | copy slice contents to a **new** string          |\n| `keccak`         | equal to `keccak256(s.toString())`, but cheaper  |\n**concatenate**\n| `add`            | Concatenate 2 slices into a **new** string       |\n| `join`           | Join slice array on `self` as separator          |\n**compare**\n| `cmp`            | 0 for eq, \u003c 0 for lt, \u003e 0 for gt                 |\n| `eq`,`ne`        | ==, !=  (more efficient than cmp)                |\n| `lt`,`lte`       | \u003c, \u003c=                                            |\n| `gt`,`gte`       | \u003e, \u003e=                                            |\n**index**\n| `isCharBoundary` | true if given index is an allowed boundary       |\n| `get`            | get 1 UTF-8 character at given index             |\n| `splitAt`        | (slice[:index], slice[index:])                   |\n| `getSubslice`    | slice[start:end]                                 |\n**search**\n| `find`           | index of the start of the **first** match        |\n| `rfind`          | index of the start of the **last** match         |\n|                  | *return `type(uint256).max` for no matches*      |\n| `contains`       | true if a match is found                         |\n| `startsWith`     | true if starts with pattern                      |\n| `endsWith`       | true if ends with pattern                        |\n**modify**\n| `stripPrefix`    | returns subslice without the prefix              |\n| `stripSuffix`    | returns subslice without the suffix              |\n| `splitOnce`      | split into 2 subslices on the **first** match    |\n| `rsplitOnce`     | split into 2 subslices on the **last** match     |\n| `replacen`       | *experimental* replace `n` matches               |\n|                  | *replacen requires 0 \u003c pattern.len() \u003c= to.len()*|\n**iterate**\n| `chars`          | character iterator over the slice                |\n**ascii**\n| `isAscii`        | true if all chars are ASCII                      |\n**dangerous**\n| `asSlice`        | get underlying Slice                             |\n| `ptr`            | get memory pointer                               |\n\nIndexes are in **bytes**, not characters. Indexing methods revert if `isCharBoundary` is false.\n\n## StrCharsIter\n\n*Returned by `chars` method of `StrSlice`*\n\n```solidity\nimport { StrSlice, toSlice, StrCharsIter } from \"@dk1a/solidity-stringutils/src/StrSlice.sol\";\n\nusing { toSlice } for string;\n\n/// @dev Returns a StrSlice of `str` with the 2 first UTF-8 characters removed\n/// reverts on invalid UTF8\nfunction removeFirstTwoChars(string memory str) pure returns (StrSlice) {\n    StrCharsIter memory chars = str.toSlice().chars();\n    for (uint256 i; i \u003c 2; i++) {\n        if (chars.isEmpty()) break;\n        chars.next();\n    }\n    return chars.asStr();\n}\n/*\nassertEq(removeFirstTwoChars(unicode\"📎!こんにちは\"), unicode\"こんにちは\");\n*/\n```\n\n| Method           | Description                                      |\n| ---------------- | ------------------------------------------------ |\n| `asStr`          | get underlying StrSlice of the remainder         |\n| `len`            | remainder length in **bytes**                    |\n| `isEmpty`        | true if len == 0                                 |\n| `next`           | advance the iterator, return the next StrChar    |\n| `nextBack`       | advance from the back, return the next StrChar   |\n| `count`          | returns the number of UTF-8 characters           |\n| `validateUtf8`   | returns true if the sequence is valid UTF-8      |\n**dangerous**\n| `unsafeNext`     | advance unsafely, return the next StrChar        |\n| `unsafeCount`    | unsafely count chars, read the source for caveats|\n| `ptr`            | get memory pointer                               |\n\n`count`, `validateUtf8`, `unsafeCount` consume the iterator in O(n).\n\nSafe methods revert on an invalid UTF-8 byte sequence.\n\n`unsafeNext` does NOT check if the iterator is empty, may underflow! Does not revert on invalid UTF-8. If returned `StrChar` is invalid, it will have length 0. Otherwise length 1-4.\n\nInternally `next`, `unsafeNext`, `count` all use `_nextRaw`. It's very efficient, but very unsafe and complicated. Read the source and import it separately if you need it.\n\n## StrChar\n\nRepresents a single UTF-8 encoded character.\nInternally it's bytes32 with leading byte at MSB.\n\nIt's returned by some methods of `StrSlice` and `StrCharsIter`.\n\n| Method           | Description                                      |\n| ---------------- | ------------------------------------------------ |\n| `len`            | character length in bytes                        |\n| `toBytes32`      | returns the underlying `bytes32` value           |\n| `toString`       | copy the character to a new string               |\n| `toCodePoint`    | returns the unicode code point (`ord` in python) |\n| `cmp`            | 0 for eq, \u003c 0 for lt, \u003e 0 for gt                 |\n| `eq`,`ne`        | ==, !=                                           |\n| `lt`,`lte`       | \u003c, \u003c=                                            |\n| `gt`,`gte`       | \u003e, \u003e=                                            |\n| `isValidUtf8`    | usually true                                     |\n| `isAscii`        | true if the char is ASCII                        |\n\nImport `StrChar__` (static function lib) to use `StrChar__.fromCodePoint` for code point to `StrChar` conversion.\n\n`len` can return `0` *only* for invalid UTF-8 characters. But some invalid chars *may* have non-zero len! (use `isValidUtf8` to check validity). Note that `0x00` is a valid 1-byte UTF-8 character, its len is 1.\n\n`isValidUtf8` can be false if the character was formed with an unsafe method (fromUnchecked, wrap).\n\n## Slice\n\n```solidity\nimport { Slice, toSlice } from \"@dk1a/solidity-stringutils/src/Slice.sol\";\n\nusing { toSlice } for bytes;\n\nfunction findZeroByte(bytes memory b) pure returns (uint256 index) {\n    return b.toSlice().find(\n        bytes(hex\"00\").toSlice()\n    );\n}\n```\n\nSee `using {...} for Slice global` in the source for a function summary. Many are shared between `Slice` and `StrSlice`, but there are differences.\n\nInternally Slice has very minimal assembly, instead using `memcpy`, `memchr`, `memcmp` and others; if you need the low-level functions, see `src/utils/`.\n\n## Assertions (PRBTest extension)\n\n```solidity\nimport { PRBTest } from \"@prb/test/src/PRBTest.sol\";\nimport { Assertions } from \"@dk1a/solidity-stringutils/src/test/Assertions.sol\";\n\ncontract StrSliceTest is PRBTest, Assertions {\n    function testContains() public {\n        bytes memory b1 = \"12345\";\n        bytes memory b2 = \"3\";\n        assertContains(b1, b2);\n    }\n\n    function testLt() public {\n        string memory s1 = \"123\";\n        string memory s2 = \"124\";\n        assertLt(s1, s2);\n    }\n}\n```\n\nYou can completely ignore slices if all you want is e.g. `assertContains` for native `bytes`/`string`.\n\n## Acknowledgements\n- [Arachnid/solidity-stringutils](https://github.com/Arachnid/solidity-stringutils) - I basically wanted to make an updated version of solidity-stringutils\n- [rust](https://doc.rust-lang.org/core/index.html) - most similarities are in names and general structure; the implementation can't really be similar (solidity doesn't even have generics)\n- [paulrberg/prb-math](https://github.com/paulrberg/prb-math) - good template for solidity data structure libraries with `using {...} for ... global`\n- [brockelmore/memmove](https://github.com/brockelmore/memmove) - good assembly memory management examples","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdk1a%2Fsolidity-stringutils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdk1a%2Fsolidity-stringutils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdk1a%2Fsolidity-stringutils/lists"}