Merge pull request #206 from Quentin-M/godeps_implsubmod
godeps: Remove implicit git submodules
This commit is contained in:
commit
0733cd9077
1
vendor/github.com/fatih/color
generated
vendored
1
vendor/github.com/fatih/color
generated
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 1b35f289c47d5c73c398cea8e006b7bcb6234a96
|
|
3
vendor/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
3
vendor/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
language: go
|
||||||
|
go: 1.3
|
||||||
|
|
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Fatih Arslan
|
||||||
|
|
||||||
|
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.
|
151
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
151
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Color lets you use colorized outputs in terms of [ANSI Escape Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It has support for Windows too! The API can be used in several ways, pick one that suits you.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
![Color](http://i.imgur.com/c1JI0lA.png)
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/fatih/color
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Standard colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Print with default helper functions
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// A newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// These are using the default foreground colors
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mix and reuse colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with white background.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom print functions (PrintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a custom print function for convenience
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("Warning")
|
||||||
|
red("Error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert into noncolor strings (SprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("This %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
// Use helper functions
|
||||||
|
fmt.Printf("This", color.RedString("warning"), "should be not neglected.")
|
||||||
|
fmt.Printf(color.GreenString("Info:"), "an important message." )
|
||||||
|
|
||||||
|
// Windows supported too! Just don't forget to change the output to color.Output
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plug into existing code
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use handy standard colors
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will now be in yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // Don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // Use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will now be bold magenta.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable color
|
||||||
|
|
||||||
|
There might be a case where you want to disable color output (for example to
|
||||||
|
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||||
|
disable colors both globally and for single color definition. For example
|
||||||
|
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||||
|
the color output with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
* Save/Return previous values
|
||||||
|
* Evaluate fmt.Formatter interface
|
||||||
|
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Fatih Arslan](https://github.com/fatih)
|
||||||
|
* Windows support via @shiena: [ansicolor](https://github.com/shiena/ansicolor)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||||
|
|
351
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
351
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoColor defines if the output is colorized or not. By default it's set to
|
||||||
|
// false. This is a global option and affects all colors. For more control over
|
||||||
|
// each color block use the methods DisableColor() individually.
|
||||||
|
var NoColor = false
|
||||||
|
|
||||||
|
// Color defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Color struct {
|
||||||
|
params []Attribute
|
||||||
|
noColor *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute defines a single SGR Code
|
||||||
|
type Attribute int
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
// Base attributes
|
||||||
|
const (
|
||||||
|
Reset Attribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack Attribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a newly created color object.
|
||||||
|
func New(value ...Attribute) *Color {
|
||||||
|
c := &Color{params: make([]Attribute, 0)}
|
||||||
|
c.Add(value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given parameters immediately. It will change the color of
|
||||||
|
// output with the given SGR parameters until color.Unset() is called.
|
||||||
|
func Set(p ...Attribute) *Color {
|
||||||
|
c := New(p...)
|
||||||
|
c.Set()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset resets all escape attributes and clears the output. Usually should
|
||||||
|
// be called after Set().
|
||||||
|
func Unset() {
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the SGR sequence.
|
||||||
|
func (c *Color) Set() *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unset() {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||||
|
func (c *Color) Add(value ...Attribute) *Color {
|
||||||
|
c.params = append(c.params, value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) prepend(value Attribute) {
|
||||||
|
c.params = append(c.params, 0)
|
||||||
|
copy(c.params[1:], c.params[0:])
|
||||||
|
c.params[0] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output defines the standard output of the print functions. By default
|
||||||
|
// os.Stdout is used.
|
||||||
|
var Output = ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||||
|
|
||||||
|
// Print formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are added between operands when neither is a
|
||||||
|
// string. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprint(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf formats according to a format specifier and writes to standard output.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||||
|
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintf(Output, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are always added between operands and a newline is
|
||||||
|
// appended. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintln(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Print().
|
||||||
|
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) { c.Print(a...) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Printf().
|
||||||
|
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||||
|
return func(format string, a ...interface{}) { c.Printf(format, a...) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Println().
|
||||||
|
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) { c.Println(a...) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjuction with color.Output, example:
|
||||||
|
//
|
||||||
|
// put := New(FgYellow).SprintFunc()
|
||||||
|
// fmt.Ffprintf(color.Output, "This is a %s", put("warning"))
|
||||||
|
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintfFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjuction with color.Output.
|
||||||
|
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||||
|
return func(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjuction with color.Output.
|
||||||
|
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintln(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
|
||||||
|
// an example output might be: "1;36" -> bold cyan
|
||||||
|
func (c *Color) sequence() string {
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(format, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||||
|
// be printed.
|
||||||
|
func (c *Color) wrap(s string) string {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.format() + s + c.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) format() string {
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unformat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableColor disables the color output. Useful to not change any existing
|
||||||
|
// code and still being able to output. Can be used for flags like
|
||||||
|
// "--no-color". To enable back use EnableColor() method.
|
||||||
|
func (c *Color) DisableColor() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColor enables the color output. Use it in conjuction with
|
||||||
|
// DisableColor(). Otherwise this method has no side effects.
|
||||||
|
func (c *Color) EnableColor() {
|
||||||
|
c.noColor = boolPtr(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) isNoColorSet() bool {
|
||||||
|
// check first if we have user setted action
|
||||||
|
if c.noColor != nil {
|
||||||
|
return *c.noColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not return the global option, which is disabled by default
|
||||||
|
return NoColor
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolPtr(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Black is an convenient helper function to print with black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Black(format string, a ...interface{}) { printColor(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// Red is an convenient helper function to print with red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Red(format string, a ...interface{}) { printColor(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// Green is an convenient helper function to print with green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Green(format string, a ...interface{}) { printColor(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// Yellow is an convenient helper function to print with yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Yellow(format string, a ...interface{}) { printColor(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// Blue is an convenient helper function to print with blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Blue(format string, a ...interface{}) { printColor(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// Magenta is an convenient helper function to print with magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Magenta(format string, a ...interface{}) { printColor(format, FgMagenta, a...) }
|
||||||
|
|
||||||
|
// Cyan is an convenient helper function to print with cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Cyan(format string, a ...interface{}) { printColor(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// White is an convenient helper function to print with white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func White(format string, a ...interface{}) { printColor(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
func printColor(format string, p Attribute, a ...interface{}) {
|
||||||
|
if !strings.HasSuffix(format, "\n") {
|
||||||
|
format += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Color{params: []Attribute{p}}
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlackString is an convenient helper function to return a string with black
|
||||||
|
// foreground.
|
||||||
|
func BlackString(format string, a ...interface{}) string {
|
||||||
|
return New(FgBlack).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedString is an convenient helper function to return a string with red
|
||||||
|
// foreground.
|
||||||
|
func RedString(format string, a ...interface{}) string {
|
||||||
|
return New(FgRed).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreenString is an convenient helper function to return a string with green
|
||||||
|
// foreground.
|
||||||
|
func GreenString(format string, a ...interface{}) string {
|
||||||
|
return New(FgGreen).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// YellowString is an convenient helper function to return a string with yellow
|
||||||
|
// foreground.
|
||||||
|
func YellowString(format string, a ...interface{}) string {
|
||||||
|
return New(FgYellow).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlueString is an convenient helper function to return a string with blue
|
||||||
|
// foreground.
|
||||||
|
func BlueString(format string, a ...interface{}) string {
|
||||||
|
return New(FgBlue).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MagentaString is an convenient helper function to return a string with magenta
|
||||||
|
// foreground.
|
||||||
|
func MagentaString(format string, a ...interface{}) string {
|
||||||
|
return New(FgMagenta).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyanString is an convenient helper function to return a string with cyan
|
||||||
|
// foreground.
|
||||||
|
func CyanString(format string, a ...interface{}) string {
|
||||||
|
return New(FgCyan).SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhiteString is an convenient helper function to return a string with white
|
||||||
|
// foreground.
|
||||||
|
func WhiteString(format string, a ...interface{}) string {
|
||||||
|
return New(FgWhite).SprintfFunc()(format, a...)
|
||||||
|
}
|
176
vendor/github.com/fatih/color/color_test.go
generated
vendored
Normal file
176
vendor/github.com/fatih/color/color_test.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Testing colors is kinda different. First we test for given colors and their
|
||||||
|
// escaped formatted results. Next we create some visual tests to be tested.
|
||||||
|
// Each visual test includes the color name to be compared.
|
||||||
|
func TestColor(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
testColors := []struct {
|
||||||
|
text string
|
||||||
|
code Attribute
|
||||||
|
}{
|
||||||
|
{text: "black", code: FgBlack},
|
||||||
|
{text: "red", code: FgRed},
|
||||||
|
{text: "green", code: FgGreen},
|
||||||
|
{text: "yellow", code: FgYellow},
|
||||||
|
{text: "blue", code: FgBlue},
|
||||||
|
{text: "magent", code: FgMagenta},
|
||||||
|
{text: "cyan", code: FgCyan},
|
||||||
|
{text: "white", code: FgWhite},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
New(c.code).Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
scannedLine := fmt.Sprintf("%q", line)
|
||||||
|
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||||
|
escapedForm := fmt.Sprintf("%q", colored)
|
||||||
|
|
||||||
|
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||||
|
|
||||||
|
if scannedLine != escapedForm {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoColor(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
testColors := []struct {
|
||||||
|
text string
|
||||||
|
code Attribute
|
||||||
|
}{
|
||||||
|
{text: "black", code: FgBlack},
|
||||||
|
{text: "red", code: FgRed},
|
||||||
|
{text: "green", code: FgGreen},
|
||||||
|
{text: "yellow", code: FgYellow},
|
||||||
|
{text: "blue", code: FgBlue},
|
||||||
|
{text: "magent", code: FgMagenta},
|
||||||
|
{text: "cyan", code: FgCyan},
|
||||||
|
{text: "white", code: FgWhite},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
p := New(c.code)
|
||||||
|
p.DisableColor()
|
||||||
|
p.Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
if line != c.text {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// global check
|
||||||
|
NoColor = true
|
||||||
|
defer func() {
|
||||||
|
NoColor = false
|
||||||
|
}()
|
||||||
|
for _, c := range testColors {
|
||||||
|
p := New(c.code)
|
||||||
|
p.Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
if line != c.text {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColorVisual(t *testing.T) {
|
||||||
|
// First Visual Test
|
||||||
|
fmt.Println("")
|
||||||
|
Output = ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||||
|
|
||||||
|
New(FgRed).Printf("red\t")
|
||||||
|
New(BgRed).Print(" ")
|
||||||
|
New(FgRed, Bold).Println(" red")
|
||||||
|
|
||||||
|
New(FgGreen).Printf("green\t")
|
||||||
|
New(BgGreen).Print(" ")
|
||||||
|
New(FgGreen, Bold).Println(" green")
|
||||||
|
|
||||||
|
New(FgYellow).Printf("yellow\t")
|
||||||
|
New(BgYellow).Print(" ")
|
||||||
|
New(FgYellow, Bold).Println(" yellow")
|
||||||
|
|
||||||
|
New(FgBlue).Printf("blue\t")
|
||||||
|
New(BgBlue).Print(" ")
|
||||||
|
New(FgBlue, Bold).Println(" blue")
|
||||||
|
|
||||||
|
New(FgMagenta).Printf("magenta\t")
|
||||||
|
New(BgMagenta).Print(" ")
|
||||||
|
New(FgMagenta, Bold).Println(" magenta")
|
||||||
|
|
||||||
|
New(FgCyan).Printf("cyan\t")
|
||||||
|
New(BgCyan).Print(" ")
|
||||||
|
New(FgCyan, Bold).Println(" cyan")
|
||||||
|
|
||||||
|
New(FgWhite).Printf("white\t")
|
||||||
|
New(BgWhite).Print(" ")
|
||||||
|
New(FgWhite, Bold).Println(" white")
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
// Second Visual test
|
||||||
|
Black("black")
|
||||||
|
Red("red")
|
||||||
|
Green("green")
|
||||||
|
Yellow("yellow")
|
||||||
|
Blue("blue")
|
||||||
|
Magenta("magenta")
|
||||||
|
Cyan("cyan")
|
||||||
|
White("white")
|
||||||
|
|
||||||
|
// Third visual test
|
||||||
|
fmt.Println()
|
||||||
|
Set(FgBlue)
|
||||||
|
fmt.Println("is this blue?")
|
||||||
|
Unset()
|
||||||
|
|
||||||
|
Set(FgMagenta)
|
||||||
|
fmt.Println("and this magenta?")
|
||||||
|
Unset()
|
||||||
|
|
||||||
|
// Fourth Visual test
|
||||||
|
fmt.Println()
|
||||||
|
blue := New(FgBlue).PrintlnFunc()
|
||||||
|
blue("blue text with custom print func")
|
||||||
|
|
||||||
|
red := New(FgRed).PrintfFunc()
|
||||||
|
red("red text with a printf func: %d\n", 123)
|
||||||
|
|
||||||
|
put := New(FgYellow).SprintFunc()
|
||||||
|
warn := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
// Fifth Visual Test
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Fprintln(Output, BlackString("black"))
|
||||||
|
fmt.Fprintln(Output, RedString("red"))
|
||||||
|
fmt.Fprintln(Output, GreenString("green"))
|
||||||
|
fmt.Fprintln(Output, YellowString("yellow"))
|
||||||
|
fmt.Fprintln(Output, BlueString("blue"))
|
||||||
|
fmt.Fprintln(Output, MagentaString("magenta"))
|
||||||
|
fmt.Fprintln(Output, CyanString("cyan"))
|
||||||
|
fmt.Fprintln(Output, WhiteString("white"))
|
||||||
|
}
|
114
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
114
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
Package color is an ANSI color package to output colorized or SGR defined
|
||||||
|
output to the standard output. The API can be used in several way, pick one
|
||||||
|
that suits you.
|
||||||
|
|
||||||
|
Use simple and default helper functions with predefined foreground colors:
|
||||||
|
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// a newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// More default foreground colors..
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Yellow("Yellow color too!")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
However there are times where custom color mixes are required. Below are some
|
||||||
|
examples to create custom color objects and use the print functions of each
|
||||||
|
separate color object.
|
||||||
|
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with White background.")
|
||||||
|
|
||||||
|
|
||||||
|
You can create PrintXxx functions to simplify even more:
|
||||||
|
|
||||||
|
// Create a custom print function for convenient
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("warning")
|
||||||
|
red("error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("don't forget this...")
|
||||||
|
|
||||||
|
|
||||||
|
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
|
||||||
|
yellow := New(FgYellow).SprintFunc()
|
||||||
|
red := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Windows support is enabled by default. All Print functions works as intended.
|
||||||
|
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||||
|
set the output to color.Output:
|
||||||
|
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Using with existing code is possible. Just use the Set() method to set the
|
||||||
|
standard output to the given parameters. That way a rewrite of an existing
|
||||||
|
code is not required.
|
||||||
|
|
||||||
|
// Use handy standard colors.
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will be now in Yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will be now bold magenta.")
|
||||||
|
|
||||||
|
There might be a case where you want to disable color output (for example to
|
||||||
|
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||||
|
disable colors both globally and for single color definition. For example
|
||||||
|
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||||
|
the color output with:
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
*/
|
||||||
|
package color
|
1
vendor/github.com/kr/text
generated
vendored
1
vendor/github.com/kr/text
generated
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 7cafcd837844e784b526369c9bce262804aebc60
|
|
19
vendor/github.com/kr/text/License
generated
vendored
Normal file
19
vendor/github.com/kr/text/License
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright 2012 Keith Rarick
|
||||||
|
|
||||||
|
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.
|
3
vendor/github.com/kr/text/Readme
generated
vendored
Normal file
3
vendor/github.com/kr/text/Readme
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This is a Go package for manipulating paragraphs of text.
|
||||||
|
|
||||||
|
See http://go.pkgdoc.org/github.com/kr/text for full documentation.
|
73
vendor/github.com/kr/text/cmd/agg/doc.go
generated
vendored
Normal file
73
vendor/github.com/kr/text/cmd/agg/doc.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Agg computes aggregate values over tabular text.
|
||||||
|
It behaves somewhat like the SQL “GROUP BY” clause.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
agg [function...]
|
||||||
|
|
||||||
|
It reads input from stdin as a sequence of records, one per line.
|
||||||
|
It treats each line as a set of fields separated by white space.
|
||||||
|
One field (the first, by default) is designated as the key.
|
||||||
|
Successive lines with equal keys are grouped into a group,
|
||||||
|
and agg produces one line of output for each group.
|
||||||
|
(Note that only contiguous input lines can form a group.
|
||||||
|
If you need to make sure that all records for a given key
|
||||||
|
are grouped together, sort the input first.)
|
||||||
|
|
||||||
|
For each remaining field,
|
||||||
|
agg applies a function to all the values in the group,
|
||||||
|
producing a single output value.
|
||||||
|
The command line arguments specify which functions to use,
|
||||||
|
one per field in the input table.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
|
||||||
|
The available functions are:
|
||||||
|
|
||||||
|
key group by this field (default for field 1)
|
||||||
|
first value from first line of group (default for rest)
|
||||||
|
last value from last line of group
|
||||||
|
sample value from any line of group, uniformly at random
|
||||||
|
prefix longest common string prefix
|
||||||
|
join:sep concatenate strings with given sep
|
||||||
|
smin lexically least string
|
||||||
|
smax lexically greatest string
|
||||||
|
min numerically least value
|
||||||
|
max numerically greatest value
|
||||||
|
sum numeric sum
|
||||||
|
mean arithmetic mean
|
||||||
|
count number of records (ignores input value)
|
||||||
|
const:val print val, ignoring input
|
||||||
|
drop omit the column entirely
|
||||||
|
|
||||||
|
The numeric functions skip items that don't parse as numbers.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
|
||||||
|
Using the following input:
|
||||||
|
|
||||||
|
$ cat >input
|
||||||
|
-rwx alice 100 /home/alice/bin/crdt
|
||||||
|
-rw- alice 210002 /home/alice/thesis.tex
|
||||||
|
-rw- bob 10051 /home/bob/expenses.tab
|
||||||
|
-rwx kr 862060 /home/kr/bin/blog
|
||||||
|
-rwx kr 304608 /home/kr/bin/agg
|
||||||
|
|
||||||
|
Disk usage for each user, plus where that disk usage occurs
|
||||||
|
(longest common prefix of filesystem paths):
|
||||||
|
|
||||||
|
$ agg <input drop key sum prefix
|
||||||
|
alice 210153 /home/alice/
|
||||||
|
bob 10051 /home/bob/expenses.tab
|
||||||
|
kr 1166668 /home/kr/
|
||||||
|
|
||||||
|
Disk usage for executable vs non-executable files:
|
||||||
|
|
||||||
|
$ sort input | agg key drop sum join:,
|
||||||
|
-rw- 220053 /home/alice/thesis.tex,/home/bob/expenses.tab
|
||||||
|
-rwx 1166768 /home/alice/bin/crdt,/home/kr/bin/agg,/home/kr/bin/blog
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
112
vendor/github.com/kr/text/cmd/agg/main.go
generated
vendored
Normal file
112
vendor/github.com/kr/text/cmd/agg/main.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// TODO(kr): tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type agg interface {
|
||||||
|
merge(string)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
key = 0
|
||||||
|
funcmap = make(map[int]func(init, arg string) agg)
|
||||||
|
argmap = make(map[int]string)
|
||||||
|
symtab = map[string]func(init, arg string) agg{
|
||||||
|
"first": first,
|
||||||
|
"last": last,
|
||||||
|
"prefix": prefix,
|
||||||
|
"sample": sample,
|
||||||
|
"join": join,
|
||||||
|
"smin": smin,
|
||||||
|
"smax": smax,
|
||||||
|
"min": min,
|
||||||
|
"max": max,
|
||||||
|
"sum": sum,
|
||||||
|
"mean": mean,
|
||||||
|
"count": count,
|
||||||
|
"const": constf,
|
||||||
|
"drop": nil,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetPrefix("agg: ")
|
||||||
|
log.SetFlags(0)
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
for i, sym := range os.Args[1:] {
|
||||||
|
if p := strings.IndexByte(sym, ':'); p >= 0 {
|
||||||
|
sym, argmap[i] = sym[:p], sym[p+1:]
|
||||||
|
}
|
||||||
|
if sym == "key" {
|
||||||
|
key, sym = i, "first"
|
||||||
|
}
|
||||||
|
f, ok := symtab[sym]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("bad function: %q", sym)
|
||||||
|
}
|
||||||
|
funcmap[i] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := bufio.NewScanner(os.Stdin)
|
||||||
|
var g *group
|
||||||
|
for sc.Scan() {
|
||||||
|
ss := strings.Fields(sc.Text())
|
||||||
|
if !matches(g, ss) {
|
||||||
|
emit(g)
|
||||||
|
g = &group{key: ss[key]}
|
||||||
|
}
|
||||||
|
mergeLine(g, ss)
|
||||||
|
}
|
||||||
|
emit(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
type group struct {
|
||||||
|
key string
|
||||||
|
agg []agg
|
||||||
|
}
|
||||||
|
|
||||||
|
func matches(g *group, ss []string) bool {
|
||||||
|
return g != nil && g.key == ss[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func emit(g *group) {
|
||||||
|
if g == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rest := false
|
||||||
|
for i, a := range g.agg {
|
||||||
|
if f, ok := funcmap[i]; ok && f == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rest {
|
||||||
|
fmt.Print("\t")
|
||||||
|
}
|
||||||
|
rest = true
|
||||||
|
fmt.Print(a)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeLine(g *group, ss []string) {
|
||||||
|
for i, s := range ss {
|
||||||
|
if i >= len(g.agg) {
|
||||||
|
f := funcmap[i]
|
||||||
|
if f == nil {
|
||||||
|
f = first
|
||||||
|
}
|
||||||
|
g.agg = append(g.agg, f(s, argmap[i]))
|
||||||
|
} else {
|
||||||
|
g.agg[i].merge(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
vendor/github.com/kr/text/cmd/agg/num.go
generated
vendored
Normal file
99
vendor/github.com/kr/text/cmd/agg/num.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func min(s, arg string) agg { return newBinop(s, opmin) }
|
||||||
|
func max(s, arg string) agg { return newBinop(s, opmax) }
|
||||||
|
func sum(s, arg string) agg { return newBinop(s, opsum) }
|
||||||
|
|
||||||
|
type binop struct {
|
||||||
|
v *big.Float
|
||||||
|
f func(a, b *big.Float) *big.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBinop(s string, f func(a, b *big.Float) *big.Float) *binop {
|
||||||
|
v, _ := parseFloat(s)
|
||||||
|
return &binop{v, f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *binop) String() string {
|
||||||
|
if o.v == nil {
|
||||||
|
return "NaN"
|
||||||
|
}
|
||||||
|
return o.v.Text('f', -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *binop) merge(s string) {
|
||||||
|
v, ok := parseFloat(s)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
o.v = o.f(o.v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func opmin(a, b *big.Float) *big.Float {
|
||||||
|
if a != nil && (b == nil || a.Cmp(b) <= 0) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func opmax(a, b *big.Float) *big.Float {
|
||||||
|
if a != nil && (b == nil || a.Cmp(b) >= 0) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsum(a, b *big.Float) *big.Float {
|
||||||
|
if a == nil {
|
||||||
|
return b
|
||||||
|
} else if b == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return a.Add(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type meanagg struct {
|
||||||
|
v *big.Float
|
||||||
|
d float64 // actually an integer
|
||||||
|
}
|
||||||
|
|
||||||
|
func mean(s, arg string) agg {
|
||||||
|
v, ok := parseFloat(s)
|
||||||
|
if !ok {
|
||||||
|
return &meanagg{new(big.Float), 0}
|
||||||
|
}
|
||||||
|
return &meanagg{v, 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *meanagg) String() string {
|
||||||
|
if m.d == 0 {
|
||||||
|
return "NaN"
|
||||||
|
}
|
||||||
|
v := new(big.Float).Quo(m.v, big.NewFloat(m.d))
|
||||||
|
return v.Text('f', -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *meanagg) merge(s string) {
|
||||||
|
v, ok := parseFloat(s)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.v.Add(m.v, v)
|
||||||
|
m.d++
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFloat(s string) (*big.Float, bool) {
|
||||||
|
v, _, err := big.ParseFloat(s, 0, 1000, big.ToNearestEven)
|
||||||
|
return v, err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type counter int
|
||||||
|
|
||||||
|
func count(init, arg string) agg { return new(counter) }
|
||||||
|
func (c *counter) String() string { return strconv.Itoa(int(*c) + 1) }
|
||||||
|
func (c *counter) merge(string) { *c++ }
|
74
vendor/github.com/kr/text/cmd/agg/string.go
generated
vendored
Normal file
74
vendor/github.com/kr/text/cmd/agg/string.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func first(s, arg string) agg { return &sbinop{s, opfirst} }
|
||||||
|
func last(s, arg string) agg { return &sbinop{s, oplast} }
|
||||||
|
func prefix(s, arg string) agg { return &sbinop{s, opprefix} }
|
||||||
|
func join(s, arg string) agg { return &sbinop{s, opjoin(arg)} }
|
||||||
|
func smin(s, arg string) agg { return &sbinop{s, opsmin} }
|
||||||
|
func smax(s, arg string) agg { return &sbinop{s, opsmax} }
|
||||||
|
|
||||||
|
type sbinop struct {
|
||||||
|
s string
|
||||||
|
f func(a, b string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *sbinop) String() string { return o.s }
|
||||||
|
|
||||||
|
func (o *sbinop) merge(s string) { o.s = o.f(o.s, s) }
|
||||||
|
|
||||||
|
func opfirst(a, b string) string { return a }
|
||||||
|
func oplast(a, b string) string { return b }
|
||||||
|
|
||||||
|
func opprefix(a, b string) string {
|
||||||
|
for i := range a {
|
||||||
|
if i >= len(b) || a[i] != b[i] {
|
||||||
|
return a[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func opjoin(sep string) func(a, b string) string {
|
||||||
|
return func(a, b string) string {
|
||||||
|
return a + sep + b // TODO(kr): too slow? maybe strings.Join?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsmin(a, b string) string {
|
||||||
|
if strings.Compare(a, b) <= 0 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsmax(a, b string) string {
|
||||||
|
if strings.Compare(a, b) >= 0 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type sampler struct {
|
||||||
|
n int
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func sample(s, arg string) agg { return &sampler{1, s} }
|
||||||
|
func (p *sampler) String() string { return p.s }
|
||||||
|
func (p *sampler) merge(s string) {
|
||||||
|
p.n++
|
||||||
|
if rand.Intn(p.n) == 0 {
|
||||||
|
p.s = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type constant string
|
||||||
|
|
||||||
|
func constf(init, arg string) agg { return constant(arg) }
|
||||||
|
func (c constant) String() string { return string(c) }
|
||||||
|
func (c constant) merge(string) {}
|
5
vendor/github.com/kr/text/colwriter/Readme
generated
vendored
Normal file
5
vendor/github.com/kr/text/colwriter/Readme
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Package colwriter provides a write filter that formats
|
||||||
|
input lines in multiple columns.
|
||||||
|
|
||||||
|
The package is a straightforward translation from
|
||||||
|
/src/cmd/draw/mc.c in Plan 9 from User Space.
|
147
vendor/github.com/kr/text/colwriter/column.go
generated
vendored
Normal file
147
vendor/github.com/kr/text/colwriter/column.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// Package colwriter provides a write filter that formats
|
||||||
|
// input lines in multiple columns.
|
||||||
|
//
|
||||||
|
// The package is a straightforward translation from
|
||||||
|
// /src/cmd/draw/mc.c in Plan 9 from User Space.
|
||||||
|
package colwriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tab = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Print each input line ending in a colon ':' separately.
|
||||||
|
BreakOnColon uint = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Writer is a filter that arranges input lines in as many columns as will
|
||||||
|
// fit in its width. Tab '\t' chars in the input are translated to sequences
|
||||||
|
// of spaces ending at multiples of 4 positions.
|
||||||
|
//
|
||||||
|
// If BreakOnColon is set, each input line ending in a colon ':' is written
|
||||||
|
// separately.
|
||||||
|
//
|
||||||
|
// The Writer assumes that all Unicode code points have the same width; this
|
||||||
|
// may not be true in some fonts.
|
||||||
|
type Writer struct {
|
||||||
|
w io.Writer
|
||||||
|
buf []byte
|
||||||
|
width int
|
||||||
|
flag uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter allocates and initializes a new Writer writing to w.
|
||||||
|
// Parameter width controls the total number of characters on each line
|
||||||
|
// across all columns.
|
||||||
|
func NewWriter(w io.Writer, width int, flag uint) *Writer {
|
||||||
|
return &Writer{
|
||||||
|
w: w,
|
||||||
|
width: width,
|
||||||
|
flag: flag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes p to the writer w. The only errors returned are ones
|
||||||
|
// encountered while writing to the underlying output stream.
|
||||||
|
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||||
|
var linelen int
|
||||||
|
var lastWasColon bool
|
||||||
|
for i, c := range p {
|
||||||
|
w.buf = append(w.buf, c)
|
||||||
|
linelen++
|
||||||
|
if c == '\t' {
|
||||||
|
w.buf[len(w.buf)-1] = ' '
|
||||||
|
for linelen%tab != 0 {
|
||||||
|
w.buf = append(w.buf, ' ')
|
||||||
|
linelen++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.flag&BreakOnColon != 0 && c == ':' {
|
||||||
|
lastWasColon = true
|
||||||
|
} else if lastWasColon {
|
||||||
|
if c == '\n' {
|
||||||
|
pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
|
||||||
|
if pos < 0 {
|
||||||
|
pos = 0
|
||||||
|
}
|
||||||
|
line := w.buf[pos:]
|
||||||
|
w.buf = w.buf[:pos]
|
||||||
|
if err = w.columnate(); err != nil {
|
||||||
|
if len(line) < i {
|
||||||
|
return i - len(line), err
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if n, err := w.w.Write(line); err != nil {
|
||||||
|
if r := len(line) - n; r < i {
|
||||||
|
return i - r, err
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastWasColon = false
|
||||||
|
}
|
||||||
|
if c == '\n' {
|
||||||
|
linelen = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush should be called after the last call to Write to ensure that any data
|
||||||
|
// buffered in the Writer is written to output.
|
||||||
|
func (w *Writer) Flush() error {
|
||||||
|
return w.columnate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) columnate() error {
|
||||||
|
words := bytes.Split(w.buf, []byte{'\n'})
|
||||||
|
w.buf = nil
|
||||||
|
if len(words[len(words)-1]) == 0 {
|
||||||
|
words = words[:len(words)-1]
|
||||||
|
}
|
||||||
|
maxwidth := 0
|
||||||
|
for _, wd := range words {
|
||||||
|
if n := utf8.RuneCount(wd); n > maxwidth {
|
||||||
|
maxwidth = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxwidth++ // space char
|
||||||
|
wordsPerLine := w.width / maxwidth
|
||||||
|
if wordsPerLine <= 0 {
|
||||||
|
wordsPerLine = 1
|
||||||
|
}
|
||||||
|
nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
|
||||||
|
for i := 0; i < nlines; i++ {
|
||||||
|
col := 0
|
||||||
|
endcol := 0
|
||||||
|
for j := i; j < len(words); j += nlines {
|
||||||
|
endcol += maxwidth
|
||||||
|
_, err := w.w.Write(words[j])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
col += utf8.RuneCount(words[j])
|
||||||
|
if j+nlines < len(words) {
|
||||||
|
for col < endcol {
|
||||||
|
_, err := w.w.Write([]byte{' '})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
col++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := w.w.Write([]byte{'\n'})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
90
vendor/github.com/kr/text/colwriter/column_test.go
generated
vendored
Normal file
90
vendor/github.com/kr/text/colwriter/column_test.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package colwriter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var src = `
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.godir
|
||||||
|
Procfile:
|
||||||
|
README.md
|
||||||
|
api.go
|
||||||
|
apps.go
|
||||||
|
auth.go
|
||||||
|
darwin.go
|
||||||
|
data.go
|
||||||
|
dyno.go:
|
||||||
|
env.go
|
||||||
|
git.go
|
||||||
|
help.go
|
||||||
|
hkdist
|
||||||
|
linux.go
|
||||||
|
ls.go
|
||||||
|
main.go
|
||||||
|
plugin.go
|
||||||
|
run.go
|
||||||
|
scale.go
|
||||||
|
ssh.go
|
||||||
|
tail.go
|
||||||
|
term
|
||||||
|
unix.go
|
||||||
|
update.go
|
||||||
|
version.go
|
||||||
|
windows.go
|
||||||
|
`[1:]
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
wid int
|
||||||
|
flag uint
|
||||||
|
src string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{80, 0, "", ""},
|
||||||
|
{80, 0, src, `
|
||||||
|
.git README.md darwin.go git.go ls.go scale.go unix.go
|
||||||
|
.gitignore api.go data.go help.go main.go ssh.go update.go
|
||||||
|
.godir apps.go dyno.go: hkdist plugin.go tail.go version.go
|
||||||
|
Procfile: auth.go env.go linux.go run.go term windows.go
|
||||||
|
`[1:]},
|
||||||
|
{80, BreakOnColon, src, `
|
||||||
|
.git .gitignore .godir
|
||||||
|
|
||||||
|
Procfile:
|
||||||
|
README.md api.go apps.go auth.go darwin.go data.go
|
||||||
|
|
||||||
|
dyno.go:
|
||||||
|
env.go hkdist main.go scale.go term version.go
|
||||||
|
git.go linux.go plugin.go ssh.go unix.go windows.go
|
||||||
|
help.go ls.go run.go tail.go update.go
|
||||||
|
`[1:]},
|
||||||
|
{20, 0, `
|
||||||
|
Hello
|
||||||
|
Γειά σου
|
||||||
|
안녕
|
||||||
|
今日は
|
||||||
|
`[1:], `
|
||||||
|
Hello 안녕
|
||||||
|
Γειά σου 今日は
|
||||||
|
`[1:]},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriter(t *testing.T) {
|
||||||
|
for _, test := range tests {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
w := NewWriter(b, test.wid, test.flag)
|
||||||
|
if _, err := w.Write([]byte(test.src)); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if g := b.String(); test.want != g {
|
||||||
|
t.Log("\n" + test.want)
|
||||||
|
t.Log("\n" + g)
|
||||||
|
t.Errorf("%q != %q", test.want, g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
vendor/github.com/kr/text/doc.go
generated
vendored
Normal file
3
vendor/github.com/kr/text/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package text provides rudimentary functions for manipulating text in
|
||||||
|
// paragraphs.
|
||||||
|
package text
|
74
vendor/github.com/kr/text/indent.go
generated
vendored
Normal file
74
vendor/github.com/kr/text/indent.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
119
vendor/github.com/kr/text/indent_test.go
generated
vendored
Normal file
119
vendor/github.com/kr/text/indent_test.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
inp, exp, pre string
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []T{
|
||||||
|
{
|
||||||
|
"The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n",
|
||||||
|
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n",
|
||||||
|
"xxx",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.",
|
||||||
|
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.",
|
||||||
|
"xxx",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndent(t *testing.T) {
|
||||||
|
for _, test := range tests {
|
||||||
|
got := Indent(test.inp, test.pre)
|
||||||
|
if got != test.exp {
|
||||||
|
t.Errorf("mismatch %q != %q", got, test.exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndentWriterTest struct {
|
||||||
|
inp, exp string
|
||||||
|
pre []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts = []IndentWriterTest{
|
||||||
|
{
|
||||||
|
`
|
||||||
|
The quick brown fox
|
||||||
|
jumps over the lazy
|
||||||
|
dog.
|
||||||
|
But not quickly.
|
||||||
|
`[1:],
|
||||||
|
`
|
||||||
|
xxxThe quick brown fox
|
||||||
|
xxxjumps over the lazy
|
||||||
|
xxxdog.
|
||||||
|
xxxBut not quickly.
|
||||||
|
`[1:],
|
||||||
|
[]string{"xxx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`
|
||||||
|
The quick brown fox
|
||||||
|
jumps over the lazy
|
||||||
|
dog.
|
||||||
|
But not quickly.
|
||||||
|
`[1:],
|
||||||
|
`
|
||||||
|
xxaThe quick brown fox
|
||||||
|
xxxjumps over the lazy
|
||||||
|
xxxdog.
|
||||||
|
xxxBut not quickly.
|
||||||
|
`[1:],
|
||||||
|
[]string{"xxa", "xxx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`
|
||||||
|
The quick brown fox
|
||||||
|
jumps over the lazy
|
||||||
|
dog.
|
||||||
|
But not quickly.
|
||||||
|
`[1:],
|
||||||
|
`
|
||||||
|
xxaThe quick brown fox
|
||||||
|
xxbjumps over the lazy
|
||||||
|
xxcdog.
|
||||||
|
xxxBut not quickly.
|
||||||
|
`[1:],
|
||||||
|
[]string{"xxa", "xxb", "xxc", "xxx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`
|
||||||
|
The quick brown fox
|
||||||
|
jumps over the lazy
|
||||||
|
dog.
|
||||||
|
|
||||||
|
But not quickly.`[1:],
|
||||||
|
`
|
||||||
|
xxaThe quick brown fox
|
||||||
|
xxxjumps over the lazy
|
||||||
|
xxxdog.
|
||||||
|
xxx
|
||||||
|
xxxBut not quickly.`[1:],
|
||||||
|
[]string{"xxa", "xxx"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndentWriter(t *testing.T) {
|
||||||
|
for _, test := range ts {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
pre := make([][]byte, len(test.pre))
|
||||||
|
for i := range test.pre {
|
||||||
|
pre[i] = []byte(test.pre[i])
|
||||||
|
}
|
||||||
|
w := NewIndentWriter(b, pre...)
|
||||||
|
if _, err := w.Write([]byte(test.inp)); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if got := b.String(); got != test.exp {
|
||||||
|
t.Errorf("mismatch %q != %q", got, test.exp)
|
||||||
|
t.Log(got)
|
||||||
|
t.Log(test.exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
vendor/github.com/kr/text/mc/Readme
generated
vendored
Normal file
9
vendor/github.com/kr/text/mc/Readme
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Command mc prints in multiple columns.
|
||||||
|
|
||||||
|
Usage: mc [-] [-N] [file...]
|
||||||
|
|
||||||
|
Mc splits the input into as many columns as will fit in N
|
||||||
|
print positions. If the output is a tty, the default N is
|
||||||
|
the number of characters in a terminal line; otherwise the
|
||||||
|
default N is 80. Under option - each input line ending in
|
||||||
|
a colon ':' is printed separately.
|
62
vendor/github.com/kr/text/mc/mc.go
generated
vendored
Normal file
62
vendor/github.com/kr/text/mc/mc.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Command mc prints in multiple columns.
|
||||||
|
//
|
||||||
|
// Usage: mc [-] [-N] [file...]
|
||||||
|
//
|
||||||
|
// Mc splits the input into as many columns as will fit in N
|
||||||
|
// print positions. If the output is a tty, the default N is
|
||||||
|
// the number of characters in a terminal line; otherwise the
|
||||||
|
// default N is 80. Under option - each input line ending in
|
||||||
|
// a colon ':' is printed separately.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"github.com/kr/text/colwriter"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var width int
|
||||||
|
var flag uint
|
||||||
|
args := os.Args[1:]
|
||||||
|
for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' {
|
||||||
|
if len(args[0]) > 1 {
|
||||||
|
width, _ = strconv.Atoi(args[0][1:])
|
||||||
|
} else {
|
||||||
|
flag |= colwriter.BreakOnColon
|
||||||
|
}
|
||||||
|
args = args[1:]
|
||||||
|
}
|
||||||
|
if width < 1 {
|
||||||
|
_, width, _ = pty.Getsize(os.Stdout)
|
||||||
|
}
|
||||||
|
if width < 1 {
|
||||||
|
width = 80
|
||||||
|
}
|
||||||
|
|
||||||
|
w := colwriter.NewWriter(os.Stdout, width, flag)
|
||||||
|
if len(args) > 0 {
|
||||||
|
for _, s := range args {
|
||||||
|
if f, err := os.Open(s); err == nil {
|
||||||
|
copyin(w, f)
|
||||||
|
f.Close()
|
||||||
|
} else {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyin(w, os.Stdin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyin(w *colwriter.Writer, r io.Reader) {
|
||||||
|
if _, err := io.Copy(w, r); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
86
vendor/github.com/kr/text/wrap.go
generated
vendored
Normal file
86
vendor/github.com/kr/text/wrap.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
62
vendor/github.com/kr/text/wrap_test.go
generated
vendored
Normal file
62
vendor/github.com/kr/text/wrap_test.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var text = "The quick brown fox jumps over the lazy dog."
|
||||||
|
|
||||||
|
func TestWrap(t *testing.T) {
|
||||||
|
exp := [][]string{
|
||||||
|
{"The", "quick", "brown", "fox"},
|
||||||
|
{"jumps", "over", "the", "lazy", "dog."},
|
||||||
|
}
|
||||||
|
words := bytes.Split([]byte(text), sp)
|
||||||
|
got := WrapWords(words, 1, 24, defaultPenalty)
|
||||||
|
if len(exp) != len(got) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
for i := range exp {
|
||||||
|
if len(exp[i]) != len(got[i]) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
for j := range exp[i] {
|
||||||
|
if exp[i][j] != string(got[i][j]) {
|
||||||
|
t.Fatal(i, exp[i][j], got[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapNarrow(t *testing.T) {
|
||||||
|
exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog."
|
||||||
|
if Wrap(text, 5) != exp {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapOneLine(t *testing.T) {
|
||||||
|
exp := "The quick brown fox jumps over the lazy dog."
|
||||||
|
if Wrap(text, 500) != exp {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrapBug1(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
limit int
|
||||||
|
text string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{4, "aaaaa", "aaaaa"},
|
||||||
|
{4, "a aaaaa", "a\naaaaa"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range cases {
|
||||||
|
got := Wrap(test.text, test.limit)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Wrap(%q, %d) = %q want %q", test.text, test.limit, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/shiena/ansicolor
generated
vendored
1
vendor/github.com/shiena/ansicolor
generated
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit a422bbe96644373c5753384a59d678f7d261ff10
|
|
27
vendor/github.com/shiena/ansicolor/.gitignore
generated
vendored
Normal file
27
vendor/github.com/shiena/ansicolor/.gitignore
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Created by http://www.gitignore.io
|
||||||
|
|
||||||
|
### Go ###
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
|
21
vendor/github.com/shiena/ansicolor/LICENSE
generated
vendored
Normal file
21
vendor/github.com/shiena/ansicolor/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) [2014] [shiena]
|
||||||
|
|
||||||
|
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.
|
101
vendor/github.com/shiena/ansicolor/README.md
generated
vendored
Normal file
101
vendor/github.com/shiena/ansicolor/README.md
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
[![GoDoc](https://godoc.org/github.com/shiena/ansicolor?status.svg)](https://godoc.org/github.com/shiena/ansicolor)
|
||||||
|
|
||||||
|
# ansicolor
|
||||||
|
|
||||||
|
Ansicolor library provides color console in Windows as ANSICON for Golang.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
|Escape sequence|Text attributes|
|
||||||
|
|---------------|----|
|
||||||
|
|\x1b[0m|All attributes off(color at startup)|
|
||||||
|
|\x1b[1m|Bold on(enable foreground intensity)|
|
||||||
|
|\x1b[4m|Underline on|
|
||||||
|
|\x1b[5m|Blink on(enable background intensity)|
|
||||||
|
|\x1b[21m|Bold off(disable foreground intensity)|
|
||||||
|
|\x1b[24m|Underline off|
|
||||||
|
|\x1b[25m|Blink off(disable background intensity)|
|
||||||
|
|
||||||
|
|Escape sequence|Foreground colors|
|
||||||
|
|---------------|----|
|
||||||
|
|\x1b[30m|Black|
|
||||||
|
|\x1b[31m|Red|
|
||||||
|
|\x1b[32m|Green|
|
||||||
|
|\x1b[33m|Yellow|
|
||||||
|
|\x1b[34m|Blue|
|
||||||
|
|\x1b[35m|Magenta|
|
||||||
|
|\x1b[36m|Cyan|
|
||||||
|
|\x1b[37m|White|
|
||||||
|
|\x1b[39m|Default(foreground color at startup)|
|
||||||
|
|\x1b[90m|Light Gray|
|
||||||
|
|\x1b[91m|Light Red|
|
||||||
|
|\x1b[92m|Light Green|
|
||||||
|
|\x1b[93m|Light Yellow|
|
||||||
|
|\x1b[94m|Light Blue|
|
||||||
|
|\x1b[95m|Light Magenta|
|
||||||
|
|\x1b[96m|Light Cyan|
|
||||||
|
|\x1b[97m|Light White|
|
||||||
|
|
||||||
|
|Escape sequence|Background colors|
|
||||||
|
|---------------|----|
|
||||||
|
|\x1b[40m|Black|
|
||||||
|
|\x1b[41m|Red|
|
||||||
|
|\x1b[42m|Green|
|
||||||
|
|\x1b[43m|Yellow|
|
||||||
|
|\x1b[44m|Blue|
|
||||||
|
|\x1b[45m|Magenta|
|
||||||
|
|\x1b[46m|Cyan|
|
||||||
|
|\x1b[47m|White|
|
||||||
|
|\x1b[49m|Default(background color at startup)|
|
||||||
|
|\x1b[100m|Light Gray|
|
||||||
|
|\x1b[101m|Light Red|
|
||||||
|
|\x1b[102m|Light Green|
|
||||||
|
|\x1b[103m|Light Yellow|
|
||||||
|
|\x1b[104m|Light Blue|
|
||||||
|
|\x1b[105m|Light Magenta|
|
||||||
|
|\x1b[106m|Light Cyan|
|
||||||
|
|\x1b[107m|Light White|
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||||
|
text := "%sforeground %sbold%s %sbackground%s\n"
|
||||||
|
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
![screenshot](https://gist.githubusercontent.com/shiena/a1bada24b525314a7d5e/raw/c763aa7cda6e4fefaccf831e2617adc40b6151c7/main.png)
|
||||||
|
|
||||||
|
## See also:
|
||||||
|
|
||||||
|
- https://github.com/daviddengcn/go-colortext
|
||||||
|
- https://github.com/adoxa/ansicon
|
||||||
|
- https://github.com/aslakhellesoy/wac
|
||||||
|
- https://github.com/wsxiaoys/terminal
|
||||||
|
- https://github.com/mattn/go-colorable
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork it
|
||||||
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
|
4. Push to the branch (`git push origin my-new-feature`)
|
||||||
|
5. Create new Pull Request
|
||||||
|
|
42
vendor/github.com/shiena/ansicolor/ansicolor.go
generated
vendored
Normal file
42
vendor/github.com/shiena/ansicolor/ansicolor.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package ansicolor provides color console in Windows as ANSICON.
|
||||||
|
package ansicolor
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type outputMode int
|
||||||
|
|
||||||
|
// DiscardNonColorEscSeq supports the divided color escape sequence.
|
||||||
|
// But non-color escape sequence is not output.
|
||||||
|
// Please use the OutputNonColorEscSeq If you want to output a non-color
|
||||||
|
// escape sequences such as ncurses. However, it does not support the divided
|
||||||
|
// color escape sequence.
|
||||||
|
const (
|
||||||
|
_ outputMode = iota
|
||||||
|
DiscardNonColorEscSeq
|
||||||
|
OutputNonColorEscSeq
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
|
||||||
|
// using io.Writer w as its initial contents.
|
||||||
|
// In the console of Windows, which change the foreground and background
|
||||||
|
// colors of the text by the escape sequence.
|
||||||
|
// In the console of other systems, which writes to w all text.
|
||||||
|
func NewAnsiColorWriter(w io.Writer) io.Writer {
|
||||||
|
return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewModeAnsiColorWriter create and initializes a new ansiColorWriter
|
||||||
|
// by specifying the outputMode.
|
||||||
|
func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer {
|
||||||
|
if _, ok := w.(*ansiColorWriter); !ok {
|
||||||
|
return &ansiColorWriter{
|
||||||
|
w: w,
|
||||||
|
mode: mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
27
vendor/github.com/shiena/ansicolor/ansicolor/main.go
generated
vendored
Normal file
27
vendor/github.com/shiena/ansicolor/ansicolor/main.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
The ansicolor command colors a console text by ANSI escape sequence like wac.
|
||||||
|
|
||||||
|
$ go get github.com/shiena/ansicolor/ansicolor
|
||||||
|
|
||||||
|
See also:
|
||||||
|
https://github.com/aslakhellesoy/wac
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||||
|
io.Copy(w, os.Stdin)
|
||||||
|
}
|
18
vendor/github.com/shiena/ansicolor/ansicolor_ansi.go
generated
vendored
Normal file
18
vendor/github.com/shiena/ansicolor/ansicolor_ansi.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package ansicolor
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type ansiColorWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
mode outputMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||||
|
return cw.w.Write(p)
|
||||||
|
}
|
29
vendor/github.com/shiena/ansicolor/ansicolor_test.go
generated
vendored
Normal file
29
vendor/github.com/shiena/ansicolor/ansicolor_test.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2015 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ansicolor_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewAnsiColor1(t *testing.T) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w := ansicolor.NewAnsiColorWriter(inner)
|
||||||
|
if w == inner {
|
||||||
|
t.Errorf("Get %#v, want %#v", w, inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAnsiColor2(t *testing.T) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w1 := ansicolor.NewAnsiColorWriter(inner)
|
||||||
|
w2 := ansicolor.NewAnsiColorWriter(w1)
|
||||||
|
if w1 != w2 {
|
||||||
|
t.Errorf("Get %#v, want %#v", w1, w2)
|
||||||
|
}
|
||||||
|
}
|
417
vendor/github.com/shiena/ansicolor/ansicolor_windows.go
generated
vendored
Normal file
417
vendor/github.com/shiena/ansicolor/ansicolor_windows.go
generated
vendored
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package ansicolor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type csiState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
outsideCsiCode csiState = iota
|
||||||
|
firstCsiCode
|
||||||
|
secondCsiCode
|
||||||
|
)
|
||||||
|
|
||||||
|
type parseResult int
|
||||||
|
|
||||||
|
const (
|
||||||
|
noConsole parseResult = iota
|
||||||
|
changedColor
|
||||||
|
unknown
|
||||||
|
)
|
||||||
|
|
||||||
|
type ansiColorWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
mode outputMode
|
||||||
|
state csiState
|
||||||
|
paramStartBuf bytes.Buffer
|
||||||
|
paramBuf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
firstCsiChar byte = '\x1b'
|
||||||
|
secondeCsiChar byte = '['
|
||||||
|
separatorChar byte = ';'
|
||||||
|
sgrCode byte = 'm'
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
foregroundBlue = uint16(0x0001)
|
||||||
|
foregroundGreen = uint16(0x0002)
|
||||||
|
foregroundRed = uint16(0x0004)
|
||||||
|
foregroundIntensity = uint16(0x0008)
|
||||||
|
backgroundBlue = uint16(0x0010)
|
||||||
|
backgroundGreen = uint16(0x0020)
|
||||||
|
backgroundRed = uint16(0x0040)
|
||||||
|
backgroundIntensity = uint16(0x0080)
|
||||||
|
underscore = uint16(0x8000)
|
||||||
|
|
||||||
|
foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
|
||||||
|
backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ansiReset = "0"
|
||||||
|
ansiIntensityOn = "1"
|
||||||
|
ansiIntensityOff = "21"
|
||||||
|
ansiUnderlineOn = "4"
|
||||||
|
ansiUnderlineOff = "24"
|
||||||
|
ansiBlinkOn = "5"
|
||||||
|
ansiBlinkOff = "25"
|
||||||
|
|
||||||
|
ansiForegroundBlack = "30"
|
||||||
|
ansiForegroundRed = "31"
|
||||||
|
ansiForegroundGreen = "32"
|
||||||
|
ansiForegroundYellow = "33"
|
||||||
|
ansiForegroundBlue = "34"
|
||||||
|
ansiForegroundMagenta = "35"
|
||||||
|
ansiForegroundCyan = "36"
|
||||||
|
ansiForegroundWhite = "37"
|
||||||
|
ansiForegroundDefault = "39"
|
||||||
|
|
||||||
|
ansiBackgroundBlack = "40"
|
||||||
|
ansiBackgroundRed = "41"
|
||||||
|
ansiBackgroundGreen = "42"
|
||||||
|
ansiBackgroundYellow = "43"
|
||||||
|
ansiBackgroundBlue = "44"
|
||||||
|
ansiBackgroundMagenta = "45"
|
||||||
|
ansiBackgroundCyan = "46"
|
||||||
|
ansiBackgroundWhite = "47"
|
||||||
|
ansiBackgroundDefault = "49"
|
||||||
|
|
||||||
|
ansiLightForegroundGray = "90"
|
||||||
|
ansiLightForegroundRed = "91"
|
||||||
|
ansiLightForegroundGreen = "92"
|
||||||
|
ansiLightForegroundYellow = "93"
|
||||||
|
ansiLightForegroundBlue = "94"
|
||||||
|
ansiLightForegroundMagenta = "95"
|
||||||
|
ansiLightForegroundCyan = "96"
|
||||||
|
ansiLightForegroundWhite = "97"
|
||||||
|
|
||||||
|
ansiLightBackgroundGray = "100"
|
||||||
|
ansiLightBackgroundRed = "101"
|
||||||
|
ansiLightBackgroundGreen = "102"
|
||||||
|
ansiLightBackgroundYellow = "103"
|
||||||
|
ansiLightBackgroundBlue = "104"
|
||||||
|
ansiLightBackgroundMagenta = "105"
|
||||||
|
ansiLightBackgroundCyan = "106"
|
||||||
|
ansiLightBackgroundWhite = "107"
|
||||||
|
)
|
||||||
|
|
||||||
|
type drawType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
foreground drawType = iota
|
||||||
|
background
|
||||||
|
)
|
||||||
|
|
||||||
|
type winColor struct {
|
||||||
|
code uint16
|
||||||
|
drawType drawType
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorMap = map[string]winColor{
|
||||||
|
ansiForegroundBlack: {0, foreground},
|
||||||
|
ansiForegroundRed: {foregroundRed, foreground},
|
||||||
|
ansiForegroundGreen: {foregroundGreen, foreground},
|
||||||
|
ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground},
|
||||||
|
ansiForegroundBlue: {foregroundBlue, foreground},
|
||||||
|
ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
|
||||||
|
ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
|
||||||
|
ansiBackgroundBlack: {0, background},
|
||||||
|
ansiBackgroundRed: {backgroundRed, background},
|
||||||
|
ansiBackgroundGreen: {backgroundGreen, background},
|
||||||
|
ansiBackgroundYellow: {backgroundRed | backgroundGreen, background},
|
||||||
|
ansiBackgroundBlue: {backgroundBlue, background},
|
||||||
|
ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
|
||||||
|
ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiBackgroundDefault: {0, background},
|
||||||
|
|
||||||
|
ansiLightForegroundGray: {foregroundIntensity, foreground},
|
||||||
|
ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground},
|
||||||
|
ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground},
|
||||||
|
ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
|
||||||
|
ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
|
||||||
|
ansiLightBackgroundGray: {backgroundIntensity, background},
|
||||||
|
ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background},
|
||||||
|
ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background},
|
||||||
|
ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background},
|
||||||
|
ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||||
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
defaultAttr *textAttributes
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo != nil {
|
||||||
|
colorMap[ansiForegroundDefault] = winColor{
|
||||||
|
screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
|
||||||
|
foreground,
|
||||||
|
}
|
||||||
|
colorMap[ansiBackgroundDefault] = winColor{
|
||||||
|
screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
|
||||||
|
background,
|
||||||
|
}
|
||||||
|
defaultAttr = convertTextAttr(screenInfo.WAttributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type coord struct {
|
||||||
|
X, Y int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type smallRect struct {
|
||||||
|
Left, Top, Right, Bottom int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleScreenBufferInfo struct {
|
||||||
|
DwSize coord
|
||||||
|
DwCursorPosition coord
|
||||||
|
WAttributes uint16
|
||||||
|
SrWindow smallRect
|
||||||
|
DwMaximumWindowSize coord
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
|
||||||
|
var csbi consoleScreenBufferInfo
|
||||||
|
ret, _, _ := procGetConsoleScreenBufferInfo.Call(
|
||||||
|
hConsoleOutput,
|
||||||
|
uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
if ret == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &csbi
|
||||||
|
}
|
||||||
|
|
||||||
|
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
|
||||||
|
ret, _, _ := procSetConsoleTextAttribute.Call(
|
||||||
|
hConsoleOutput,
|
||||||
|
uintptr(wAttributes))
|
||||||
|
return ret != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type textAttributes struct {
|
||||||
|
foregroundColor uint16
|
||||||
|
backgroundColor uint16
|
||||||
|
foregroundIntensity uint16
|
||||||
|
backgroundIntensity uint16
|
||||||
|
underscore uint16
|
||||||
|
otherAttributes uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertTextAttr(winAttr uint16) *textAttributes {
|
||||||
|
fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
|
||||||
|
bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
|
||||||
|
fgIntensity := winAttr & foregroundIntensity
|
||||||
|
bgIntensity := winAttr & backgroundIntensity
|
||||||
|
underline := winAttr & underscore
|
||||||
|
otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
|
||||||
|
return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertWinAttr(textAttr *textAttributes) uint16 {
|
||||||
|
var winAttr uint16
|
||||||
|
winAttr |= textAttr.foregroundColor
|
||||||
|
winAttr |= textAttr.backgroundColor
|
||||||
|
winAttr |= textAttr.foregroundIntensity
|
||||||
|
winAttr |= textAttr.backgroundIntensity
|
||||||
|
winAttr |= textAttr.underscore
|
||||||
|
winAttr |= textAttr.otherAttributes
|
||||||
|
return winAttr
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeColor(param []byte) parseResult {
|
||||||
|
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo == nil {
|
||||||
|
return noConsole
|
||||||
|
}
|
||||||
|
|
||||||
|
winAttr := convertTextAttr(screenInfo.WAttributes)
|
||||||
|
strParam := string(param)
|
||||||
|
if len(strParam) <= 0 {
|
||||||
|
strParam = "0"
|
||||||
|
}
|
||||||
|
csiParam := strings.Split(strParam, string(separatorChar))
|
||||||
|
for _, p := range csiParam {
|
||||||
|
c, ok := colorMap[p]
|
||||||
|
switch {
|
||||||
|
case !ok:
|
||||||
|
switch p {
|
||||||
|
case ansiReset:
|
||||||
|
winAttr.foregroundColor = defaultAttr.foregroundColor
|
||||||
|
winAttr.backgroundColor = defaultAttr.backgroundColor
|
||||||
|
winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
|
||||||
|
winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
|
||||||
|
winAttr.underscore = 0
|
||||||
|
winAttr.otherAttributes = 0
|
||||||
|
case ansiIntensityOn:
|
||||||
|
winAttr.foregroundIntensity = foregroundIntensity
|
||||||
|
case ansiIntensityOff:
|
||||||
|
winAttr.foregroundIntensity = 0
|
||||||
|
case ansiUnderlineOn:
|
||||||
|
winAttr.underscore = underscore
|
||||||
|
case ansiUnderlineOff:
|
||||||
|
winAttr.underscore = 0
|
||||||
|
case ansiBlinkOn:
|
||||||
|
winAttr.backgroundIntensity = backgroundIntensity
|
||||||
|
case ansiBlinkOff:
|
||||||
|
winAttr.backgroundIntensity = 0
|
||||||
|
default:
|
||||||
|
// unknown code
|
||||||
|
}
|
||||||
|
case c.drawType == foreground:
|
||||||
|
winAttr.foregroundColor = c.code
|
||||||
|
case c.drawType == background:
|
||||||
|
winAttr.backgroundColor = c.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winTextAttribute := convertWinAttr(winAttr)
|
||||||
|
setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
|
||||||
|
|
||||||
|
return changedColor
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEscapeSequence(command byte, param []byte) parseResult {
|
||||||
|
if defaultAttr == nil {
|
||||||
|
return noConsole
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case sgrCode:
|
||||||
|
return changeColor(param)
|
||||||
|
default:
|
||||||
|
return unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *ansiColorWriter) flushBuffer() (int, error) {
|
||||||
|
return cw.flushTo(cw.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *ansiColorWriter) resetBuffer() (int, error) {
|
||||||
|
return cw.flushTo(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
|
||||||
|
var n1, n2 int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
startBytes := cw.paramStartBuf.Bytes()
|
||||||
|
cw.paramStartBuf.Reset()
|
||||||
|
if w != nil {
|
||||||
|
n1, err = cw.w.Write(startBytes)
|
||||||
|
if err != nil {
|
||||||
|
return n1, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n1 = len(startBytes)
|
||||||
|
}
|
||||||
|
paramBytes := cw.paramBuf.Bytes()
|
||||||
|
cw.paramBuf.Reset()
|
||||||
|
if w != nil {
|
||||||
|
n2, err = cw.w.Write(paramBytes)
|
||||||
|
if err != nil {
|
||||||
|
return n1 + n2, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n2 = len(paramBytes)
|
||||||
|
}
|
||||||
|
return n1 + n2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isParameterChar(b byte) bool {
|
||||||
|
return ('0' <= b && b <= '9') || b == separatorChar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *ansiColorWriter) Write(p []byte) (int, error) {
|
||||||
|
r, nw, first, last := 0, 0, 0, 0
|
||||||
|
if cw.mode != DiscardNonColorEscSeq {
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
cw.resetBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for i, ch := range p {
|
||||||
|
switch cw.state {
|
||||||
|
case outsideCsiCode:
|
||||||
|
if ch == firstCsiChar {
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
cw.state = firstCsiCode
|
||||||
|
}
|
||||||
|
case firstCsiCode:
|
||||||
|
switch ch {
|
||||||
|
case firstCsiChar:
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
break
|
||||||
|
case secondeCsiChar:
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
cw.state = secondCsiCode
|
||||||
|
last = i - 1
|
||||||
|
default:
|
||||||
|
cw.resetBuffer()
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
case secondCsiCode:
|
||||||
|
if isParameterChar(ch) {
|
||||||
|
cw.paramBuf.WriteByte(ch)
|
||||||
|
} else {
|
||||||
|
nw, err = cw.w.Write(p[first:last])
|
||||||
|
r += nw
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
first = i + 1
|
||||||
|
result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
|
||||||
|
if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
|
||||||
|
cw.paramBuf.WriteByte(ch)
|
||||||
|
nw, err := cw.flushBuffer()
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
r += nw
|
||||||
|
} else {
|
||||||
|
n, _ := cw.resetBuffer()
|
||||||
|
// Add one more to the size of the buffer for the last ch
|
||||||
|
r += n + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
|
||||||
|
nw, err = cw.w.Write(p[first:len(p)])
|
||||||
|
r += nw
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, err
|
||||||
|
}
|
277
vendor/github.com/shiena/ansicolor/ansicolor_windows_test.go
generated
vendored
Normal file
277
vendor/github.com/shiena/ansicolor/ansicolor_windows_test.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package ansicolor_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
. "github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWritePlanText(t *testing.T) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w := ansicolor.NewAnsiColorWriter(inner)
|
||||||
|
expected := "plain text"
|
||||||
|
fmt.Fprintf(w, expected)
|
||||||
|
actual := inner.String()
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("Get %q, want %q", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteParseText(t *testing.T) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w := ansicolor.NewAnsiColorWriter(inner)
|
||||||
|
|
||||||
|
inputTail := "\x1b[0mtail text"
|
||||||
|
expectedTail := "tail text"
|
||||||
|
fmt.Fprintf(w, inputTail)
|
||||||
|
actualTail := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualTail != expectedTail {
|
||||||
|
t.Errorf("Get %q, want %q", actualTail, expectedTail)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputHead := "head text\x1b[0m"
|
||||||
|
expectedHead := "head text"
|
||||||
|
fmt.Fprintf(w, inputHead)
|
||||||
|
actualHead := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualHead != expectedHead {
|
||||||
|
t.Errorf("Get %q, want %q", actualHead, expectedHead)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputBothEnds := "both ends \x1b[0m text"
|
||||||
|
expectedBothEnds := "both ends text"
|
||||||
|
fmt.Fprintf(w, inputBothEnds)
|
||||||
|
actualBothEnds := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualBothEnds != expectedBothEnds {
|
||||||
|
t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc"
|
||||||
|
expectedManyEsc := "\x1b\x1b\x1b many esc"
|
||||||
|
fmt.Fprintf(w, inputManyEsc)
|
||||||
|
actualManyEsc := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualManyEsc != expectedManyEsc {
|
||||||
|
t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedSplit := "split text"
|
||||||
|
for _, ch := range "split \x1b[0m text" {
|
||||||
|
fmt.Fprintf(w, string(ch))
|
||||||
|
}
|
||||||
|
actualSplit := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualSplit != expectedSplit {
|
||||||
|
t.Errorf("Get %q, want %q", actualSplit, expectedSplit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type screenNotFoundError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w := ansicolor.NewAnsiColorWriter(inner)
|
||||||
|
fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText)
|
||||||
|
|
||||||
|
actualText = inner.String()
|
||||||
|
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo != nil {
|
||||||
|
actualAttributes = screenInfo.WAttributes
|
||||||
|
} else {
|
||||||
|
err = &screenNotFoundError{}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type testParam struct {
|
||||||
|
text string
|
||||||
|
attributes uint16
|
||||||
|
ansiColor string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAnsiColorText(t *testing.T) {
|
||||||
|
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo == nil {
|
||||||
|
t.Fatal("Could not get ConsoleScreenBufferInfo")
|
||||||
|
}
|
||||||
|
defer ChangeColor(screenInfo.WAttributes)
|
||||||
|
defaultFgColor := screenInfo.WAttributes & uint16(0x0007)
|
||||||
|
defaultBgColor := screenInfo.WAttributes & uint16(0x0070)
|
||||||
|
defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008)
|
||||||
|
defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080)
|
||||||
|
|
||||||
|
fgParam := []testParam{
|
||||||
|
{"foreground black ", uint16(0x0000 | 0x0000), "30"},
|
||||||
|
{"foreground red ", uint16(0x0004 | 0x0000), "31"},
|
||||||
|
{"foreground green ", uint16(0x0002 | 0x0000), "32"},
|
||||||
|
{"foreground yellow ", uint16(0x0006 | 0x0000), "33"},
|
||||||
|
{"foreground blue ", uint16(0x0001 | 0x0000), "34"},
|
||||||
|
{"foreground magenta", uint16(0x0005 | 0x0000), "35"},
|
||||||
|
{"foreground cyan ", uint16(0x0003 | 0x0000), "36"},
|
||||||
|
{"foreground white ", uint16(0x0007 | 0x0000), "37"},
|
||||||
|
{"foreground default", defaultFgColor | 0x0000, "39"},
|
||||||
|
{"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"},
|
||||||
|
{"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"},
|
||||||
|
{"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"},
|
||||||
|
{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"},
|
||||||
|
{"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"},
|
||||||
|
{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"},
|
||||||
|
{"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"},
|
||||||
|
{"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"},
|
||||||
|
}
|
||||||
|
|
||||||
|
bgParam := []testParam{
|
||||||
|
{"background black ", uint16(0x0007 | 0x0000), "40"},
|
||||||
|
{"background red ", uint16(0x0007 | 0x0040), "41"},
|
||||||
|
{"background green ", uint16(0x0007 | 0x0020), "42"},
|
||||||
|
{"background yellow ", uint16(0x0007 | 0x0060), "43"},
|
||||||
|
{"background blue ", uint16(0x0007 | 0x0010), "44"},
|
||||||
|
{"background magenta", uint16(0x0007 | 0x0050), "45"},
|
||||||
|
{"background cyan ", uint16(0x0007 | 0x0030), "46"},
|
||||||
|
{"background white ", uint16(0x0007 | 0x0070), "47"},
|
||||||
|
{"background default", uint16(0x0007) | defaultBgColor, "49"},
|
||||||
|
{"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"},
|
||||||
|
{"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"},
|
||||||
|
{"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"},
|
||||||
|
{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"},
|
||||||
|
{"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"},
|
||||||
|
{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"},
|
||||||
|
{"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"},
|
||||||
|
{"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"},
|
||||||
|
}
|
||||||
|
|
||||||
|
resetParam := []testParam{
|
||||||
|
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"},
|
||||||
|
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
boldParam := []testParam{
|
||||||
|
{"bold on", uint16(0x0007 | 0x0008), "1"},
|
||||||
|
{"bold off", uint16(0x0007), "21"},
|
||||||
|
}
|
||||||
|
|
||||||
|
underscoreParam := []testParam{
|
||||||
|
{"underscore on", uint16(0x0007 | 0x8000), "4"},
|
||||||
|
{"underscore off", uint16(0x0007), "24"},
|
||||||
|
}
|
||||||
|
|
||||||
|
blinkParam := []testParam{
|
||||||
|
{"blink on", uint16(0x0007 | 0x0080), "5"},
|
||||||
|
{"blink off", uint16(0x0007), "25"},
|
||||||
|
}
|
||||||
|
|
||||||
|
mixedParam := []testParam{
|
||||||
|
{"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"},
|
||||||
|
{"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"},
|
||||||
|
{"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"},
|
||||||
|
{"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"},
|
||||||
|
{"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"},
|
||||||
|
{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"},
|
||||||
|
{"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"},
|
||||||
|
{"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"},
|
||||||
|
{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) {
|
||||||
|
actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor)
|
||||||
|
if actualText != expectedText {
|
||||||
|
t.Errorf("Get %q, want %q", actualText, expectedText)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Could not get ConsoleScreenBufferInfo")
|
||||||
|
}
|
||||||
|
if actualAttributes != expectedAttributes {
|
||||||
|
t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range fgParam {
|
||||||
|
ResetColor()
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range bgParam {
|
||||||
|
ChangeColor(uint16(0x0070 | 0x0007))
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range resetParam {
|
||||||
|
ChangeColor(uint16(0x0000 | 0x0070 | 0x0008))
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetColor()
|
||||||
|
for _, v := range boldParam {
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetColor()
|
||||||
|
for _, v := range underscoreParam {
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetColor()
|
||||||
|
for _, v := range blinkParam {
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range mixedParam {
|
||||||
|
ResetColor()
|
||||||
|
assertTextAttribute(v.text, v.attributes, v.ansiColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnoreUnknownSequences(t *testing.T) {
|
||||||
|
inner := bytes.NewBufferString("")
|
||||||
|
w := ansicolor.NewModeAnsiColorWriter(inner, ansicolor.OutputNonColorEscSeq)
|
||||||
|
|
||||||
|
inputText := "\x1b[=decpath mode"
|
||||||
|
expectedTail := inputText
|
||||||
|
fmt.Fprintf(w, inputText)
|
||||||
|
actualTail := inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualTail != expectedTail {
|
||||||
|
t.Errorf("Get %q, want %q", actualTail, expectedTail)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputText = "\x1b[=tailing esc and bracket\x1b["
|
||||||
|
expectedTail = inputText
|
||||||
|
fmt.Fprintf(w, inputText)
|
||||||
|
actualTail = inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualTail != expectedTail {
|
||||||
|
t.Errorf("Get %q, want %q", actualTail, expectedTail)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputText = "\x1b[?tailing esc\x1b"
|
||||||
|
expectedTail = inputText
|
||||||
|
fmt.Fprintf(w, inputText)
|
||||||
|
actualTail = inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualTail != expectedTail {
|
||||||
|
t.Errorf("Get %q, want %q", actualTail, expectedTail)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputText = "\x1b[1h;3punended color code invalid\x1b3"
|
||||||
|
expectedTail = inputText
|
||||||
|
fmt.Fprintf(w, inputText)
|
||||||
|
actualTail = inner.String()
|
||||||
|
inner.Reset()
|
||||||
|
if actualTail != expectedTail {
|
||||||
|
t.Errorf("Get %q, want %q", actualTail, expectedTail)
|
||||||
|
}
|
||||||
|
}
|
24
vendor/github.com/shiena/ansicolor/example_test.go
generated
vendored
Normal file
24
vendor/github.com/shiena/ansicolor/example_test.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ansicolor_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/shiena/ansicolor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNewAnsiColorWriter() {
|
||||||
|
w := ansicolor.NewAnsiColorWriter(os.Stdout)
|
||||||
|
text := "%sforeground %sbold%s %sbackground%s\n"
|
||||||
|
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m")
|
||||||
|
}
|
19
vendor/github.com/shiena/ansicolor/export_test.go
generated
vendored
Normal file
19
vendor/github.com/shiena/ansicolor/export_test.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2014 shiena Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package ansicolor
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo
|
||||||
|
|
||||||
|
func ChangeColor(color uint16) {
|
||||||
|
setConsoleTextAttribute(uintptr(syscall.Stdout), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetColor() {
|
||||||
|
ChangeColor(uint16(0x0007))
|
||||||
|
}
|
676
vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go
generated
vendored
Normal file
676
vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go
generated
vendored
Normal file
@ -0,0 +1,676 @@
|
|||||||
|
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package cache provides interface and implementation of a cache algorithms.
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cacher provides interface to implements a caching functionality.
|
||||||
|
// An implementation must be goroutine-safe.
|
||||||
|
type Cacher interface {
|
||||||
|
// Capacity returns cache capacity.
|
||||||
|
Capacity() int
|
||||||
|
|
||||||
|
// SetCapacity sets cache capacity.
|
||||||
|
SetCapacity(capacity int)
|
||||||
|
|
||||||
|
// Promote promotes the 'cache node'.
|
||||||
|
Promote(n *Node)
|
||||||
|
|
||||||
|
// Ban evicts the 'cache node' and prevent subsequent 'promote'.
|
||||||
|
Ban(n *Node)
|
||||||
|
|
||||||
|
// Evict evicts the 'cache node'.
|
||||||
|
Evict(n *Node)
|
||||||
|
|
||||||
|
// EvictNS evicts 'cache node' with the given namespace.
|
||||||
|
EvictNS(ns uint64)
|
||||||
|
|
||||||
|
// EvictAll evicts all 'cache node'.
|
||||||
|
EvictAll()
|
||||||
|
|
||||||
|
// Close closes the 'cache tree'
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is a 'cacheable object'. It may implements util.Releaser, if
|
||||||
|
// so the the Release method will be called once object is released.
|
||||||
|
type Value interface{}
|
||||||
|
|
||||||
|
type CacheGetter struct {
|
||||||
|
Cache *Cache
|
||||||
|
NS uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *CacheGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
|
||||||
|
return g.Cache.Get(g.NS, key, setFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The hash tables implementation is based on:
|
||||||
|
// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, Kunlong Zhang, and Michael Spear. ACM Symposium on Principles of Distributed Computing, Jul 2014.
|
||||||
|
|
||||||
|
const (
|
||||||
|
mInitialSize = 1 << 4
|
||||||
|
mOverflowThreshold = 1 << 5
|
||||||
|
mOverflowGrowThreshold = 1 << 7
|
||||||
|
)
|
||||||
|
|
||||||
|
type mBucket struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
node []*Node
|
||||||
|
frozen bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *mBucket) freeze() []*Node {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if !b.frozen {
|
||||||
|
b.frozen = true
|
||||||
|
}
|
||||||
|
return b.node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *mBucket) get(r *Cache, h *mNode, hash uint32, ns, key uint64, noset bool) (done, added bool, n *Node) {
|
||||||
|
b.mu.Lock()
|
||||||
|
|
||||||
|
if b.frozen {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the node.
|
||||||
|
for _, n := range b.node {
|
||||||
|
if n.hash == hash && n.ns == ns && n.key == key {
|
||||||
|
atomic.AddInt32(&n.ref, 1)
|
||||||
|
b.mu.Unlock()
|
||||||
|
return true, false, n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get only.
|
||||||
|
if noset {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return true, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create node.
|
||||||
|
n = &Node{
|
||||||
|
r: r,
|
||||||
|
hash: hash,
|
||||||
|
ns: ns,
|
||||||
|
key: key,
|
||||||
|
ref: 1,
|
||||||
|
}
|
||||||
|
// Add node to bucket.
|
||||||
|
b.node = append(b.node, n)
|
||||||
|
bLen := len(b.node)
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
// Update counter.
|
||||||
|
grow := atomic.AddInt32(&r.nodes, 1) >= h.growThreshold
|
||||||
|
if bLen > mOverflowThreshold {
|
||||||
|
grow = grow || atomic.AddInt32(&h.overflow, 1) >= mOverflowGrowThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow.
|
||||||
|
if grow && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
|
||||||
|
nhLen := len(h.buckets) << 1
|
||||||
|
nh := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, nhLen),
|
||||||
|
mask: uint32(nhLen) - 1,
|
||||||
|
pred: unsafe.Pointer(h),
|
||||||
|
growThreshold: int32(nhLen * mOverflowThreshold),
|
||||||
|
shrinkThreshold: int32(nhLen >> 1),
|
||||||
|
}
|
||||||
|
ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
|
||||||
|
if !ok {
|
||||||
|
panic("BUG: failed swapping head")
|
||||||
|
}
|
||||||
|
go nh.initBuckets()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, true, n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *mBucket) delete(r *Cache, h *mNode, hash uint32, ns, key uint64) (done, deleted bool) {
|
||||||
|
b.mu.Lock()
|
||||||
|
|
||||||
|
if b.frozen {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the node.
|
||||||
|
var (
|
||||||
|
n *Node
|
||||||
|
bLen int
|
||||||
|
)
|
||||||
|
for i := range b.node {
|
||||||
|
n = b.node[i]
|
||||||
|
if n.ns == ns && n.key == key {
|
||||||
|
if atomic.LoadInt32(&n.ref) == 0 {
|
||||||
|
deleted = true
|
||||||
|
|
||||||
|
// Call releaser.
|
||||||
|
if n.value != nil {
|
||||||
|
if r, ok := n.value.(util.Releaser); ok {
|
||||||
|
r.Release()
|
||||||
|
}
|
||||||
|
n.value = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove node from bucket.
|
||||||
|
b.node = append(b.node[:i], b.node[i+1:]...)
|
||||||
|
bLen = len(b.node)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if deleted {
|
||||||
|
// Call OnDel.
|
||||||
|
for _, f := range n.onDel {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update counter.
|
||||||
|
atomic.AddInt32(&r.size, int32(n.size)*-1)
|
||||||
|
shrink := atomic.AddInt32(&r.nodes, -1) < h.shrinkThreshold
|
||||||
|
if bLen >= mOverflowThreshold {
|
||||||
|
atomic.AddInt32(&h.overflow, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink.
|
||||||
|
if shrink && len(h.buckets) > mInitialSize && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
|
||||||
|
nhLen := len(h.buckets) >> 1
|
||||||
|
nh := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, nhLen),
|
||||||
|
mask: uint32(nhLen) - 1,
|
||||||
|
pred: unsafe.Pointer(h),
|
||||||
|
growThreshold: int32(nhLen * mOverflowThreshold),
|
||||||
|
shrinkThreshold: int32(nhLen >> 1),
|
||||||
|
}
|
||||||
|
ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
|
||||||
|
if !ok {
|
||||||
|
panic("BUG: failed swapping head")
|
||||||
|
}
|
||||||
|
go nh.initBuckets()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
type mNode struct {
|
||||||
|
buckets []unsafe.Pointer // []*mBucket
|
||||||
|
mask uint32
|
||||||
|
pred unsafe.Pointer // *mNode
|
||||||
|
resizeInProgess int32
|
||||||
|
|
||||||
|
overflow int32
|
||||||
|
growThreshold int32
|
||||||
|
shrinkThreshold int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *mNode) initBucket(i uint32) *mBucket {
|
||||||
|
if b := (*mBucket)(atomic.LoadPointer(&n.buckets[i])); b != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
p := (*mNode)(atomic.LoadPointer(&n.pred))
|
||||||
|
if p != nil {
|
||||||
|
var node []*Node
|
||||||
|
if n.mask > p.mask {
|
||||||
|
// Grow.
|
||||||
|
pb := (*mBucket)(atomic.LoadPointer(&p.buckets[i&p.mask]))
|
||||||
|
if pb == nil {
|
||||||
|
pb = p.initBucket(i & p.mask)
|
||||||
|
}
|
||||||
|
m := pb.freeze()
|
||||||
|
// Split nodes.
|
||||||
|
for _, x := range m {
|
||||||
|
if x.hash&n.mask == i {
|
||||||
|
node = append(node, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Shrink.
|
||||||
|
pb0 := (*mBucket)(atomic.LoadPointer(&p.buckets[i]))
|
||||||
|
if pb0 == nil {
|
||||||
|
pb0 = p.initBucket(i)
|
||||||
|
}
|
||||||
|
pb1 := (*mBucket)(atomic.LoadPointer(&p.buckets[i+uint32(len(n.buckets))]))
|
||||||
|
if pb1 == nil {
|
||||||
|
pb1 = p.initBucket(i + uint32(len(n.buckets)))
|
||||||
|
}
|
||||||
|
m0 := pb0.freeze()
|
||||||
|
m1 := pb1.freeze()
|
||||||
|
// Merge nodes.
|
||||||
|
node = make([]*Node, 0, len(m0)+len(m1))
|
||||||
|
node = append(node, m0...)
|
||||||
|
node = append(node, m1...)
|
||||||
|
}
|
||||||
|
b := &mBucket{node: node}
|
||||||
|
if atomic.CompareAndSwapPointer(&n.buckets[i], nil, unsafe.Pointer(b)) {
|
||||||
|
if len(node) > mOverflowThreshold {
|
||||||
|
atomic.AddInt32(&n.overflow, int32(len(node)-mOverflowThreshold))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*mBucket)(atomic.LoadPointer(&n.buckets[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *mNode) initBuckets() {
|
||||||
|
for i := range n.buckets {
|
||||||
|
n.initBucket(uint32(i))
|
||||||
|
}
|
||||||
|
atomic.StorePointer(&n.pred, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache is a 'cache map'.
|
||||||
|
type Cache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
mHead unsafe.Pointer // *mNode
|
||||||
|
nodes int32
|
||||||
|
size int32
|
||||||
|
cacher Cacher
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCache creates a new 'cache map'. The cacher is optional and
|
||||||
|
// may be nil.
|
||||||
|
func NewCache(cacher Cacher) *Cache {
|
||||||
|
h := &mNode{
|
||||||
|
buckets: make([]unsafe.Pointer, mInitialSize),
|
||||||
|
mask: mInitialSize - 1,
|
||||||
|
growThreshold: int32(mInitialSize * mOverflowThreshold),
|
||||||
|
shrinkThreshold: 0,
|
||||||
|
}
|
||||||
|
for i := range h.buckets {
|
||||||
|
h.buckets[i] = unsafe.Pointer(&mBucket{})
|
||||||
|
}
|
||||||
|
r := &Cache{
|
||||||
|
mHead: unsafe.Pointer(h),
|
||||||
|
cacher: cacher,
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Cache) getBucket(hash uint32) (*mNode, *mBucket) {
|
||||||
|
h := (*mNode)(atomic.LoadPointer(&r.mHead))
|
||||||
|
i := hash & h.mask
|
||||||
|
b := (*mBucket)(atomic.LoadPointer(&h.buckets[i]))
|
||||||
|
if b == nil {
|
||||||
|
b = h.initBucket(i)
|
||||||
|
}
|
||||||
|
return h, b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Cache) delete(n *Node) bool {
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(n.hash)
|
||||||
|
done, deleted := b.delete(r, h, n.hash, n.ns, n.key)
|
||||||
|
if done {
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nodes returns number of 'cache node' in the map.
|
||||||
|
func (r *Cache) Nodes() int {
|
||||||
|
return int(atomic.LoadInt32(&r.nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns sums of 'cache node' size in the map.
|
||||||
|
func (r *Cache) Size() int {
|
||||||
|
return int(atomic.LoadInt32(&r.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capacity returns cache capacity.
|
||||||
|
func (r *Cache) Capacity() int {
|
||||||
|
if r.cacher == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return r.cacher.Capacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCapacity sets cache capacity.
|
||||||
|
func (r *Cache) SetCapacity(capacity int) {
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.SetCapacity(capacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets 'cache node' with the given namespace and key.
|
||||||
|
// If cache node is not found and setFunc is not nil, Get will atomically creates
|
||||||
|
// the 'cache node' by calling setFunc. Otherwise Get will returns nil.
|
||||||
|
//
|
||||||
|
// The returned 'cache handle' should be released after use by calling Release
|
||||||
|
// method.
|
||||||
|
func (r *Cache) Get(ns, key uint64, setFunc func() (size int, value Value)) *Handle {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, setFunc == nil)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
n.mu.Lock()
|
||||||
|
if n.value == nil {
|
||||||
|
if setFunc == nil {
|
||||||
|
n.mu.Unlock()
|
||||||
|
n.unref()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.size, n.value = setFunc()
|
||||||
|
if n.value == nil {
|
||||||
|
n.size = 0
|
||||||
|
n.mu.Unlock()
|
||||||
|
n.unref()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&r.size, int32(n.size))
|
||||||
|
}
|
||||||
|
n.mu.Unlock()
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Promote(n)
|
||||||
|
}
|
||||||
|
return &Handle{unsafe.Pointer(n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes and ban 'cache node' with the given namespace and key.
|
||||||
|
// A banned 'cache node' will never inserted into the 'cache tree'. Ban
|
||||||
|
// only attributed to the particular 'cache node', so when a 'cache node'
|
||||||
|
// is recreated it will not be banned.
|
||||||
|
//
|
||||||
|
// If onDel is not nil, then it will be executed if such 'cache node'
|
||||||
|
// doesn't exist or once the 'cache node' is released.
|
||||||
|
//
|
||||||
|
// Delete return true is such 'cache node' exist.
|
||||||
|
func (r *Cache) Delete(ns, key uint64, onDel func()) bool {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, true)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
if onDel != nil {
|
||||||
|
n.mu.Lock()
|
||||||
|
n.onDel = append(n.onDel, onDel)
|
||||||
|
n.mu.Unlock()
|
||||||
|
}
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Ban(n)
|
||||||
|
}
|
||||||
|
n.unref()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if onDel != nil {
|
||||||
|
onDel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evict evicts 'cache node' with the given namespace and key. This will
|
||||||
|
// simply call Cacher.Evict.
|
||||||
|
//
|
||||||
|
// Evict return true is such 'cache node' exist.
|
||||||
|
func (r *Cache) Evict(ns, key uint64) bool {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := murmur32(ns, key, 0xf00)
|
||||||
|
for {
|
||||||
|
h, b := r.getBucket(hash)
|
||||||
|
done, _, n := b.get(r, h, hash, ns, key, true)
|
||||||
|
if done {
|
||||||
|
if n != nil {
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.Evict(n)
|
||||||
|
}
|
||||||
|
n.unref()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvictNS evicts 'cache node' with the given namespace. This will
|
||||||
|
// simply call Cacher.EvictNS.
|
||||||
|
func (r *Cache) EvictNS(ns uint64) {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.EvictNS(ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvictAll evicts all 'cache node'. This will simply call Cacher.EvictAll.
|
||||||
|
func (r *Cache) EvictAll() {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
if r.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.cacher != nil {
|
||||||
|
r.cacher.EvictAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the 'cache map' and releases all 'cache node'.
|
||||||
|
func (r *Cache) Close() error {
|
||||||
|
r.mu.Lock()
|
||||||
|
if !r.closed {
|
||||||
|
r.closed = true
|
||||||
|
|
||||||
|
if r.cacher != nil {
|
||||||
|
if err := r.cacher.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h := (*mNode)(r.mHead)
|
||||||
|
h.initBuckets()
|
||||||
|
|
||||||
|
for i := range h.buckets {
|
||||||
|
b := (*mBucket)(h.buckets[i])
|
||||||
|
for _, n := range b.node {
|
||||||
|
// Call releaser.
|
||||||
|
if n.value != nil {
|
||||||
|
if r, ok := n.value.(util.Releaser); ok {
|
||||||
|
r.Release()
|
||||||
|
}
|
||||||
|
n.value = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call OnDel.
|
||||||
|
for _, f := range n.onDel {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node is a 'cache node'.
|
||||||
|
type Node struct {
|
||||||
|
r *Cache
|
||||||
|
|
||||||
|
hash uint32
|
||||||
|
ns, key uint64
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
size int
|
||||||
|
value Value
|
||||||
|
|
||||||
|
ref int32
|
||||||
|
onDel []func()
|
||||||
|
|
||||||
|
CacheData unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NS returns this 'cache node' namespace.
|
||||||
|
func (n *Node) NS() uint64 {
|
||||||
|
return n.ns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns this 'cache node' key.
|
||||||
|
func (n *Node) Key() uint64 {
|
||||||
|
return n.key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns this 'cache node' size.
|
||||||
|
func (n *Node) Size() int {
|
||||||
|
return n.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns this 'cache node' value.
|
||||||
|
func (n *Node) Value() Value {
|
||||||
|
return n.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref returns this 'cache node' ref counter.
|
||||||
|
func (n *Node) Ref() int32 {
|
||||||
|
return atomic.LoadInt32(&n.ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandle returns an handle for this 'cache node'.
|
||||||
|
func (n *Node) GetHandle() *Handle {
|
||||||
|
if atomic.AddInt32(&n.ref, 1) <= 1 {
|
||||||
|
panic("BUG: Node.GetHandle on zero ref")
|
||||||
|
}
|
||||||
|
return &Handle{unsafe.Pointer(n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) unref() {
|
||||||
|
if atomic.AddInt32(&n.ref, -1) == 0 {
|
||||||
|
n.r.delete(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) unrefLocked() {
|
||||||
|
if atomic.AddInt32(&n.ref, -1) == 0 {
|
||||||
|
n.r.mu.RLock()
|
||||||
|
if !n.r.closed {
|
||||||
|
n.r.delete(n)
|
||||||
|
}
|
||||||
|
n.r.mu.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handle struct {
|
||||||
|
n unsafe.Pointer // *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) Value() Value {
|
||||||
|
n := (*Node)(atomic.LoadPointer(&h.n))
|
||||||
|
if n != nil {
|
||||||
|
return n.value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) Release() {
|
||||||
|
nPtr := atomic.LoadPointer(&h.n)
|
||||||
|
if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
|
||||||
|
n := (*Node)(nPtr)
|
||||||
|
n.unrefLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func murmur32(ns, key uint64, seed uint32) uint32 {
|
||||||
|
const (
|
||||||
|
m = uint32(0x5bd1e995)
|
||||||
|
r = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
k1 := uint32(ns >> 32)
|
||||||
|
k2 := uint32(ns)
|
||||||
|
k3 := uint32(key >> 32)
|
||||||
|
k4 := uint32(key)
|
||||||
|
|
||||||
|
k1 *= m
|
||||||
|
k1 ^= k1 >> r
|
||||||
|
k1 *= m
|
||||||
|
|
||||||
|
k2 *= m
|
||||||
|
k2 ^= k2 >> r
|
||||||
|
k2 *= m
|
||||||
|
|
||||||
|
k3 *= m
|
||||||
|
k3 ^= k3 >> r
|
||||||
|
k3 *= m
|
||||||
|
|
||||||
|
k4 *= m
|
||||||
|
k4 ^= k4 >> r
|
||||||
|
k4 *= m
|
||||||
|
|
||||||
|
h := seed
|
||||||
|
|
||||||
|
h *= m
|
||||||
|
h ^= k1
|
||||||
|
h *= m
|
||||||
|
h ^= k2
|
||||||
|
h *= m
|
||||||
|
h ^= k3
|
||||||
|
h *= m
|
||||||
|
h ^= k4
|
||||||
|
|
||||||
|
h ^= h >> 13
|
||||||
|
h *= m
|
||||||
|
h ^= h >> 15
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
195
vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go
generated
vendored
Normal file
195
vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lruNode struct {
|
||||||
|
n *Node
|
||||||
|
h *Handle
|
||||||
|
ban bool
|
||||||
|
|
||||||
|
next, prev *lruNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *lruNode) insert(at *lruNode) {
|
||||||
|
x := at.next
|
||||||
|
at.next = n
|
||||||
|
n.prev = at
|
||||||
|
n.next = x
|
||||||
|
x.prev = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *lruNode) remove() {
|
||||||
|
if n.prev != nil {
|
||||||
|
n.prev.next = n.next
|
||||||
|
n.next.prev = n.prev
|
||||||
|
n.prev = nil
|
||||||
|
n.next = nil
|
||||||
|
} else {
|
||||||
|
panic("BUG: removing removed node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lru struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
capacity int
|
||||||
|
used int
|
||||||
|
recent lruNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) reset() {
|
||||||
|
r.recent.next = &r.recent
|
||||||
|
r.recent.prev = &r.recent
|
||||||
|
r.used = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Capacity() int {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
return r.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) SetCapacity(capacity int) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
r.capacity = capacity
|
||||||
|
for r.used > r.capacity {
|
||||||
|
rn := r.recent.prev
|
||||||
|
if rn == nil {
|
||||||
|
panic("BUG: invalid LRU used or capacity counter")
|
||||||
|
}
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Promote(n *Node) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
if n.CacheData == nil {
|
||||||
|
if n.Size() <= r.capacity {
|
||||||
|
rn := &lruNode{n: n, h: n.GetHandle()}
|
||||||
|
rn.insert(&r.recent)
|
||||||
|
n.CacheData = unsafe.Pointer(rn)
|
||||||
|
r.used += n.Size()
|
||||||
|
|
||||||
|
for r.used > r.capacity {
|
||||||
|
rn := r.recent.prev
|
||||||
|
if rn == nil {
|
||||||
|
panic("BUG: invalid LRU used or capacity counter")
|
||||||
|
}
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if !rn.ban {
|
||||||
|
rn.remove()
|
||||||
|
rn.insert(&r.recent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Ban(n *Node) {
|
||||||
|
r.mu.Lock()
|
||||||
|
if n.CacheData == nil {
|
||||||
|
n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
|
||||||
|
} else {
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if !rn.ban {
|
||||||
|
rn.remove()
|
||||||
|
rn.ban = true
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
rn.h.Release()
|
||||||
|
rn.h = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Evict(n *Node) {
|
||||||
|
r.mu.Lock()
|
||||||
|
rn := (*lruNode)(n.CacheData)
|
||||||
|
if rn == nil || rn.ban {
|
||||||
|
r.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.CacheData = nil
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) EvictNS(ns uint64) {
|
||||||
|
var evicted []*lruNode
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
for e := r.recent.prev; e != &r.recent; {
|
||||||
|
rn := e
|
||||||
|
e = e.prev
|
||||||
|
if rn.n.NS() == ns {
|
||||||
|
rn.remove()
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
r.used -= rn.n.Size()
|
||||||
|
evicted = append(evicted, rn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for _, rn := range evicted {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) EvictAll() {
|
||||||
|
r.mu.Lock()
|
||||||
|
back := r.recent.prev
|
||||||
|
for rn := back; rn != &r.recent; rn = rn.prev {
|
||||||
|
rn.n.CacheData = nil
|
||||||
|
}
|
||||||
|
r.reset()
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
for rn := back; rn != &r.recent; rn = rn.prev {
|
||||||
|
rn.h.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lru) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLRU create a new LRU-cache.
|
||||||
|
func NewLRU(capacity int) Cacher {
|
||||||
|
r := &lru{capacity: capacity}
|
||||||
|
r.reset()
|
||||||
|
return r
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user