vendor: add new dependencies
This commit is contained in:
parent
9b21edfbce
commit
5fe915787d
|
@ -24,3 +24,6 @@ f6b343c37ca80bfa8ea539da67a0b621f84fab1d golang.org/x/crypto/acme
|
||||||
f6b343c37ca80bfa8ea539da67a0b621f84fab1d golang.org/x/crypto/acme/autocert
|
f6b343c37ca80bfa8ea539da67a0b621f84fab1d golang.org/x/crypto/acme/autocert
|
||||||
45e771701b814666a7eb299e6c7a57d0b1799e91 golang.org/x/net/context
|
45e771701b814666a7eb299e6c7a57d0b1799e91 golang.org/x/net/context
|
||||||
45e771701b814666a7eb299e6c7a57d0b1799e91 golang.org/x/net/context/ctxhttp
|
45e771701b814666a7eb299e6c7a57d0b1799e91 golang.org/x/net/context/ctxhttp
|
||||||
|
e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb github.com/kr/pretty
|
||||||
|
bb797dc4fb8320488f47bf11de07a733d7233e1f github.com/kr/text
|
||||||
|
3b8db5e93c4c02efbc313e17b2e796b0914a01fb go.uber.org/atomic
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package pretty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sbuf []string
|
||||||
|
|
||||||
|
func (s *sbuf) Write(b []byte) (int, error) {
|
||||||
|
*s = append(*s, string(b))
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff returns a slice where each element describes
|
||||||
|
// a difference between a and b.
|
||||||
|
func Diff(a, b interface{}) (desc []string) {
|
||||||
|
Fdiff((*sbuf)(&desc), a, b)
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdiff writes to w a description of the differences between a and b.
|
||||||
|
func Fdiff(w io.Writer, a, b interface{}) {
|
||||||
|
diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
type diffWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
l string // label
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w diffWriter) printf(f string, a ...interface{}) {
|
||||||
|
var l string
|
||||||
|
if w.l != "" {
|
||||||
|
l = w.l + ": "
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w.w, l+f, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w diffWriter) diff(av, bv reflect.Value) {
|
||||||
|
if !av.IsValid() && bv.IsValid() {
|
||||||
|
w.printf("nil != %#v", bv.Interface())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if av.IsValid() && !bv.IsValid() {
|
||||||
|
w.printf("%#v != nil", av.Interface())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !av.IsValid() && !bv.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
at := av.Type()
|
||||||
|
bt := bv.Type()
|
||||||
|
if at != bt {
|
||||||
|
w.printf("%v != %v", at, bt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// numeric types, including bool
|
||||||
|
if at.Kind() < reflect.Array {
|
||||||
|
a, b := av.Interface(), bv.Interface()
|
||||||
|
if a != b {
|
||||||
|
w.printf("%#v != %#v", a, b)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch at.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
a, b := av.Interface(), bv.Interface()
|
||||||
|
if a != b {
|
||||||
|
w.printf("%q != %q", a, b)
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
switch {
|
||||||
|
case av.IsNil() && !bv.IsNil():
|
||||||
|
w.printf("nil != %v", bv.Interface())
|
||||||
|
case !av.IsNil() && bv.IsNil():
|
||||||
|
w.printf("%v != nil", av.Interface())
|
||||||
|
case !av.IsNil() && !bv.IsNil():
|
||||||
|
w.diff(av.Elem(), bv.Elem())
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < av.NumField(); i++ {
|
||||||
|
w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
lenA := av.Len()
|
||||||
|
lenB := bv.Len()
|
||||||
|
if lenA != lenB {
|
||||||
|
w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; i < lenA; i++ {
|
||||||
|
w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
|
||||||
|
for _, k := range ak {
|
||||||
|
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||||
|
w.printf("%q != (missing)", av.MapIndex(k))
|
||||||
|
}
|
||||||
|
for _, k := range both {
|
||||||
|
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||||
|
w.diff(av.MapIndex(k), bv.MapIndex(k))
|
||||||
|
}
|
||||||
|
for _, k := range bk {
|
||||||
|
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||||
|
w.printf("(missing) != %q", bv.MapIndex(k))
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
|
||||||
|
default:
|
||||||
|
if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||||
|
w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d diffWriter) relabel(name string) (d1 diffWriter) {
|
||||||
|
d1 = d
|
||||||
|
if d.l != "" && name[0] != '[' {
|
||||||
|
d1.l += "."
|
||||||
|
}
|
||||||
|
d1.l += name
|
||||||
|
return d1
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
|
||||||
|
for _, av := range a {
|
||||||
|
inBoth := false
|
||||||
|
for _, bv := range b {
|
||||||
|
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||||
|
inBoth = true
|
||||||
|
both = append(both, av)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !inBoth {
|
||||||
|
ak = append(ak, av)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bv := range b {
|
||||||
|
inBoth := false
|
||||||
|
for _, av := range a {
|
||||||
|
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||||
|
inBoth = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !inBoth {
|
||||||
|
bk = append(bk, bv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,337 @@
|
||||||
|
package pretty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/kr/text"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
type formatter struct {
|
||||||
|
x interface{}
|
||||||
|
force bool
|
||||||
|
quote bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatter makes a wrapper, f, that will format x as go source with line
|
||||||
|
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
||||||
|
// "#" and " " (space) flags are set, for example:
|
||||||
|
//
|
||||||
|
// fmt.Sprintf("%# v", Formatter(x))
|
||||||
|
//
|
||||||
|
// If one of these two flags is not set, or any other verb is used, f will
|
||||||
|
// format x according to the usual rules of package fmt.
|
||||||
|
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
||||||
|
func Formatter(x interface{}) (f fmt.Formatter) {
|
||||||
|
return formatter{x: x, quote: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fo formatter) String() string {
|
||||||
|
return fmt.Sprint(fo.x) // unwrap it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fo formatter) passThrough(f fmt.State, c rune) {
|
||||||
|
s := "%"
|
||||||
|
for i := 0; i < 128; i++ {
|
||||||
|
if f.Flag(i) {
|
||||||
|
s += string(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w, ok := f.Width(); ok {
|
||||||
|
s += fmt.Sprintf("%d", w)
|
||||||
|
}
|
||||||
|
if p, ok := f.Precision(); ok {
|
||||||
|
s += fmt.Sprintf(".%d", p)
|
||||||
|
}
|
||||||
|
s += string(c)
|
||||||
|
fmt.Fprintf(f, s, fo.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fo formatter) Format(f fmt.State, c rune) {
|
||||||
|
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
||||||
|
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
||||||
|
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
|
||||||
|
p.printValue(reflect.ValueOf(fo.x), true, fo.quote)
|
||||||
|
w.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fo.passThrough(f, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type printer struct {
|
||||||
|
io.Writer
|
||||||
|
tw *tabwriter.Writer
|
||||||
|
visited map[visit]int
|
||||||
|
depth int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) indent() *printer {
|
||||||
|
q := *p
|
||||||
|
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
||||||
|
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
||||||
|
return &q
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
||||||
|
if showType {
|
||||||
|
io.WriteString(p, v.Type().String())
|
||||||
|
fmt.Fprintf(p, "(%#v)", x)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(p, "%#v", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printValue must keep track of already-printed pointer values to avoid
|
||||||
|
// infinite recursion.
|
||||||
|
type visit struct {
|
||||||
|
v uintptr
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
||||||
|
if p.depth > 10 {
|
||||||
|
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
p.printInline(v, v.Bool(), showType)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
p.printInline(v, v.Int(), showType)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
p.printInline(v, v.Uint(), showType)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
p.printInline(v, v.Float(), showType)
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
fmt.Fprintf(p, "%#v", v.Complex())
|
||||||
|
case reflect.String:
|
||||||
|
p.fmtString(v.String(), quote)
|
||||||
|
case reflect.Map:
|
||||||
|
t := v.Type()
|
||||||
|
if showType {
|
||||||
|
io.WriteString(p, t.String())
|
||||||
|
}
|
||||||
|
writeByte(p, '{')
|
||||||
|
if nonzero(v) {
|
||||||
|
expand := !canInline(v.Type())
|
||||||
|
pp := p
|
||||||
|
if expand {
|
||||||
|
writeByte(p, '\n')
|
||||||
|
pp = p.indent()
|
||||||
|
}
|
||||||
|
keys := v.MapKeys()
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
showTypeInStruct := true
|
||||||
|
k := keys[i]
|
||||||
|
mv := v.MapIndex(k)
|
||||||
|
pp.printValue(k, false, true)
|
||||||
|
writeByte(pp, ':')
|
||||||
|
if expand {
|
||||||
|
writeByte(pp, '\t')
|
||||||
|
}
|
||||||
|
showTypeInStruct = t.Elem().Kind() == reflect.Interface
|
||||||
|
pp.printValue(mv, showTypeInStruct, true)
|
||||||
|
if expand {
|
||||||
|
io.WriteString(pp, ",\n")
|
||||||
|
} else if i < v.Len()-1 {
|
||||||
|
io.WriteString(pp, ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expand {
|
||||||
|
pp.tw.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeByte(p, '}')
|
||||||
|
case reflect.Struct:
|
||||||
|
t := v.Type()
|
||||||
|
if v.CanAddr() {
|
||||||
|
addr := v.UnsafeAddr()
|
||||||
|
vis := visit{addr, t}
|
||||||
|
if vd, ok := p.visited[vis]; ok && vd < p.depth {
|
||||||
|
p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
|
||||||
|
break // don't print v again
|
||||||
|
}
|
||||||
|
p.visited[vis] = p.depth
|
||||||
|
}
|
||||||
|
|
||||||
|
if showType {
|
||||||
|
io.WriteString(p, t.String())
|
||||||
|
}
|
||||||
|
writeByte(p, '{')
|
||||||
|
if nonzero(v) {
|
||||||
|
expand := !canInline(v.Type())
|
||||||
|
pp := p
|
||||||
|
if expand {
|
||||||
|
writeByte(p, '\n')
|
||||||
|
pp = p.indent()
|
||||||
|
}
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
showTypeInStruct := true
|
||||||
|
if f := t.Field(i); f.Name != "" {
|
||||||
|
io.WriteString(pp, f.Name)
|
||||||
|
writeByte(pp, ':')
|
||||||
|
if expand {
|
||||||
|
writeByte(pp, '\t')
|
||||||
|
}
|
||||||
|
showTypeInStruct = labelType(f.Type)
|
||||||
|
}
|
||||||
|
pp.printValue(getField(v, i), showTypeInStruct, true)
|
||||||
|
if expand {
|
||||||
|
io.WriteString(pp, ",\n")
|
||||||
|
} else if i < v.NumField()-1 {
|
||||||
|
io.WriteString(pp, ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expand {
|
||||||
|
pp.tw.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeByte(p, '}')
|
||||||
|
case reflect.Interface:
|
||||||
|
switch e := v.Elem(); {
|
||||||
|
case e.Kind() == reflect.Invalid:
|
||||||
|
io.WriteString(p, "nil")
|
||||||
|
case e.IsValid():
|
||||||
|
pp := *p
|
||||||
|
pp.depth++
|
||||||
|
pp.printValue(e, showType, true)
|
||||||
|
default:
|
||||||
|
io.WriteString(p, v.Type().String())
|
||||||
|
io.WriteString(p, "(nil)")
|
||||||
|
}
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
t := v.Type()
|
||||||
|
if showType {
|
||||||
|
io.WriteString(p, t.String())
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
||||||
|
io.WriteString(p, "(nil)")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Slice && v.IsNil() {
|
||||||
|
io.WriteString(p, "nil")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
writeByte(p, '{')
|
||||||
|
expand := !canInline(v.Type())
|
||||||
|
pp := p
|
||||||
|
if expand {
|
||||||
|
writeByte(p, '\n')
|
||||||
|
pp = p.indent()
|
||||||
|
}
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
||||||
|
pp.printValue(v.Index(i), showTypeInSlice, true)
|
||||||
|
if expand {
|
||||||
|
io.WriteString(pp, ",\n")
|
||||||
|
} else if i < v.Len()-1 {
|
||||||
|
io.WriteString(pp, ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expand {
|
||||||
|
pp.tw.Flush()
|
||||||
|
}
|
||||||
|
writeByte(p, '}')
|
||||||
|
case reflect.Ptr:
|
||||||
|
e := v.Elem()
|
||||||
|
if !e.IsValid() {
|
||||||
|
writeByte(p, '(')
|
||||||
|
io.WriteString(p, v.Type().String())
|
||||||
|
io.WriteString(p, ")(nil)")
|
||||||
|
} else {
|
||||||
|
pp := *p
|
||||||
|
pp.depth++
|
||||||
|
writeByte(pp, '&')
|
||||||
|
pp.printValue(e, true, true)
|
||||||
|
}
|
||||||
|
case reflect.Chan:
|
||||||
|
x := v.Pointer()
|
||||||
|
if showType {
|
||||||
|
writeByte(p, '(')
|
||||||
|
io.WriteString(p, v.Type().String())
|
||||||
|
fmt.Fprintf(p, ")(%#v)", x)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(p, "%#v", x)
|
||||||
|
}
|
||||||
|
case reflect.Func:
|
||||||
|
io.WriteString(p, v.Type().String())
|
||||||
|
io.WriteString(p, " {...}")
|
||||||
|
case reflect.UnsafePointer:
|
||||||
|
p.printInline(v, v.Pointer(), showType)
|
||||||
|
case reflect.Invalid:
|
||||||
|
io.WriteString(p, "nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func canInline(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
return !canExpand(t.Elem())
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
if canExpand(t.Field(i).Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Interface:
|
||||||
|
return false
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
return !canExpand(t.Elem())
|
||||||
|
case reflect.Ptr:
|
||||||
|
return false
|
||||||
|
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func canExpand(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Map, reflect.Struct,
|
||||||
|
reflect.Interface, reflect.Array, reflect.Slice,
|
||||||
|
reflect.Ptr:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func labelType(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Interface, reflect.Struct:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) fmtString(s string, quote bool) {
|
||||||
|
if quote {
|
||||||
|
s = strconv.Quote(s)
|
||||||
|
}
|
||||||
|
io.WriteString(p, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryDeepEqual(a, b interface{}) bool {
|
||||||
|
defer func() { recover() }()
|
||||||
|
return reflect.DeepEqual(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeByte(w io.Writer, b byte) {
|
||||||
|
w.Write([]byte{b})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getField(v reflect.Value, i int) reflect.Value {
|
||||||
|
val := v.Field(i)
|
||||||
|
if val.Kind() == reflect.Interface && !val.IsNil() {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Package pretty provides pretty-printing for Go values. This is
|
||||||
|
// useful during debugging, to avoid wrapping long output lines in
|
||||||
|
// the terminal.
|
||||||
|
//
|
||||||
|
// It provides a function, Formatter, that can be used with any
|
||||||
|
// function that accepts a format string. It also provides
|
||||||
|
// convenience wrappers for functions in packages fmt and log.
|
||||||
|
package pretty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Errorf is a convenience wrapper for fmt.Errorf.
|
||||||
|
//
|
||||||
|
// Calling Errorf(f, x, y) is equivalent to
|
||||||
|
// fmt.Errorf(f, Formatter(x), Formatter(y)).
|
||||||
|
func Errorf(format string, a ...interface{}) error {
|
||||||
|
return fmt.Errorf(format, wrap(a, false)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf is a convenience wrapper for fmt.Fprintf.
|
||||||
|
//
|
||||||
|
// Calling Fprintf(w, f, x, y) is equivalent to
|
||||||
|
// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
|
||||||
|
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
|
||||||
|
return fmt.Fprintf(w, format, wrap(a, false)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log is a convenience wrapper for log.Printf.
|
||||||
|
//
|
||||||
|
// Calling Log(x, y) is equivalent to
|
||||||
|
// log.Print(Formatter(x), Formatter(y)), but each operand is
|
||||||
|
// formatted with "%# v".
|
||||||
|
func Log(a ...interface{}) {
|
||||||
|
log.Print(wrap(a, true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logf is a convenience wrapper for log.Printf.
|
||||||
|
//
|
||||||
|
// Calling Logf(f, x, y) is equivalent to
|
||||||
|
// log.Printf(f, Formatter(x), Formatter(y)).
|
||||||
|
func Logf(format string, a ...interface{}) {
|
||||||
|
log.Printf(format, wrap(a, false)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logln is a convenience wrapper for log.Printf.
|
||||||
|
//
|
||||||
|
// Calling Logln(x, y) is equivalent to
|
||||||
|
// log.Println(Formatter(x), Formatter(y)), but each operand is
|
||||||
|
// formatted with "%# v".
|
||||||
|
func Logln(a ...interface{}) {
|
||||||
|
log.Println(wrap(a, true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print pretty-prints its operands and writes to standard output.
|
||||||
|
//
|
||||||
|
// Calling Print(x, y) is equivalent to
|
||||||
|
// fmt.Print(Formatter(x), Formatter(y)), but each operand is
|
||||||
|
// formatted with "%# v".
|
||||||
|
func Print(a ...interface{}) (n int, errno error) {
|
||||||
|
return fmt.Print(wrap(a, true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf is a convenience wrapper for fmt.Printf.
|
||||||
|
//
|
||||||
|
// Calling Printf(f, x, y) is equivalent to
|
||||||
|
// fmt.Printf(f, Formatter(x), Formatter(y)).
|
||||||
|
func Printf(format string, a ...interface{}) (n int, errno error) {
|
||||||
|
return fmt.Printf(format, wrap(a, false)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println pretty-prints its operands and writes to standard output.
|
||||||
|
//
|
||||||
|
// Calling Print(x, y) is equivalent to
|
||||||
|
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
|
||||||
|
// formatted with "%# v".
|
||||||
|
func Println(a ...interface{}) (n int, errno error) {
|
||||||
|
return fmt.Println(wrap(a, true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is a convenience wrapper for fmt.Sprintf.
|
||||||
|
//
|
||||||
|
// Calling Sprintf(f, x, y) is equivalent to
|
||||||
|
// fmt.Sprintf(f, Formatter(x), Formatter(y)).
|
||||||
|
func Sprintf(format string, a ...interface{}) string {
|
||||||
|
return fmt.Sprintf(format, wrap(a, false)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrap(a []interface{}, force bool) []interface{} {
|
||||||
|
w := make([]interface{}, len(a))
|
||||||
|
for i, x := range a {
|
||||||
|
w[i] = formatter{x: x, force: force}
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package pretty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func nonzero(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() != 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() != 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() != 0
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return v.Complex() != complex(0, 0)
|
||||||
|
case reflect.String:
|
||||||
|
return v.String() != ""
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
if nonzero(getField(v, i)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case reflect.Array:
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
if nonzero(v.Index(i)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
|
||||||
|
return !v.IsNil()
|
||||||
|
case reflect.UnsafePointer:
|
||||||
|
return v.Pointer() != 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Package text provides rudimentary functions for manipulating text in
|
||||||
|
// paragraphs.
|
||||||
|
package text
|
|
@ -0,0 +1,74 @@
|
||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Indent inserts prefix at the beginning of each non-empty line of s. The
|
||||||
|
// end-of-line marker is NL.
|
||||||
|
func Indent(s, prefix string) string {
|
||||||
|
return string(IndentBytes([]byte(s), []byte(prefix)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
|
||||||
|
// The end-of-line marker is NL.
|
||||||
|
func IndentBytes(b, prefix []byte) []byte {
|
||||||
|
var res []byte
|
||||||
|
bol := true
|
||||||
|
for _, c := range b {
|
||||||
|
if bol && c != '\n' {
|
||||||
|
res = append(res, prefix...)
|
||||||
|
}
|
||||||
|
res = append(res, c)
|
||||||
|
bol = c == '\n'
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer indents each line of its input.
|
||||||
|
type indentWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
bol bool
|
||||||
|
pre [][]byte
|
||||||
|
sel int
|
||||||
|
off int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIndentWriter makes a new write filter that indents the input
|
||||||
|
// lines. Each line is prefixed in order with the corresponding
|
||||||
|
// element of pre. If there are more lines than elements, the last
|
||||||
|
// element of pre is repeated for each subsequent line.
|
||||||
|
func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
|
||||||
|
return &indentWriter{
|
||||||
|
w: w,
|
||||||
|
pre: pre,
|
||||||
|
bol: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only errors returned are from the underlying indentWriter.
|
||||||
|
func (w *indentWriter) Write(p []byte) (n int, err error) {
|
||||||
|
for _, c := range p {
|
||||||
|
if w.bol {
|
||||||
|
var i int
|
||||||
|
i, err = w.w.Write(w.pre[w.sel][w.off:])
|
||||||
|
w.off += i
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = w.w.Write([]byte{c})
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
w.bol = c == '\n'
|
||||||
|
if w.bol {
|
||||||
|
w.off = 0
|
||||||
|
if w.sel < len(w.pre)-1 {
|
||||||
|
w.sel++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nl = []byte{'\n'}
|
||||||
|
sp = []byte{' '}
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultPenalty = 1e5
|
||||||
|
|
||||||
|
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
||||||
|
// raggedness.
|
||||||
|
func Wrap(s string, lim int) string {
|
||||||
|
return string(WrapBytes([]byte(s), lim))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
|
||||||
|
// raggedness.
|
||||||
|
func WrapBytes(b []byte, lim int) []byte {
|
||||||
|
words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
|
||||||
|
var lines [][]byte
|
||||||
|
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
||||||
|
lines = append(lines, bytes.Join(line, sp))
|
||||||
|
}
|
||||||
|
return bytes.Join(lines, nl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
||||||
|
// control over the details of the text wrapping process. For most uses, either
|
||||||
|
// Wrap or WrapBytes will be sufficient and more convenient.
|
||||||
|
//
|
||||||
|
// WrapWords splits a list of words into lines with minimal "raggedness",
|
||||||
|
// treating each byte as one unit, accounting for spc units between adjacent
|
||||||
|
// words on each line, and attempting to limit lines to lim units. Raggedness
|
||||||
|
// is the total error over all lines, where error is the square of the
|
||||||
|
// difference of the length of the line and lim. Too-long lines (which only
|
||||||
|
// happen when a single word is longer than lim units) have pen penalty units
|
||||||
|
// added to the error.
|
||||||
|
func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
|
||||||
|
n := len(words)
|
||||||
|
|
||||||
|
length := make([][]int, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
length[i] = make([]int, n)
|
||||||
|
length[i][i] = len(words[i])
|
||||||
|
for j := i + 1; j < n; j++ {
|
||||||
|
length[i][j] = length[i][j-1] + spc + len(words[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nbrk := make([]int, n)
|
||||||
|
cost := make([]int, n)
|
||||||
|
for i := range cost {
|
||||||
|
cost[i] = math.MaxInt32
|
||||||
|
}
|
||||||
|
for i := n - 1; i >= 0; i-- {
|
||||||
|
if length[i][n-1] <= lim || i == n-1 {
|
||||||
|
cost[i] = 0
|
||||||
|
nbrk[i] = n
|
||||||
|
} else {
|
||||||
|
for j := i + 1; j < n; j++ {
|
||||||
|
d := lim - length[i][j-1]
|
||||||
|
c := d*d + cost[j]
|
||||||
|
if length[i][j-1] > lim {
|
||||||
|
c += pen // too-long lines get a worse penalty
|
||||||
|
}
|
||||||
|
if c < cost[i] {
|
||||||
|
cost[i] = c
|
||||||
|
nbrk[i] = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines [][][]byte
|
||||||
|
i := 0
|
||||||
|
for i < n {
|
||||||
|
lines = append(lines, words[i:nbrk[i]])
|
||||||
|
i = nbrk[i]
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||||
|
// access.
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Int32 is an atomic wrapper around an int32.
|
||||||
|
type Int32 struct{ v int32 }
|
||||||
|
|
||||||
|
// NewInt32 creates an Int32.
|
||||||
|
func NewInt32(i int32) *Int32 {
|
||||||
|
return &Int32{i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int32) Load() int32 {
|
||||||
|
return atomic.LoadInt32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Add(n int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Sub(n int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, -n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Inc() int32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Dec() int32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Int32) CAS(old, new int32) bool {
|
||||||
|
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int32) Store(n int32) {
|
||||||
|
atomic.StoreInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||||
|
func (i *Int32) Swap(n int32) int32 {
|
||||||
|
return atomic.SwapInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 is an atomic wrapper around an int64.
|
||||||
|
type Int64 struct{ v int64 }
|
||||||
|
|
||||||
|
// NewInt64 creates an Int64.
|
||||||
|
func NewInt64(i int64) *Int64 {
|
||||||
|
return &Int64{i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int64) Load() int64 {
|
||||||
|
return atomic.LoadInt64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Add(n int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Sub(n int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, -n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Inc() int64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Dec() int64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Int64) CAS(old, new int64) bool {
|
||||||
|
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int64) Store(n int64) {
|
||||||
|
atomic.StoreInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||||
|
func (i *Int64) Swap(n int64) int64 {
|
||||||
|
return atomic.SwapInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 is an atomic wrapper around an uint32.
|
||||||
|
type Uint32 struct{ v uint32 }
|
||||||
|
|
||||||
|
// NewUint32 creates a Uint32.
|
||||||
|
func NewUint32(i uint32) *Uint32 {
|
||||||
|
return &Uint32{i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint32) Load() uint32 {
|
||||||
|
return atomic.LoadUint32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Add(n uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Sub(n uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Inc() uint32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||||
|
func (i *Uint32) Dec() uint32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Uint32) CAS(old, new uint32) bool {
|
||||||
|
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint32) Store(n uint32) {
|
||||||
|
atomic.StoreUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||||
|
func (i *Uint32) Swap(n uint32) uint32 {
|
||||||
|
return atomic.SwapUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 is an atomic wrapper around a uint64.
|
||||||
|
type Uint64 struct{ v uint64 }
|
||||||
|
|
||||||
|
// NewUint64 creates a Uint64.
|
||||||
|
func NewUint64(i uint64) *Uint64 {
|
||||||
|
return &Uint64{i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint64) Load() uint64 {
|
||||||
|
return atomic.LoadUint64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Add(n uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Sub(n uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Inc() uint64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Dec() uint64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Uint64) CAS(old, new uint64) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint64) Store(n uint64) {
|
||||||
|
atomic.StoreUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||||
|
func (i *Uint64) Swap(n uint64) uint64 {
|
||||||
|
return atomic.SwapUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool is an atomic Boolean.
|
||||||
|
type Bool struct{ v uint32 }
|
||||||
|
|
||||||
|
// NewBool creates a Bool.
|
||||||
|
func NewBool(initial bool) *Bool {
|
||||||
|
return &Bool{boolToInt(initial)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the Boolean.
|
||||||
|
func (b *Bool) Load() bool {
|
||||||
|
return truthy(atomic.LoadUint32(&b.v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (b *Bool) Store(new bool) {
|
||||||
|
atomic.StoreUint32(&b.v, boolToInt(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap sets the given value and returns the previous value.
|
||||||
|
func (b *Bool) Swap(new bool) bool {
|
||||||
|
return truthy(atomic.SwapUint32(&b.v, boolToInt(new)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle atomically negates the Boolean and returns the previous value.
|
||||||
|
func (b *Bool) Toggle() bool {
|
||||||
|
return truthy(atomic.AddUint32(&b.v, 1) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func truthy(n uint32) bool {
|
||||||
|
return n&1 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToInt(b bool) uint32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 is an atomic wrapper around float64.
|
||||||
|
type Float64 struct {
|
||||||
|
v uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64 creates a Float64.
|
||||||
|
func NewFloat64(f float64) *Float64 {
|
||||||
|
return &Float64{math.Float64bits(f)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (f *Float64) Load() float64 {
|
||||||
|
return math.Float64frombits(atomic.LoadUint64(&f.v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (f *Float64) Store(s float64) {
|
||||||
|
atomic.StoreUint64(&f.v, math.Float64bits(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Add(s float64) float64 {
|
||||||
|
for {
|
||||||
|
old := f.Load()
|
||||||
|
new := old + s
|
||||||
|
if f.CAS(old, new) {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Sub(s float64) float64 {
|
||||||
|
return f.Add(-s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (f *Float64) CAS(old, new float64) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new))
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
// String is an atomic type-safe wrapper around atomic.Value for strings.
|
||||||
|
type String struct{ v atomic.Value }
|
||||||
|
|
||||||
|
// NewString creates a String.
|
||||||
|
func NewString(str string) *String {
|
||||||
|
s := &String{}
|
||||||
|
if str != "" {
|
||||||
|
s.Store(str)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped string.
|
||||||
|
func (s *String) Load() string {
|
||||||
|
v := s.v.Load()
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed string.
|
||||||
|
// Note: Converting the string to an interface{} to store in the atomic.Value
|
||||||
|
// requires an allocation.
|
||||||
|
func (s *String) Store(str string) {
|
||||||
|
s.v.Store(str)
|
||||||
|
}
|
Loading…
Reference in New Issue