diff options
| author | gingerBill <bill@gingerbill.org> | 2019-03-15 18:30:39 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2019-03-15 18:30:39 +0000 |
| commit | fdb60b2d511f99dd9b8b427d032333992b10fd6a (patch) | |
| tree | f4149a91081cd71c13dca54d8f1e305a90456b48 /core/strings | |
| parent | 885c5dc8b75dd88d376627195a5468d12264bcc5 (diff) | |
Improve package strings
Diffstat (limited to 'core/strings')
| -rw-r--r-- | core/strings/builder.odin | 4 | ||||
| -rw-r--r-- | core/strings/strings.odin | 200 |
2 files changed, 203 insertions, 1 deletions
diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 547c456ba..922412638 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -17,6 +17,10 @@ destroy_builder :: proc(b: ^Builder) { clear(&b.buf); } +grow_builder :: proc(b: ^Builder, cap: int) { + reserve(&b.buf, cap); +} + builder_from_slice :: proc(backing: []byte) -> Builder { s := transmute(mem.Raw_Slice)backing; d := mem.Raw_Dynamic_Array{ diff --git a/core/strings/strings.odin b/core/strings/strings.odin index edf288c16..df3d69732 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -3,6 +3,21 @@ package strings import "core:mem" import "core:unicode/utf8" +clone :: proc(s: string, allocator := context.allocator) -> string { + c := make([]byte, len(s)+1, allocator); + copy(c, cast([]byte)s); + c[len(s)] = 0; + return string(c[:len(s)]); +} + +clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring { + c := make([]byte, len(s)+1, allocator); + copy(c, cast([]byte)s); + c[len(s)] = 0; + return cstring(&c[0]); +} + +@(deprecated="Please use 'strings.clone'") new_string :: proc(s: string, allocator := context.allocator) -> string { c := make([]byte, len(s)+1, allocator); copy(c, cast([]byte)s); @@ -10,6 +25,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string { return string(c[:len(s)]); } +@(deprecated="Please use 'strings.clone_to_cstring'") new_cstring :: proc(s: string, allocator := context.allocator) -> cstring { c := make([]byte, len(s)+1, allocator); copy(c, cast([]byte)s); @@ -46,6 +62,10 @@ contains_any :: proc(s, chars: string) -> bool { } +rune_count :: proc(s: string) -> int { + return utf8.rune_count_in_string(s); +} + equal_fold :: proc(s, t: string) -> bool { loop: for s != "" && t != "" { @@ -209,7 +229,7 @@ last_index_any :: proc(s, chars: string) -> int { count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case - return utf8.rune_count_in_string(s) + 1; + return rune_count(s) + 1; } if len(substr) == 1 { c := substr[0]; @@ -493,3 +513,181 @@ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)); } +// scrub scruvs invalid utf-8 characters and replaces them with the replacement string +// Adjacent invalid bytes are only replaced once +scrub :: proc(str: string, replacement: string, allocator := context.allocator) -> string { + b := make_builder(allocator);; + grow_builder(&b, len(str)); + + has_error := false; + cursor := 0; + origin := str; + + for len(str) > 0 { + r, w := utf8.decode_rune_in_string(str); + + if r == utf8.RUNE_ERROR { + if !has_error { + has_error = true; + write_string(&b, origin[:cursor]); + } + } else if has_error { + has_error = false; + write_string(&b, replacement); + + origin = origin[cursor:]; + cursor = 0; + } + + cursor += w; + str = str[w:]; + } + + return to_string(b); +} + + +reverse :: proc(str: string, allocator := context.allocator) -> string { + n := len(str); + buf := make([]byte, n); + i := 0; + + for len(str) > 0 { + _, w := utf8.decode_rune_in_string(str); + copy(buf[i:], cast([]byte)str[:w]); + str = str[w:]; + } + return string(buf); +} + +expand_tabs :: proc(str: string, tab_size: int, allocator := context.allocator) -> string { + if tab_size <= 0 { + panic("tab size must be positive"); + } + + if str == "" { + return ""; + } + + b := make_builder(allocator); + + column: int; + + for len(str) > 0 { + r, w := utf8.decode_rune_in_string(str); + + if r == '\t' { + expand := tab_size - column%tab_size; + + for i := 0; i < expand; i += 1 { + write_byte(&b, ' '); + } + + column += expand; + } else { + if r == '\n' { + column = 0; + } else { + column += w; + } + + write_rune(&b, r); + } + + str = str[w:]; + } + + return to_string(b); +} + + +partition :: proc(str, sep: string) -> (head, match, tail: string) { + i := index(str, sep); + if i == -1 { + head = str; + return; + } + + head = str[:i]; + match = str[i:i+len(sep)]; + tail = str[i+len(sep):]; + return; +} + +center_justify :: centre_justify; // NOTE(bill): Because Americans exist + +// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length +centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == "" { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b := make_builder(allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + write_pad_string(&b, pad, pad_len, remains/2); + write_string(&b, str); + write_pad_string(&b, pad, pad_len, (remains+1)/2); + + return to_string(b); +} + +// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length +left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == "" { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b := make_builder(allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + write_string(&b, str); + write_pad_string(&b, pad, pad_len, remains); + + return to_string(b); +} + +// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length +right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { + n := rune_count(str); + if n >= length || pad == "" { + return clone(str, allocator); + } + + remains := length-1; + pad_len := rune_count(pad); + + b := make_builder(allocator); + grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + + write_pad_string(&b, pad, pad_len, remains); + write_string(&b, str); + + return to_string(b); +} + + +@private +write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) { + repeats := remains / pad_len; + + for i := 0; i < repeats; i += 1 { + write_string(b, pad); + } + + remains = remains % pad_len; + + if remains != 0 do for i := 0; i < remains; i += 1 { + r, w := utf8.decode_rune_in_string(pad); + write_rune(b, r); + pad = pad[w:]; + } +} |