#136: Start working on database persistence
This commit is contained in:
12
vendor/modernc.org/strutil/AUTHORS
generated
vendored
Normal file
12
vendor/modernc.org/strutil/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# This file lists authors for copyright purposes. This file is distinct from
|
||||
# the CONTRIBUTORS files. See the latter for an explanation.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
#
|
||||
# The email address is not required for organizations.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
CZ.NIC z.s.p.o. <kontakt@nic.cz>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
9
vendor/modernc.org/strutil/CONTRIBUTORS
generated
vendored
Normal file
9
vendor/modernc.org/strutil/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# This file lists people who contributed code to this repository. The AUTHORS
|
||||
# file lists the copyright holders; this file lists people.
|
||||
#
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Jan Mercl <0xjnml@gmail.com>
|
27
vendor/modernc.org/strutil/LICENSE
generated
vendored
Normal file
27
vendor/modernc.org/strutil/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014 The strutil Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the names of the authors nor the names of the
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
59
vendor/modernc.org/strutil/Makefile
generated
vendored
Normal file
59
vendor/modernc.org/strutil/Makefile
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# 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.
|
||||
|
||||
.PHONY: all clean cover cpu editor internalError later mem nuke todo edit
|
||||
|
||||
grep=--include=*.go --include=*.l --include=*.y --include=*.yy
|
||||
ngrep='TODOOK\|parser\.go\|scanner\.go\|.*_string\.go'
|
||||
|
||||
all: editor
|
||||
go vet 2>&1 | grep -v $(ngrep) || true
|
||||
golint 2>&1 | grep -v $(ngrep) || true
|
||||
make todo
|
||||
unused . || true
|
||||
misspell *.go
|
||||
gosimple || true
|
||||
maligned || true
|
||||
unconvert -apply
|
||||
|
||||
clean:
|
||||
go clean
|
||||
rm -f *~ *.test *.out
|
||||
|
||||
cover:
|
||||
t=$(shell tempfile) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t
|
||||
|
||||
cpu: clean
|
||||
go test -run @ -bench . -cpuprofile cpu.out
|
||||
go tool pprof -lines *.test cpu.out
|
||||
|
||||
edit:
|
||||
@ 1>/dev/null 2>/dev/null gvim -p Makefile *.go
|
||||
|
||||
editor:
|
||||
unconvert -apply || true
|
||||
gofmt -l -s -w *.go
|
||||
go test -i
|
||||
go test 2>&1 | tee log
|
||||
go install
|
||||
|
||||
internalError:
|
||||
egrep -ho '"internal error.*"' *.go | sort | cat -n
|
||||
|
||||
later:
|
||||
@grep -n $(grep) LATER * || true
|
||||
@grep -n $(grep) MAYBE * || true
|
||||
|
||||
mem: clean
|
||||
go test -run @ -bench . -memprofile mem.out -memprofilerate 1 -timeout 24h
|
||||
go tool pprof -lines -web -alloc_space *.test mem.out
|
||||
|
||||
nuke: clean
|
||||
go clean -i
|
||||
|
||||
todo:
|
||||
@grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * | grep -v $(ngrep) || true
|
||||
@grep -nr $(grep) TODO * | grep -v $(ngrep) || true
|
||||
@grep -nr $(grep) BUG * | grep -v $(ngrep) || true
|
||||
@grep -nr $(grep) [^[:alpha:]]println * | grep -v $(ngrep) || true
|
8
vendor/modernc.org/strutil/README
generated
vendored
Normal file
8
vendor/modernc.org/strutil/README
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
modernc.orgall-able mirror of modified code already published at:
|
||||
http://git.nic.cz/redmine/projects/gostrutil/repository
|
||||
|
||||
Online godoc documentation for this package (should be) available at:
|
||||
http://gopkgdoc.appspot.com/pkg/modernc.org/strutil
|
||||
|
||||
Installation:
|
||||
$ go get modernc.org/strutil
|
5
vendor/modernc.org/strutil/go.mod
generated
vendored
Normal file
5
vendor/modernc.org/strutil/go.mod
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
module modernc.org/strutil
|
||||
|
||||
go 1.15
|
||||
|
||||
require modernc.org/mathutil v1.2.2
|
4
vendor/modernc.org/strutil/go.sum
generated
vendored
Normal file
4
vendor/modernc.org/strutil/go.sum
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
733
vendor/modernc.org/strutil/strutil.go
generated
vendored
Normal file
733
vendor/modernc.org/strutil/strutil.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
Reference in New Issue
Block a user