pax_global_header00006660000000000000000000000064126765017340014525gustar00rootroot0000000000000052 comment=9a33e5f11b96f56507319d2f712c5dab02ecd49f golang-github-naoina-go-stringutil-0.1.0/000077500000000000000000000000001267650173400203225ustar00rootroot00000000000000golang-github-naoina-go-stringutil-0.1.0/.travis.yml000066400000000000000000000002201267650173400224250ustar00rootroot00000000000000language: go go: - 1.4 - 1.5 - tip install: - go get -v github.com/naoina/go-stringutil script: - go test -v -bench . -benchmem ./... golang-github-naoina-go-stringutil-0.1.0/LICENSE000066400000000000000000000020621267650173400213270ustar00rootroot00000000000000Copyright (c) 2015 Naoya Inada Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-naoina-go-stringutil-0.1.0/README.md000066400000000000000000000004431267650173400216020ustar00rootroot00000000000000# stringutil [![Build Status](https://travis-ci.org/naoina/go-stringutil.svg?branch=master)](https://travis-ci.org/naoina/go-stringutil) ## Installation go get -u github.com/naoina/go-stringutil ## Documentation See https://godoc.org/github.com/naoina/go-stringutil ## License MIT golang-github-naoina-go-stringutil-0.1.0/da.go000066400000000000000000000125201267650173400212350ustar00rootroot00000000000000package stringutil import ( "fmt" "sort" "unicode/utf8" ) const ( terminationCharacter = '#' ) func mustDoubleArray(da *doubleArray, err error) *doubleArray { if err != nil { panic(err) } return da } func (da *doubleArray) Build(keys []string) error { records := makeRecords(keys) if err := da.build(records, 1, 0, make(map[int]struct{})); err != nil { return err } return nil } type doubleArray struct { bc []baseCheck node []int } func newDoubleArray(keys []string) (*doubleArray, error) { da := &doubleArray{ bc: []baseCheck{0}, node: []int{-1}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node. } if err := da.Build(keys); err != nil { return nil, err } return da, nil } // baseCheck contains BASE, CHECK and Extra flags. // From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK. // // BASE (22bit) | Extra flags (2bit) | CHECK (8bit) // |----------------------|--|--------| // 32 10 8 0 type baseCheck uint32 func (bc baseCheck) Base() int { return int(bc >> 10) } func (bc *baseCheck) SetBase(base int) { *bc |= baseCheck(base) << 10 } func (bc baseCheck) Check() byte { return byte(bc) } func (bc *baseCheck) SetCheck(check byte) { *bc |= baseCheck(check) } func (bc baseCheck) IsEmpty() bool { return bc&0xfffffcff == 0 } func (da *doubleArray) Lookup(path string) (length int) { idx := 1 tmpIdx := idx for i := 0; i < len(path); i++ { c := path[i] tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { break } idx = tmpIdx } if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { return da.node[da.bc[next].Base()] } return -1 } func (da *doubleArray) LookupByBytes(path []byte) (length int) { idx := 1 tmpIdx := idx for i := 0; i < len(path); i++ { c := path[i] tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { break } idx = tmpIdx } if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { return da.node[da.bc[next].Base()] } return -1 } func (da *doubleArray) build(srcs []record, idx, depth int, usedBase map[int]struct{}) error { sort.Stable(recordSlice(srcs)) base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase) if err != nil { return err } if leaf != nil { da.bc[idx].SetBase(len(da.node)) da.node = append(da.node, leaf.value) } for _, sib := range siblings { da.setCheck(da.nextIndex(base, sib.c), sib.c) } for _, sib := range siblings { if err := da.build(srcs[sib.start:sib.end], da.nextIndex(base, sib.c), depth+1, usedBase); err != nil { return err } } return nil } func (da *doubleArray) setBase(i, base int) { da.bc[i].SetBase(base) } func (da *doubleArray) setCheck(i int, check byte) { da.bc[i].SetCheck(check) } func (da *doubleArray) findEmptyIndex(start int) int { i := start for ; i < len(da.bc); i++ { if da.bc[i].IsEmpty() { break } } return i } // findBase returns good BASE. func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) { for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) { base = da.nextIndex(idx, firstChar) if _, used := usedBase[base]; used { continue } i := 0 for ; i < len(siblings); i++ { next := da.nextIndex(base, siblings[i].c) if len(da.bc) <= next { da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...) } if !da.bc[next].IsEmpty() { break } } if i == len(siblings) { break } } usedBase[base] = struct{}{} return base } func (da *doubleArray) arrange(records []record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) { siblings, leaf, err = makeSiblings(records, depth) if err != nil { return -1, nil, nil, err } if len(siblings) < 1 { return -1, nil, leaf, nil } base = da.findBase(siblings, idx, usedBase) da.setBase(idx, base) return base, siblings, leaf, err } type sibling struct { start int end int c byte } func (da *doubleArray) nextIndex(base int, c byte) int { return base ^ int(c) } func makeSiblings(records []record, depth int) (sib []sibling, leaf *record, err error) { var ( pc byte n int ) for i, r := range records { if len(r.key) <= depth { leaf = &r continue } c := r.key[depth] switch { case pc < c: sib = append(sib, sibling{start: i, c: c}) case pc == c: continue default: return nil, nil, fmt.Errorf("stringutil: BUG: records hasn't been sorted") } if n > 0 { sib[n-1].end = i } pc = c n++ } if n == 0 { return nil, leaf, nil } sib[n-1].end = len(records) return sib, leaf, nil } type record struct { key string value int } func makeRecords(srcs []string) (records []record) { termChar := string(terminationCharacter) for _, s := range srcs { records = append(records, record{ key: string(s + termChar), value: utf8.RuneCountInString(s), }) } return records } type recordSlice []record func (rs recordSlice) Len() int { return len(rs) } func (rs recordSlice) Less(i, j int) bool { return rs[i].key < rs[j].key } func (rs recordSlice) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] } golang-github-naoina-go-stringutil-0.1.0/strings.go000066400000000000000000000200121267650173400223350ustar00rootroot00000000000000package stringutil import ( "sync" "unicode" "unicode/utf8" ) var ( mu sync.Mutex // Based on https://github.com/golang/lint/blob/32a87160691b3c96046c0c678fe57c5bef761456/lint.go#L702 commonInitialismMap = map[string]struct{}{ "API": struct{}{}, "ASCII": struct{}{}, "CPU": struct{}{}, "CSRF": struct{}{}, "CSS": struct{}{}, "DNS": struct{}{}, "EOF": struct{}{}, "GUID": struct{}{}, "HTML": struct{}{}, "HTTP": struct{}{}, "HTTPS": struct{}{}, "ID": struct{}{}, "IP": struct{}{}, "JSON": struct{}{}, "LHS": struct{}{}, "QPS": struct{}{}, "RAM": struct{}{}, "RHS": struct{}{}, "RPC": struct{}{}, "SLA": struct{}{}, "SMTP": struct{}{}, "SQL": struct{}{}, "SSH": struct{}{}, "TCP": struct{}{}, "TLS": struct{}{}, "TTL": struct{}{}, "UDP": struct{}{}, "UI": struct{}{}, "UID": struct{}{}, "UUID": struct{}{}, "URI": struct{}{}, "URL": struct{}{}, "UTF8": struct{}{}, "VM": struct{}{}, "XML": struct{}{}, "XSRF": struct{}{}, "XSS": struct{}{}, } commonInitialisms = keys(commonInitialismMap) commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) longestLen = longestLength(commonInitialisms) shortestLen = shortestLength(commonInitialisms, longestLen) ) // ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case. // It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'. func ToUpperCamelCase(s string) string { if s == "" { return "" } upper := true start := 0 result := make([]byte, 0, len(s)) var runeBuf [utf8.UTFMax]byte var initialism []byte for _, c := range s { if c == '_' { upper = true candidate := string(result[start:]) initialism = initialism[:0] for _, r := range candidate { if r < utf8.RuneSelf { initialism = append(initialism, toUpperASCII(byte(r))) } else { n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) initialism = append(initialism, runeBuf[:n]...) } } if length := commonInitialism.LookupByBytes(initialism); length > 0 { result = append(result[:start], initialism...) } start = len(result) continue } if upper { if c < utf8.RuneSelf { result = append(result, toUpperASCII(byte(c))) } else { n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(c)) result = append(result, runeBuf[:n]...) } upper = false continue } if c < utf8.RuneSelf { result = append(result, byte(c)) } else { n := utf8.EncodeRune(runeBuf[:], c) result = append(result, runeBuf[:n]...) } } candidate := string(result[start:]) initialism = initialism[:0] for _, r := range candidate { if r < utf8.RuneSelf { initialism = append(initialism, toUpperASCII(byte(r))) } else { n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) initialism = append(initialism, runeBuf[:n]...) } } if length := commonInitialism.LookupByBytes(initialism); length > 0 { result = append(result[:start], initialism...) } return string(result) } // ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for // only the ASCII characters. // ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if // contains non-ASCII characters. func ToUpperCamelCaseASCII(s string) string { if s == "" { return "" } upper := true start := 0 result := make([]byte, 0, len(s)) var initialism []byte for i := 0; i < len(s); i++ { c := s[i] if c == '_' { upper = true candidate := result[start:] initialism = initialism[:0] for _, b := range candidate { initialism = append(initialism, toUpperASCII(b)) } if length := commonInitialism.LookupByBytes(initialism); length > 0 { result = append(result[:start], initialism...) } start = len(result) continue } if upper { result = append(result, toUpperASCII(c)) upper = false continue } result = append(result, c) } candidate := result[start:] initialism = initialism[:0] for _, b := range candidate { initialism = append(initialism, toUpperASCII(b)) } if length := commonInitialism.LookupByBytes(initialism); length > 0 { result = append(result[:start], initialism...) } return string(result) } // ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case. // It will insert letter of '_' at position of previous letter of uppercase and all // letters convert to lower case. // ToSnakeCase does not insert '_' letter into a common initialism word like ID, URL and so on. func ToSnakeCase(s string) string { if s == "" { return "" } result := make([]byte, 0, len(s)) var runeBuf [utf8.UTFMax]byte var j, skipCount int for i, c := range s { if i < skipCount { continue } if unicode.IsUpper(c) { if i != 0 { result = append(result, '_') } next := nextIndex(j, len(s)) if length := commonInitialism.Lookup(s[j:next]); length > 0 { for _, r := range s[j : j+length] { if r < utf8.RuneSelf { result = append(result, toLowerASCII(byte(r))) } else { n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(r)) result = append(result, runeBuf[:n]...) } } j += length - 1 skipCount = i + length continue } } if c < utf8.RuneSelf { result = append(result, toLowerASCII(byte(c))) } else { n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(c)) result = append(result, runeBuf[:n]...) } j++ } return string(result) } // ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII // characters. // ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if // contains non-ASCII characters. func ToSnakeCaseASCII(s string) string { if s == "" { return "" } result := make([]byte, 0, len(s)) for i := 0; i < len(s); i++ { c := s[i] if isUpperASCII(c) { if i != 0 { result = append(result, '_') } if k := i + shortestLen - 1; k < len(s) && isUpperASCII(s[k]) { if length := commonInitialism.Lookup(s[i:nextIndex(i, len(s))]); length > 0 { for j, buf := 0, s[i:i+length]; j < len(buf); j++ { result = append(result, toLowerASCII(buf[j])) } i += length - 1 continue } } } result = append(result, toLowerASCII(c)) } return string(result) } // AddCommonInitialism adds ss to list of common initialisms. func AddCommonInitialism(ss ...string) { mu.Lock() defer mu.Unlock() for _, s := range ss { commonInitialismMap[s] = struct{}{} } commonInitialisms = keys(commonInitialismMap) commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) longestLen = longestLength(commonInitialisms) shortestLen = shortestLength(commonInitialisms, longestLen) } // DelCommonInitialism deletes ss from list of common initialisms. func DelCommonInitialism(ss ...string) { mu.Lock() defer mu.Unlock() for _, s := range ss { delete(commonInitialismMap, s) } commonInitialisms = keys(commonInitialismMap) commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) longestLen = longestLength(commonInitialisms) shortestLen = shortestLength(commonInitialisms, longestLen) } func isUpperASCII(c byte) bool { return 'A' <= c && c <= 'Z' } func isLowerASCII(c byte) bool { return 'a' <= c && c <= 'z' } func toUpperASCII(c byte) byte { if isLowerASCII(c) { return c - ('a' - 'A') } return c } func toLowerASCII(c byte) byte { if isUpperASCII(c) { return c + 'a' - 'A' } return c } func nextIndex(i, maxlen int) int { if n := i + longestLen; n < maxlen { return n } return maxlen } func keys(m map[string]struct{}) []string { result := make([]string, 0, len(m)) for k := range m { result = append(result, k) } return result } func shortestLength(strs []string, shortest int) int { for _, s := range strs { if candidate := utf8.RuneCountInString(s); candidate < shortest { shortest = candidate } } return shortest } func longestLength(strs []string) (longest int) { for _, s := range strs { if candidate := utf8.RuneCountInString(s); candidate > longest { longest = candidate } } return longest } golang-github-naoina-go-stringutil-0.1.0/strings_bench_test.go000066400000000000000000000013711267650173400245420ustar00rootroot00000000000000package stringutil_test import ( "testing" "github.com/naoina/go-stringutil" ) var benchcaseForCamelCase = "the_quick_brown_fox_jumps_over_the_lazy_dog" func BenchmarkToUpperCamelCase(b *testing.B) { for i := 0; i < b.N; i++ { stringutil.ToUpperCamelCase(benchcaseForCamelCase) } } func BenchmarkToUpperCamelCaseASCII(b *testing.B) { for i := 0; i < b.N; i++ { stringutil.ToUpperCamelCaseASCII(benchcaseForCamelCase) } } var benchcaseForSnakeCase = "TheQuickBrownFoxJumpsOverTheLazyDog" func BenchmarkToSnakeCase(b *testing.B) { for i := 0; i < b.N; i++ { stringutil.ToSnakeCase(benchcaseForSnakeCase) } } func BenchmarkToSnakeCaseASCII(b *testing.B) { for i := 0; i < b.N; i++ { stringutil.ToSnakeCaseASCII(benchcaseForSnakeCase) } } golang-github-naoina-go-stringutil-0.1.0/strings_test.go000066400000000000000000000127451267650173400234120ustar00rootroot00000000000000package stringutil_test import ( "reflect" "testing" "github.com/naoina/go-stringutil" ) var commonTestCasesForToUpperCamelCase = []struct { input, expect string }{ {"", ""}, {"thequickbrownfoxoverthelazydog", "Thequickbrownfoxoverthelazydog"}, {"thequickbrownfoxoverthelazydoG", "ThequickbrownfoxoverthelazydoG"}, {"thequickbrownfoxoverthelazydo_g", "ThequickbrownfoxoverthelazydoG"}, {"TheQuickBrownFoxJumpsOverTheLazyDog", "TheQuickBrownFoxJumpsOverTheLazyDog"}, {"the_quick_brown_fox_jumps_over_the_lazy_dog", "TheQuickBrownFoxJumpsOverTheLazyDog"}, {"the_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog", "TheQuickBrownFoxJumpsOverTheLazyDog"}, {"api_server", "APIServer"}, {"a_t_api", "ATAPI"}, {"atapi", "Atapi"}, {"web_ui", "WebUI"}, {"api", "API"}, {"ascii", "ASCII"}, {"cpu", "CPU"}, {"csrf", "CSRF"}, {"css", "CSS"}, {"dns", "DNS"}, {"eof", "EOF"}, {"guid", "GUID"}, {"html", "HTML"}, {"http", "HTTP"}, {"https", "HTTPS"}, {"id", "ID"}, {"ip", "IP"}, {"json", "JSON"}, {"lhs", "LHS"}, {"qps", "QPS"}, {"ram", "RAM"}, {"rhs", "RHS"}, {"rpc", "RPC"}, {"sla", "SLA"}, {"smtp", "SMTP"}, {"sql", "SQL"}, {"ssh", "SSH"}, {"tcp", "TCP"}, {"tls", "TLS"}, {"ttl", "TTL"}, {"udp", "UDP"}, {"ui", "UI"}, {"uid", "UID"}, {"uuid", "UUID"}, {"uri", "URI"}, {"url", "URL"}, {"utf8", "UTF8"}, {"vm", "VM"}, {"xml", "XML"}, {"xsrf", "XSRF"}, {"xss", "XSS"}, } func TestToUpperCamelCase(t *testing.T) { for _, v := range append(commonTestCasesForToUpperCamelCase, []struct { input, expect string }{ {"the_quick_brown_fox_over_the_lazy_dog", "TheQuickBrownFoxOverTheLazyDog"}, }...) { actual := stringutil.ToUpperCamelCase(v.input) expect := v.expect if !reflect.DeepEqual(actual, expect) { t.Errorf(`stringutil.ToUpperCamelCase(%#v) => %#v; want %#v`, v.input, actual, expect) } } } func TestToUpperCamelCaseASCII(t *testing.T) { for _, v := range commonTestCasesForToUpperCamelCase { actual := stringutil.ToUpperCamelCaseASCII(v.input) expect := v.expect if !reflect.DeepEqual(actual, expect) { t.Errorf(`stringutil.ToUpperCamelCaseASCII(%#v) => %#v; want %#v`, v.input, actual, expect) } } } var commonTestCasesForToSnakeCase = []struct { input, expect string }{ {"", ""}, {"thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"}, {"Thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"}, {"ThequickbrownfoxjumpsoverthelazydoG", "thequickbrownfoxjumpsoverthelazydo_g"}, {"TheQuickBrownFoxJumpsOverTheLazyDog", "the_quick_brown_fox_jumps_over_the_lazy_dog"}, {"the_quick_brown_fox_jumps_over_the_lazy_dog", "the_quick_brown_fox_jumps_over_the_lazy_dog"}, {"APIServer", "api_server"}, {"ATAPI", "a_t_api"}, {"Atapi", "atapi"}, {"WebUI", "web_ui"}, {"API", "api"}, {"ASCII", "ascii"}, {"CPU", "cpu"}, {"CSRF", "csrf"}, {"CSS", "css"}, {"DNS", "dns"}, {"EOF", "eof"}, {"GUID", "guid"}, {"HTML", "html"}, {"HTTP", "http"}, {"HTTPS", "https"}, {"ID", "id"}, {"ip", "ip"}, {"JSON", "json"}, {"LHS", "lhs"}, {"QPS", "qps"}, {"RAM", "ram"}, {"RHS", "rhs"}, {"RPC", "rpc"}, {"SLA", "sla"}, {"SMTP", "smtp"}, {"SQL", "sql"}, {"SSH", "ssh"}, {"TCP", "tcp"}, {"TLS", "tls"}, {"TTL", "ttl"}, {"UDP", "udp"}, {"UI", "ui"}, {"UID", "uid"}, {"UUID", "uuid"}, {"URI", "uri"}, {"URL", "url"}, {"UTF8", "utf8"}, {"VM", "vm"}, {"XML", "xml"}, {"XSRF", "xsrf"}, {"XSS", "xss"}, } func TestToSnakeCase(t *testing.T) { for _, v := range append(commonTestCasesForToSnakeCase, []struct { input, expect string }{ {"TheQuickBrownFoxOverTheLazyDog", "the_quick_brown_fox_over_the_lazy_dog"}, }...) { actual := stringutil.ToSnakeCase(v.input) expect := v.expect if !reflect.DeepEqual(actual, expect) { t.Errorf(`stringutil.ToSnakeCase(%#v) => %#v; want %#v`, v.input, actual, expect) } } } func TestToSnakeCaseASCII(t *testing.T) { for _, v := range commonTestCasesForToSnakeCase { actual := stringutil.ToSnakeCaseASCII(v.input) expect := v.expect if !reflect.DeepEqual(actual, expect) { t.Errorf(`stringutil.ToSnakeCaseASCII(%#v) => %#v; want %#v`, v.input, actual, expect) } } } func TestAddCommonInitialismWithToUpperCamelCase(t *testing.T) { input := "test_case" actual := stringutil.ToUpperCamelCase(input) expect := "TestCase" if !reflect.DeepEqual(actual, expect) { t.Errorf(`ToUpperCamelCase(%#v) with AddCommonInitialism => %#v; want %#v`, input, actual, expect) } stringutil.AddCommonInitialism("TEST", "CASE") defer stringutil.DelCommonInitialism("TEST", "CASE") actual = stringutil.ToUpperCamelCase(input) expect = "TESTCASE" if !reflect.DeepEqual(actual, expect) { t.Errorf(`ToUpperCamelCase(%#v) with AddCommonInitialism => %#v; want %#v`, input, actual, expect) } } func TestAddCommonInitialismWithToSnakeCase(t *testing.T) { input := "TESTCase" actual := stringutil.ToSnakeCase(input) expect := "t_e_s_t_case" if !reflect.DeepEqual(actual, expect) { t.Errorf(`ToSnakeCase(%#v) with AddCommonInitialism => %#v; want %#v`, input, actual, expect) } stringutil.AddCommonInitialism("TEST", "CASE") defer stringutil.DelCommonInitialism("TEST", "CASE") actual = stringutil.ToSnakeCase(input) expect = "test_case" if !reflect.DeepEqual(actual, expect) { t.Errorf(`ToSnakeCase(%#v) with AddCommonInitialism => %#v; want %#v`, input, actual, expect) } }