{"id":13544030,"url":"https://github.com/onelivesleft/jai-string","last_synced_at":"2025-04-12T22:41:11.053Z","repository":{"id":171691242,"uuid":"348388894","full_name":"onelivesleft/jai-string","owner":"onelivesleft","description":"String modules for Jai","archived":false,"fork":false,"pushed_at":"2024-12-23T10:58:42.000Z","size":6040,"stargazers_count":21,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T16:47:42.817Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Batchfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/onelivesleft.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-03-16T14:58:42.000Z","updated_at":"2025-02-01T04:27:28.000Z","dependencies_parsed_at":"2024-01-16T17:02:34.639Z","dependency_job_id":"67864bf2-4e43-45a0-aad4-0e0319f4652c","html_url":"https://github.com/onelivesleft/jai-string","commit_stats":null,"previous_names":["onelivesleft/jai-string"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onelivesleft%2Fjai-string","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onelivesleft%2Fjai-string/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onelivesleft%2Fjai-string/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onelivesleft%2Fjai-string/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/onelivesleft","download_url":"https://codeload.github.com/onelivesleft/jai-string/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248643045,"owners_count":21138353,"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":[],"created_at":"2024-08-01T11:00:40.982Z","updated_at":"2025-04-12T22:41:11.031Z","avatar_url":"https://github.com/onelivesleft.png","language":"Batchfile","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"# jai-string\n\nModules present:\n\n* `Strings` Fairly performant and well reasoned api for working with strings.\n* `Scratch` A simple allocator for doing multiple operations in a row without grabbing more memory on each one. [Info](Scratch/module.jai)\n\nTo use clone the repo then copy the `Strings` folder into your `jai/modules` folder, or symlink them: `mklink /d c:\\jai\\modules\\Strings c:\\repos\\jai-string\\Strings`\nOptionally do the same for the `Scratch` folder if you want to have access to the scratch allocator.\n\n\n## Mechanics\n\n### Mutating in-place vs returning result\n\nAny proc in this module which writes to the string's data will have a pointer to the string as its parameter instead of just a string.  This gives a clear indicator of intent, and also delineates between different versions of a proc.  For example:\n```jai\nbar := to_upper(foo); // returns a copy of foo converted to uppercase (allocates!)\nto_upper(*foo);       // mutates foo in-place, converting it to uppercase.\n```\n\n### Generating strings\n\nAny proc which generates (allocates) a string will take an optional `null_terminate` parameter; setting this to true ensures the resulting string ends in `\\0`.\n\n\n### Character Comparison\n\nBy default characters being compared between two strings are compared using the `case_sensitive` function (unless you override it with the module parameter).  In this library any procedure which involves comparing strings will take a `character_compare` parameter in which you can specify a different procedure from the default.  For example:\n\n```jai\n    assert( contains(\"Hello\", \"h\")              == false );\n    assert( contains(\"Hello\", \"h\", ignore_case) == true  );\n```\n\nThe comparator is a struct; you can make your own like this:\n\n```jai\nare_numbers :: Character_Compare.{\n    .CUSTOM,\n    (a: u8, b: u8) -\u003e bool {\n        return (a \u003e= #char \"0\" \u0026\u0026 a \u003c= #char \"9\")\n            == (b \u003e= #char \"0\" \u0026\u0026 b \u003c= #char \"9\");\n    }\n};\n\nshare_case :: Character_Compare.{\n    .CUSTOM,\n    (a: u8, b: u8) -\u003e bool {\n        a_is_alpha := is_alpha(a);\n        if a_is_alpha != is_alpha(b)  return false;\n        if !a_is_alpha  return true;\n        return is_upper(a) == is_upper(b);\n    }\n};\n```\n\nThe two comparators built-in to the module are `case_sensitive`, `ignore_case`.\n\n*(The other two options to `.CUSTOM` are `.CASE_SENSITIVE` and `.IGNORE_CASE`: you may roll your own versions of those comparators if you wish, and by choosing the relevant identifier the correct SIMD optimisations will be invoked - however, there's not a lot of point in doing so...)*\n\n\n### Tool types: u8 / [] u8 / string / Index_Proc\n\nIn a string library it is often the case that you have a string which you are applying an operation to using a *tool* parameter.  In this library there will generally be four version of such procedures, the first three of which are the single parameters: `u8`, `[] u8`, `string`.  As tools these types behave consistently across the library:\n\n* `u8`\u003cbr\u003e\nThe single character specified will be used.\n\n* `[] u8`\u003cbr\u003e\nA match to any of the characters in the array will be used.\n\n* `string`\u003cbr\u003e\nThe exact string will be used: i.e. the characters specified in the sequence specified.\n\n\nFor example:\n```jai\n    assert( trim( \" apple  \",    #char \" \"        )  == \"apple\"  );\n    assert( trim( \"banana pear\", cast([]u8) \"ban\" )  == \" pear\" );\n    assert( trim( \"banana pear\", \"ban\"            )  == \"ana pear\" );\n```\n\nAdditionally, any time the tool is a `string` you may specify an `Index_Proc`.  An `Index_Proc` is a procedure with the signature:\n\n`(haystack: string, needle: string, initial_index: int, reversed: bool) -\u003e from_index: int, to_index: int, found: bool`\n\nThis allows you to feed an arbitrarily complex pattern match into the procedure you are using.  When using an `Index_Proc`, a character comparator is not used (as your own code is instead).\n\nFor example:\n```jai\n    question_mark_index :: (haystack: string, needle: string, initial_index: int, $$reversed: bool) -\u003e from_index: int, to_index: int, found: bool {\n        if reversed {\n            from_index, to_index, found := reverse_index_proc(question_mark_index, haystack, needle, initial_index);\n            return from_index, to_index, found;\n        }\n        else {\n            index := slice_index(haystack, initial_index);\n            if index \u003e= haystack.count  return -1, -1, false;\n\n            for haystack_index: index .. haystack.count - needle.count {\n                for needle_index: 0 .. needle.count - 1 {\n                    c := needle[needle_index];\n                    if c != #char \"?\" \u0026\u0026 c != haystack[haystack_index + needle_index]\n                        continue haystack_index;\n                }\n\n                return haystack_index, haystack_index + needle.count, true;\n            }\n\n            return -1, -1, false;\n        }\n    }\n\n    assert( starts_with(\"Hello World\", \"He??o\")                      == false );\n    assert( starts_with(\"Hello World\", \"He??o\", question_mark_index) == true );\n```\n\nNotice the use of `reverse_index_proc` to handle when the `reversed` parameter is set.  This is a library procedure that you can use if you don't want to write out the reverse algorithm yourself, but note that it is extremely inefficient!\n\nIn the docs below, any time a parameter of type `%Tool` is specified, it means there are four versions of the procedure, each corresponding to the behaviour described above (the fourth being that the %Tool is two parameters: `string`+`Index_Proc`).\n\n\u003chr\u003e\n\n### `#module_parameters`\n\n* `CHARACTER_COMPARE`\u003cbr\u003eDefault comparator used to check if two string characters are equal.  One of:\n    * `.CASE_SENSITIVE`\n    * `.IGNORE_CASE`\n\n* `INDEX_ALGORITHM`\u003cbr\u003eDetermines the default string search algorithm to use (they can be changed later using `set_index_algorithm`).  One of:\n    * `.SIMPLE`, `.SIMPLE_SSE2`, `.SIMPLE_AVX2`, `.SIMPLE_UNSAFE`\u003cbr\u003eSimplest algorithm, no memory overhead.\n    * `.BOYER_MOORE`, `.BOYER_MOORE_SSE2`, `.BOYER_MOORE_AVX2`\u003cbr\u003e[Boyer-Moore algorithm](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm).  Fastest tested scalar algorithm overall, has a small memory footprint that increases with needle size.\n    * `.KNUTH_MORRIS_PRATT`\u003cbr\u003e[Knuth-Morris-Pratt algorithm](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm). Another fast algorithm, with a similar memory footprint.\n\n#### A note on indexing algorithms\n\nThe indexing algorithm set by `set_index_algorithm` is used internally in the module for most operations: any time you call things like `first_index`, `replace`, `split` it will be employed.\nWhereas other functions in the library will utilize SIMD features (SSE2 \u0026 AVX2) when told to with the `set_simd_mode` command, you must explicitly set an index algorithm to use them if that is what you wish\u003csup\u003e*\u003c/sup\u003e:  the default indexing algorithm is scalar `Boyer-Moore`, because it is good on practically any dataset; a safe choice.  Choosing a different indexing algorithm can provide impressive performance improvements, but this depends on the dataset you are working on (the specific strings and substrings you are searching with).  SIMD algorithms can be orders of magnitude faster, but they can also be catastrophically slow when facing degenerate datasets.  If you want to get the most performance out of the library then you should choose an appropriate indexing algorithm for your dataset.\n\nTo help with this there is the `index_profile` tool (in the `tools/` folder): provide it with a file and a typical search string from your data and it will show you how each available algorithm performs with the data you are manipulating.\n\n\u003csup\u003e*\u003c/sup\u003e *(Though all the built-in indexing algorithms will detect if the needle is a single character long, and if so will use the relevant built-in character index algorithm, which will obey `set_simd_mode`)*\n\n\n### Procedures\n\n\n#### Configuration\n\n\n* `set_index_algorithm (first_index_proc := default_first_index, last_index_proc := default_last_index)`\u003cbr\u003e\nSets the index procedures used internally when searching through strings with strings (for `replace`, `split`, etc.)\n\n\n* `set_simd_mode (mode)`\u003cbr\u003eSets whether to use SIMD optimisations.  One of:\n    * `.OFF`\u003cbr\u003eDisables all SIMD optimisations, utilizing scalar code only.\n    * `.AUTO`\u003cbr\u003eUses the fastest SIMD instruction set available on the CPU.\n    * `.SSE2`\u003cbr\u003eUses SSE2 (128bit) optimisations.  This is the default.\n    * `.AVX2`\u003cbr\u003eUses AVX2 (256bit) optimisations.\n\n\n#### Substrings\n\n\n* `slice (str: string, from_index: int, [to_index: int]) -\u003e string, normalized_from_index: int, normalized_to_index: int`\u003cbr\u003e\nReturns the string inside `str`, between the specified indices.  You may use a negative index to specify backwards from the end of the string.  If you do not specify a `to_index` then it will include all characters up to the end of the string.  The last two return parameters are the positive indexes the slice ends up using, after validation.\n\n\n* `substring (str: string, from_index: int, [count: int]) -\u003e string, normalized_from_index: int, normalized_to_index: int`\u003cbr\u003e\nSame as `slice`, except instead of a `to_index` you specify a character count.  If you do not specify a `count` then it will include all characters up to the end of the string.\n\n\n* `slice_index (str: string, index: int) -\u003e normalized_index: int, well_formed: bool`\u003cbr\u003e\nReturns the validated and normalized index which would be used with the provided string, as well as whether the index was within the bounds of the string.\n\n\n* `raw_slice (str: string, from_index: int, to_index: int) -\u003e string`\u003cbr\u003e\nAs `slice`, but without any checking on the indices, and without being able to use negative indices (and thus faster).  If you do not specify a `to_index` then it will include all characters up to the end of the string.  Generally speaking, just use `slice` instead.\n\n\n* `raw_substring (str: string, from_index: int, count: int) -\u003e string`\u003cbr\u003e\nAs `substring`, but without any checking on the indices, and without being able to use negative indices (and thus faster).  If you do not specify a `count` then it will include all characters up to the end of the string.  Generally speaking, just use `substring` instead.\n\n\n* `trim (str: string) -\u003e string`\u003cbr\u003e\nReturns the substring of `str` with all characters from the start and end which are \u003c= `#char \" \"` removed (i.e. all whitespace and control codes).\n\n\n* `trim (str: string, tool: %Tool, character_compare := default_compare) -\u003e string`\u003cbr\u003e\nReturns the substring of `str` with all characters matching tool removed from the start and end.\n\n\n* `trim_start (str: string, tool: %Tool, character_compare := default_compare) -\u003e string`\u003cbr\u003e\nReturns the substring of `str` with all characters matching tool removed from the start.\n\n\n* `trim_end (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters matching tool removed from the end.\n\n\n* `trim_to (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters before the first instance and after the last instance of tool removed.  If tool is not found then the entire string is returned.\n\n\n* `trim_start_to (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters before the first instance of tool removed from the start.  If tool is not found then the entire string is returned.\n\n\n* `trim_end_to (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters after the last instance of tool removed from the end.  If tool is not found then the entire string is returned.\n\n\n* `trim_through (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters before the first instance and after the last instance of tool, as well as the tool itself, removed.  If tool is not found then the entire string is returned.\n\n\n* `trim_start_through (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters before the first instance of tool, and the tool, removed from the start.  If tool is not found then the entire string is returned.\n\n\n* `trim_end_through (str: string, tool: %Tool, character_compare := default_compare) -\u003e string, found: bool`\u003cbr\u003e\nReturns the substring of `str` with all characters after the last instance of tool, and the tool, removed from the end.  If tool is not found then the entire string is returned.\n\n\n#### Consuming\n\n\n* `advance_to (haystack: *string, needle: %Tool) -\u003e characters_skipped: int, found: bool`\u003cbr\u003e\nModifies `haystack` in-place, moving its start point forward until it hits `%Tool` (or empties).\n\n\n* `advance_through (haystack: *string, needle: %Tool) -\u003e characters_skipped: int, found: bool`\u003cbr\u003e\nModifies `haystack` in-place, moving its start point forward until it hits and reaches the end of `%Tool` (or empties).\n\n\n#### Splitting\n\n\nAll split procedures return an iterator (a for-expansion).  If you want the substrings to be in an array you can feed this iterator into `to_array` or `into_array`.\n\n\n* `split (text: string, separator: %Tool, skip_empty := false, max_results := 0, keep_separator := .NO, character_compare := default_compare)`\u003cbr\u003e\nUsed to iterate over `text` in a `for` loop, splitting the string by the chosen tool.\nIf `skip_empty` is set then your code will not be called with the empty string (i.e. when there are two consecutive `seperator`s).\nIf `max_results` is non-zero then `text` will only be split into at most that\nmany pieces.\nIf `keep_separator` is set to `.AS_PREFIX` or `.AS_POSTFIX` then the separator will be included in the strings, at the specified position.\n\nFor example:\n```jai\n    for word, index: split(\" aa  bb  cc dd  \", #char \" \", skip_empty = true, max_results = 3) {\n        if index == {\n            case  0; assert(word == \"aa\");\n            case  1; assert(word == \"bb\");\n            case  2; assert(word == \"cc\");\n            case  3; assert(false);\n        }\n    }\n\n    for word, index: split(\"Hello, World.\", \", \", keep_separator = .AS_POSTFIX) {\n        if index == {\n            case  0; assert(word == \"Hello, \");\n            case  1; assert(word == \"World.\");\n            case  2; assert(false);\n        }\n    }\n```\n\n\n* `split_into_two (text: string, separator: %Tool, keep_separator := .NO, character_compare := default_compare) -\u003e string, string`\u003cbr\u003e\nAs split with max_results set to 2, but returns the two strings directly rather than an iterator.\n\n\n* `to_array (splitter: $T/Splitter, reversed := false) -\u003e [..] string`\u003cbr\u003e\nExecutes the splitter, generating an array.\n```jai\nsplitter := split(\"How about a nice game of chess?\", #char \" \");\nwords := to_array(splitter,, temp);\nassert(words.count == 7);\n```\n\n\n* `into_array (array: *[] string, splitter: $T/Splitter, reversed := false, clear_unused := true) -\u003e [] string`\u003cbr\u003e\nExecutes the splitter and places its results into the array.  Returns an array_view over the array with the used count.\nIf `clear_unused` is set then any trailing slots in the array after the resulting count will be cleared.\n```jai\nparts : [20] string;\nview := into_array(*parts, split(\"How about a nice game of chess?\", #char \" \"));\nassert(view.count == 7);\n```\n\n\n* `count_split (text: string, count: int, max_results := 0)`\u003cbr\u003e\nAs `split`, except the string is split into sections with the specified `count`.\n\n\n* `index_split (text: string, indexes: .. int, skip_empty := false, max_results := 0)`\u003cbr\u003e\nAs `split`, except the string is split at the specified indices.\n\n\n* `line_split (text: string, keep_end := false, skip_empty := false, max_results := 0, keep_separator := .NO)`\u003cbr\u003e\nAs `split` using `#char \"\\n\"` as the tool, but will automatically handle windows vs unix file formats (i.e. will take care of `\"\\r\\n\"`).\n\n\n#### Querying\n\n\n* `first_index (haystack: string, needle: %Tool, start_index := 0, character_compare := default_compare) -\u003e index: int, found: bool, [to_index: int]`\u003cbr\u003e\nReturns the first index in `haystack` at which `needle` occurs, or `-1` if it does not occur.  `found` will be true if `needle` was found.  In the case when `%Tool` is an `Index_Proc`, `to_index` will be set to the index the pattern terminates at.\n\n\n* `last_index (haystack: string, needle: %Tool, start_index := 0, character_compare := default_compare) -\u003e index: int, found: bool, [to_index: int]`\u003cbr\u003e\nAs per `first_index`, but working backwards from the end of the `haystack`.\n\n\n* `contains (haystack: string, needle: %Tool, character_compare := default_compare) -\u003e bool`\u003cbr\u003e\nWhether `needle` occurs within `haystack`.\n\n\n* `count (haystack: string, needle: %Tool, character_compare := default_compare) -\u003e int`\u003cbr\u003e\nHow many times `needle` occurs within `haystack` (non-overlapping).\n\n\n* `equal (a: string, b: string, character_compare := default_compare) -\u003e bool`\u003cbr\u003e\nReturns whether the two strings are equal, using current or specified comparator.\n\n\n* `is_any (needle: u8, characters: [] u8, character_compare := default_compare) -\u003e bool`\u003cbr\u003e\nReturns whether `needle` is equal to any of `characters`.\n\n\n* `is_lower (char: u8) -\u003e bool`\u003cbr\u003e\nWhether `char` falls in the range `#char \"a\" - #char \"z\"`.\n\n\n* `is_upper (char: u8) -\u003e bool`\u003cbr\u003e\nWhether `char` falls in the range `#char \"A\" - #char \"Z\"`.\n\n\n* `starts_with (haystack: string, needle: %Tool, character_compare := default_compare) -\u003e bool`\u003cbr\u003e\nReturns whether `haystack` begins with `needle`.\n\n\n* `ends_with (haystack: string, needle: %Tool, character_compare := default_compare) -\u003e bool`\u003cbr\u003e\nReturns whether `haystack` ends with `needle`.\n\n\n#### Mutating\n\n\n* `pad_start (str: string, desired_count: int, pad_with := \" \", null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated at the beginning such that the string length reaches the `desired_count`.\nNote that `pad_with` can be multiple characters long (and in fact the default value is actually multiple spaces, for performance).\n\n\n* `pad_start (str: string, desired_count: int, pad_with: u8, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated at the beginning such that the string length reaches the `desired_count`.\n\n\n* `pad_end (str: string, desired_count: int, pad_with := \" \", null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated from the end such that the string length reaches the `desired_count`.\nNote that `pad_with` can be multiple characters long (and in fact the default value is actually multiple spaces, for performance).\n\n\n* `pad_end (str: string, desired_count: int, pad_with: u8, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated from the end such that the string length reaches the `desired_count`.\n\n\n* `pad (str: string, desired_count: int, pad_with := \" \", null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated from the begining *and* from the end such that the string length reaches the `desired_count`.\nNote that `pad_with` can be multiple characters long (and in fact the default value is actually multiple spaces, for performance).\n\n\n* `pad (str: string, desired_count: int, pad_with: u8, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with `pad_with` repeated from the begining *and* from the end such that the string length reaches the `desired_count`.\n\n\n* `repeat (str: string, times: int, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a string consisting of `str` repeated `times` times.\n\n\n* `replace (haystack: *string, needle: %Tool, replacement: u8, max_replacements := 0, null_terminate := false) -\u003e change_count: int`\u003cbr\u003e\nMutates the haystack in-place, replacing `needle` with the `replacement` character specified.\n\n\n* `replace (haystack: string, needle: %Tool, replacement: string,  max_replacements := 0, character_compare := default_compare, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with all (non-overlapping) instances of `needle` replaced with `replacement`.\nIf `max_replacements` is non-zero then at most that many replacements will be made (starting at the beginning of the string).\n\n\n* `reverse (str: *string)`\u003cbr\u003e\nReverses the characters in `str` in-place.\n\n\n* `reverse (str: string, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with the characters in the reverse order.\n\n\n* `to_upper (str: *string)`\u003cbr\u003e\nMutates `str` in-place, overwritting any lower-case characters with their upper-case equivalent.\n\n\n* `to_upper (str: string, null_terminate := false)`\u003cbr\u003e\nReturns a copy of `str` with all lower-case characters converted to their upper-case equivalent.\n\n\n* `to_lower (str: *string)`\u003cbr\u003e\nMutates `str` in-place, overwritting any upper-case characters with their lower-case equivalent.\n\n\n* `to_lower (str: string, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with all upper-case characters converted to their lower-case equivalent.\n\n\n* `to_capitalized (str: *string, preserve_caps := true)`\u003cbr\u003e\nSets the first letter of `str` to upper-case.  If `preserve_caps` is set to false, will set all following letters to lower-case.\n\n\n* `to_capitalized (str: string, preserve_caps := true, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of `str` with the first letter converted to upper-case.  If `preserve_caps` is disabled then all subsequent letters will be converted to lower-case.\n\n\n* `camel_from_snake (str: string, preserve_caps := false, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of underscore-separated `str`, changed into programmer CamelCase; i.e. with the leading letter, and every letter after an underscore, converted to upper-case, and with underscores removed.  If `preserve_caps` is enabled then the the underscore removal still happens, but the case is kept.\n\nFor example:\n```jai\n    assert( camel_from_snake(\"play_RTS\")       == \"playRts\" );\n    assert( camel_from_snake(\"play_RTS\", true) == \"playRTS\" );\n```\n\n\n* `snake_from_camel (str: string, preserve_caps := false, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a copy of CamelCased `str`, changed into programmer snake case; i.e. converted to lower-case, but split by `_` at each formerly upper-case letter edge.  If `preserve_caps` is enabled then the the split still happens, but the case is kept.\n\nFor example:\n```jai\n    assert( snake_from_camel(\"PlayRTS\")       == \"play_rts\" );\n    assert( snake_from_camel(\"PlayRTS\", true) == \"play_RTS\" );\n```\n\n\n#### Utilities\n\n\n* `char_as_string (char: *u8) -\u003e string`\u003cbr\u003e\nReturns a string representation of the single character provided.\n\n\n* `copy_string (str: string, null_terminate: bool) -\u003e string`\u003cbr\u003e\nReturns of a copy of `str`.\n\n\n* `join (strings: .. string, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a single string created by concatenating all the provided strings together.\n\n\n* `join (strings: [] string, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a single string, the result of joining all the strings in the `strings` array together.\n\n\n* `join (strings: [] string, separator: string|u8, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a single string, the result of joining all the strings in the `strings` array together with `separator` between them.\n\n\n* `join (strings: $T/Splitter, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a single string, the result of joining all the strings in the `strings` iterator together.\n\n\n* `join (strings: $T/Splitter, separator: string|u8, null_terminate := false) -\u003e string`\u003cbr\u003e\nReturns a single string, the result of joining all the strings in the `strings` iterator together with `separator` between them.\n\n\n* `apply_backslash (str: string, null_terminate := false) -\u003e string, well_formed: bool`\u003cbr\u003e\nConverts legal jai backslash escape sequences (i.e. `\\n`, `\\t`, etc) into their specified character. i.e. a two character string `\"\\n\"` will yield a single character string with byte value `10`;\n`well_formed` will be true if all backslash characters in `str` are followed by an appropriate escape sequence.\n\n\n* `escape (str: string, null_terminate := false) -\u003e string`\u003cbr\u003e\nReplaces the special characters which jai uses backslash escapes to represent with said backslash escape sequence. i.e. the single character string with byte value `10` will yield the two character string `\"\\n\"`\n\n\n* `reverse_index_proc (index_proc: Index_Proc, haystack: string, needle: string, boundary_index: int) -\u003e from_index: int, to_index: int, found: bool`\u003cbr\u003e\nCan be used to automatically make a reversed version of an `Index_Proc` (see `question_mark_index` example above).  It does so in an extremely inefficient way; if you care about the performance of the reverse search then you should code it directly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonelivesleft%2Fjai-string","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonelivesleft%2Fjai-string","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonelivesleft%2Fjai-string/lists"}