From 1a57267a17c2fc17fb6e104846fabc3e363c326c Mon Sep 17 00:00:00 2001 From: Emile Date: Fri, 16 Aug 2024 19:50:26 +0200 Subject: initial commit --- vendor/modernc.org/strutil/strutil.go | 733 ++++++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 vendor/modernc.org/strutil/strutil.go (limited to 'vendor/modernc.org/strutil/strutil.go') diff --git a/vendor/modernc.org/strutil/strutil.go b/vendor/modernc.org/strutil/strutil.go new file mode 100644 index 0000000..7d07b79 --- /dev/null +++ b/vendor/modernc.org/strutil/strutil.go @@ -0,0 +1,733 @@ +// Copyright (c) 2014 The sortutil Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package strutil collects utils supplemental to the standard strings package. +package strutil // import "modernc.org/strutil" + +import ( + "bytes" + "encoding/base32" + "encoding/base64" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" +) + +// Base32ExtDecode decodes base32 extended (RFC 4648) text to binary data. +func Base32ExtDecode(text []byte) (data []byte, err error) { + n := base32.HexEncoding.DecodedLen(len(text)) + data = make([]byte, n) + decoder := base32.NewDecoder(base32.HexEncoding, bytes.NewBuffer(text)) + if n, err = decoder.Read(data); err != nil { + n = 0 + } + data = data[:n] + return +} + +// Base32ExtEncode encodes binary data to base32 extended (RFC 4648) encoded text. +func Base32ExtEncode(data []byte) (text []byte) { + n := base32.HexEncoding.EncodedLen(len(data)) + buf := bytes.NewBuffer(make([]byte, 0, n)) + encoder := base32.NewEncoder(base32.HexEncoding, buf) + encoder.Write(data) + encoder.Close() + if buf.Len() != n { + panic("internal error") + } + return buf.Bytes() +} + +// Base64Decode decodes base64 text to binary data. +func Base64Decode(text []byte) (data []byte, err error) { + n := base64.StdEncoding.DecodedLen(len(text)) + data = make([]byte, n) + decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(text)) + if n, err = decoder.Read(data); err != nil { + n = 0 + } + data = data[:n] + return +} + +// Base64Encode encodes binary data to base64 encoded text. +func Base64Encode(data []byte) (text []byte) { + n := base64.StdEncoding.EncodedLen(len(data)) + buf := bytes.NewBuffer(make([]byte, 0, n)) + encoder := base64.NewEncoder(base64.StdEncoding, buf) + encoder.Write(data) + encoder.Close() + if buf.Len() != n { + panic("internal error") + } + return buf.Bytes() +} + +// Formatter is an io.Writer extended by a fmt.Printf like function Format +type Formatter interface { + io.Writer + Format(format string, args ...interface{}) (n int, errno error) +} + +type indentFormatter struct { + io.Writer + indent []byte + indentLevel int + state int +} + +const ( + st0 = iota + stBOL + stPERC + stBOLPERC +) + +// IndentFormatter returns a new Formatter which interprets %i and %u in the +// Format() format string as indent and undent commands. The commands can +// nest. The Formatter writes to io.Writer 'w' and inserts one 'indent' +// string per current indent level value. +// Behaviour of commands reaching negative indent levels is undefined. +// IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) +// output: +// abc3%e +// x +// y +// z +// The Go quoted string literal form of the above is: +// "abc%%e\n\tx\n\tx\nz\n" +// The commands can be scattered between separate invocations of Format(), +// i.e. the formatter keeps track of the indent level and knows if it is +// positioned on start of a line and should emit indentation(s). +// The same output as above can be produced by e.g.: +// f := IndentFormatter(os.Stdout, " ") +// f.Format("abc%d%%e%i\nx\n", 3) +// f.Format("y\n%uz\n") +func IndentFormatter(w io.Writer, indent string) Formatter { + return &indentFormatter{w, []byte(indent), 0, stBOL} +} + +func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) { + buf := []byte{} + for i := 0; i < len(format); i++ { + c := format[i] + switch f.state { + case st0: + switch c { + case '\n': + cc := c + if flat && f.indentLevel != 0 { + cc = ' ' + } + buf = append(buf, cc) + f.state = stBOL + case '%': + f.state = stPERC + default: + buf = append(buf, c) + } + case stBOL: + switch c { + case '\n': + cc := c + if flat && f.indentLevel != 0 { + cc = ' ' + } + buf = append(buf, cc) + case '%': + f.state = stBOLPERC + default: + if !flat { + for i := 0; i < f.indentLevel; i++ { + buf = append(buf, f.indent...) + } + } + buf = append(buf, c) + f.state = st0 + } + case stBOLPERC: + switch c { + case 'i': + f.indentLevel++ + f.state = stBOL + case 'u': + f.indentLevel-- + f.state = stBOL + default: + if !flat { + for i := 0; i < f.indentLevel; i++ { + buf = append(buf, f.indent...) + } + } + buf = append(buf, '%', c) + f.state = st0 + } + case stPERC: + switch c { + case 'i': + f.indentLevel++ + f.state = st0 + case 'u': + f.indentLevel-- + f.state = st0 + default: + buf = append(buf, '%', c) + f.state = st0 + } + default: + panic("unexpected state") + } + } + switch f.state { + case stPERC, stBOLPERC: + buf = append(buf, '%') + } + return f.Write([]byte(fmt.Sprintf(string(buf), args...))) +} + +func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) { + return f.format(false, format, args...) +} + +type flatFormatter indentFormatter + +// FlatFormatter returns a newly created Formatter with the same functionality as the one returned +// by IndentFormatter except it allows a newline in the 'format' string argument of Format +// to pass through iff indent level is currently zero. +// +// If indent level is non-zero then such new lines are changed to a space character. +// There is no indent string, the %i and %u format verbs are used solely to determine the indent level. +// +// The FlatFormatter is intended for flattening of normally nested structure textual representation to +// a one top level structure per line form. +// FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) +// output in the form of a Go quoted string literal: +// "abc3%%e x y z\n" +func FlatFormatter(w io.Writer) Formatter { + return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter)) +} + +func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) { + return (*indentFormatter)(f).format(true, format, args...) +} + +// Pool handles aligning of strings having equal values to the same string instance. +// Intended use is to conserve some memory e.g. where a large number of identically valued strings +// with non identical backing arrays may exists in several semantically distinct instances of some structs. +// Pool is *not* concurrent access safe. It doesn't handle common prefix/suffix aligning, +// e.g. having s1 == "abc" and s2 == "bc", s2 is not automatically aligned as s1[1:]. +type Pool struct { + pool map[string]string +} + +// NewPool returns a newly created Pool. +func NewPool() *Pool { + return &Pool{map[string]string{}} +} + +// Align returns a string with the same value as its argument. It guarantees that +// all aligned strings share a single instance in memory. +func (p *Pool) Align(s string) string { + if a, ok := p.pool[s]; ok { + return a + } + + s = StrPack(s) + p.pool[s] = s + return s +} + +// Count returns the number of items in the pool. +func (p *Pool) Count() int { + return len(p.pool) +} + +// GoPool is a concurrent access safe version of Pool. +type GoPool struct { + pool map[string]string + rwm *sync.RWMutex +} + +// NewGoPool returns a newly created GoPool. +func NewGoPool() (p *GoPool) { + return &GoPool{map[string]string{}, &sync.RWMutex{}} +} + +// Align returns a string with the same value as its argument. It guarantees that +// all aligned strings share a single instance in memory. +func (p *GoPool) Align(s string) (y string) { + if s != "" { + p.rwm.RLock() // R++ + if a, ok := p.pool[s]; ok { // found + p.rwm.RUnlock() // R-- + return a + } + + p.rwm.RUnlock() // R-- + // not found but with a race condition, retry within a write lock + p.rwm.Lock() // W++ + defer p.rwm.Unlock() // W-- + if a, ok := p.pool[s]; ok { // done in a race + return a + } + + // we won + s = StrPack(s) + p.pool[s] = s + return s + } + + return +} + +// Count returns the number of items in the pool. +func (p *GoPool) Count() int { + return len(p.pool) +} + +// Dict is a string <-> id bijection. Dict is *not* concurrent access safe for assigning new ids +// to strings not yet contained in the bijection. +// Id for an empty string is guaranteed to be 0, +// thus Id for any non empty string is guaranteed to be non zero. +type Dict struct { + si map[string]int + is []string +} + +// NewDict returns a newly created Dict. +func NewDict() (d *Dict) { + d = &Dict{map[string]int{}, []string{}} + d.Id("") + return +} + +// Count returns the number of items in the dict. +func (d *Dict) Count() int { + return len(d.is) +} + +// Id maps string s to its numeric identificator. +func (d *Dict) Id(s string) (y int) { + if y, ok := d.si[s]; ok { + return y + } + + s = StrPack(s) + y = len(d.is) + d.si[s] = y + d.is = append(d.is, s) + return +} + +// S maps an id to its string value and ok == true. Id values not contained in the bijection +// return "", false. +func (d *Dict) S(id int) (s string, ok bool) { + if id >= len(d.is) { + return "", false + } + return d.is[id], true +} + +// GoDict is a concurrent access safe version of Dict. +type GoDict struct { + si map[string]int + is []string + rwm *sync.RWMutex +} + +// NewGoDict returns a newly created GoDict. +func NewGoDict() (d *GoDict) { + d = &GoDict{map[string]int{}, []string{}, &sync.RWMutex{}} + d.Id("") + return +} + +// Count returns the number of items in the dict. +func (d *GoDict) Count() int { + return len(d.is) +} + +// Id maps string s to its numeric identificator. The implementation honors getting +// an existing id at the cost of assigning a new one. +func (d *GoDict) Id(s string) (y int) { + d.rwm.RLock() // R++ + if y, ok := d.si[s]; ok { // found + d.rwm.RUnlock() // R-- + return y + } + + d.rwm.RUnlock() // R-- + + // not found but with a race condition + d.rwm.Lock() // W++ recheck with write lock + defer d.rwm.Unlock() // W-- + if y, ok := d.si[s]; ok { // some other goroutine won already + return y + } + + // a race free not found state => insert the string + s = StrPack(s) + y = len(d.is) + d.si[s] = y + d.is = append(d.is, s) + return +} + +// S maps an id to its string value and ok == true. Id values not contained in the bijection +// return "", false. +func (d *GoDict) S(id int) (s string, ok bool) { + d.rwm.RLock() // R++ + defer d.rwm.RUnlock() // R-- + if id >= len(d.is) { + return "", false + } + return d.is[id], true +} + +// StrPack returns a new instance of s which is tightly packed in memory. +// It is intended for avoiding the situation where having a live reference +// to a string slice over an unreferenced biger underlying string keeps the biger one +// in memory anyway - it can't be GCed. +func StrPack(s string) string { + return string([]byte(s)) // T(U(T)) intentional. +} + +// JoinFields returns strings in flds joined by sep. Flds may contain arbitrary +// bytes, including the sep as they are safely escaped. JoinFields panics if +// sep is the backslash character or if len(sep) != 1. +func JoinFields(flds []string, sep string) string { + if len(sep) != 1 || sep == "\\" { + panic("invalid separator") + } + + a := make([]string, len(flds)) + for i, v := range flds { + v = strings.Replace(v, "\\", "\\0", -1) + a[i] = strings.Replace(v, sep, "\\1", -1) + } + return strings.Join(a, sep) +} + +// SplitFields splits s, which must be produced by JoinFields using the same +// sep, into flds. SplitFields panics if sep is the backslash character or if +// len(sep) != 1. +func SplitFields(s, sep string) (flds []string) { + if len(sep) != 1 || sep == "\\" { + panic("invalid separator") + } + + a := strings.Split(s, sep) + r := make([]string, len(a)) + for i, v := range a { + v = strings.Replace(v, "\\1", sep, -1) + r[i] = strings.Replace(v, "\\0", "\\", -1) + } + return r +} + +// PrettyPrintHooks allow to customize the result of PrettyPrint for types +// listed in the map value. +type PrettyPrintHooks map[reflect.Type]func(f Formatter, v interface{}, prefix, suffix string) + +// PrettyString returns the output of PrettyPrint as a string. +func PrettyString(v interface{}, prefix, suffix string, hooks PrettyPrintHooks) string { + var b bytes.Buffer + PrettyPrint(&b, v, prefix, suffix, hooks) + return b.String() +} + +// PrettyPrint pretty prints v to w. Zero values and unexported struct fields +// are omitted. +// +// Force printing of zero values of struct fields by including in the field tag +// PrettyPrint:"zero". +// +// Enable using a String method, if any, of a struct field type by including in +// the field tag PrettyPrint:"stringer". +// +// The tags can be combined as in PrettyPrint:"zero,stringer". The order is not +// important, so PrettyPrint:stringer,zero has the same effect. +// +// A hook attached to the field type has priority over the struct field tag +// described above. +func PrettyPrint(w io.Writer, v interface{}, prefix, suffix string, hooks PrettyPrintHooks) { + if v == nil { + return + } + + f := IndentFormatter(w, "· ") + + defer func() { + if e := recover(); e != nil { + f.Format("\npanic: %v", e) + } + }() + + prettyPrint(nil, f, prefix, suffix, v, hooks, false, false) +} + +func prettyPrint(protect map[interface{}]struct{}, sf Formatter, prefix, suffix string, v interface{}, hooks PrettyPrintHooks, zero, stringer bool) { + if v == nil { + return + } + + rt := reflect.TypeOf(v) + if handler := hooks[rt]; handler != nil { + handler(sf, v, prefix, suffix) + return + } + + rv := reflect.ValueOf(v) + if stringer { + if _, ok := v.(fmt.Stringer); ok { + sf.Format("%s%s", prefix, v) + sf.Format(suffix) + return + } + } + + switch rt.Kind() { + case reflect.Slice: + if rv.Len() == 0 && !zero { + return + } + + sf.Format("%s[]%T{ // len %d%i\n", prefix, rv.Index(0).Interface(), rv.Len()) + for i := 0; i < rv.Len(); i++ { + prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Array: + if reflect.Zero(rt).Interface() == rv.Interface() && !zero { + return + } + + sf.Format("%s[%d]%T{%i\n", prefix, rv.Len(), rv.Index(0).Interface()) + for i := 0; i < rv.Len(); i++ { + prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Struct: + if rt.NumField() == 0 { + return + } + + if reflect.DeepEqual(reflect.Zero(rt).Interface(), rv.Interface()) && !zero { + return + } + + sf.Format("%s%T{%i\n", prefix, v) + for i := 0; i < rt.NumField(); i++ { + f := rv.Field(i) + if !f.CanInterface() { + continue + } + + var stringer, zero bool + ft := rt.Field(i) + if tag, ok := ft.Tag.Lookup("PrettyPrint"); ok { + a := strings.Split(tag, ",") + for _, v := range a { + switch strings.TrimSpace(v) { + case "stringer": + stringer = true + case "zero": + zero = true + } + } + } + prettyPrint(protect, sf, fmt.Sprintf("%s: ", rt.Field(i).Name), ",\n", f.Interface(), hooks, zero, stringer) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + case reflect.Ptr: + if rv.IsNil() && !zero { + return + } + + rvi := rv.Interface() + if _, ok := protect[rvi]; ok { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s&%T{ /* recursive/repetitive pointee not shown */ }"+suffix, prefix, rv.Elem().Interface()) + return + } + + if protect == nil { + protect = map[interface{}]struct{}{} + } + protect[rvi] = struct{}{} + prettyPrint(protect, sf, prefix+"&", suffix, rv.Elem().Interface(), hooks, false, false) + case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8: + if v := rv.Int(); v != 0 || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8: + if v := rv.Uint(); v != 0 || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Float32, reflect.Float64: + if v := rv.Float(); v != 0 || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Complex64, reflect.Complex128: + if v := rv.Complex(); v != 0 || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.Uintptr: + if v := rv.Uint(); v != 0 || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, v) + } + case reflect.UnsafePointer: + s := fmt.Sprintf("%p", rv.Interface()) + if s == "0x0" && !zero { + return + } + + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%s"+suffix, prefix, s) + case reflect.Bool: + if v := rv.Bool(); v || zero { + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%v"+suffix, prefix, rv.Bool()) + } + case reflect.String: + s := rv.Interface().(string) + if s == "" && !zero { + return + } + + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%q"+suffix, prefix, s) + case reflect.Chan: + if reflect.Zero(rt).Interface() == rv.Interface() && !zero { + return + } + + c := rv.Cap() + s := "" + if c != 0 { + s = fmt.Sprintf("// capacity: %d", c) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%s%s %s%s"+suffix, prefix, rt.ChanDir(), rt.Elem().Name(), s) + case reflect.Func: + if rv.IsNil() && !zero { + return + } + + var in, out []string + for i := 0; i < rt.NumIn(); i++ { + x := reflect.Zero(rt.In(i)) + in = append(in, fmt.Sprintf("%T", x.Interface())) + } + if rt.IsVariadic() { + i := len(in) - 1 + in[i] = "..." + in[i][2:] + } + for i := 0; i < rt.NumOut(); i++ { + out = append(out, rt.Out(i).Name()) + } + s := "(" + strings.Join(in, ", ") + ")" + t := strings.Join(out, ", ") + if len(out) > 1 { + t = "(" + t + ")" + } + if t != "" { + t = " " + t + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%sfunc%s%s { ... }"+suffix, prefix, s, t) + case reflect.Map: + keys := rv.MapKeys() + if len(keys) == 0 && !zero { + return + } + + var buf bytes.Buffer + nf := IndentFormatter(&buf, "· ") + var skeys []string + for i, k := range keys { + prettyPrint(protect, nf, "", "", k.Interface(), hooks, false, false) + skeys = append(skeys, fmt.Sprintf("%s%10d", buf.Bytes(), i)) + buf.Reset() + } + sort.Strings(skeys) + sf.Format("%s%T{%i\n", prefix, v) + for _, k := range skeys { + si := strings.TrimSpace(k[len(k)-10:]) + k = k[:len(k)-10] + n, _ := strconv.ParseUint(si, 10, 64) + mv := rv.MapIndex(keys[n]) + prettyPrint(protect, sf, fmt.Sprintf("%s: ", k), ",\n", mv.Interface(), hooks, false, false) + } + suffix = strings.Replace(suffix, "%", "%%", -1) + sf.Format("%u}" + suffix) + } +} + +// Gopath returns the value of the $GOPATH environment variable or its default +// value if not set. +func Gopath() string { + if r := os.Getenv("GOPATH"); r != "" { + return r + } + + // go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122 + switch runtime.GOOS { + case "plan9": + return os.Getenv("home") + case "windows": + return filepath.Join(os.Getenv("USERPROFILE"), "go") + default: + return filepath.Join(os.Getenv("HOME"), "go") + } +} + +// Homepath returns the user's home directory path. +func Homepath() string { + // go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122 + switch runtime.GOOS { + case "plan9": + return os.Getenv("home") + case "windows": + return os.Getenv("USERPROFILE") + default: + return os.Getenv("HOME") + } +} + +// ImportPath returns the import path of the caller or an error, if any. +func ImportPath() (string, error) { + _, file, _, ok := runtime.Caller(1) + if !ok { + return "", fmt.Errorf("runtime.Caller failed") + } + + gopath := Gopath() + for _, v := range filepath.SplitList(gopath) { + gp := filepath.Join(v, "src") + path, err := filepath.Rel(gp, file) + if err != nil { + continue + } + + return filepath.Dir(path), nil + } + + return "", fmt.Errorf("cannot determine import path using GOPATH=%s", gopath) +} -- cgit 1.4.1