Migrate this project to minio micro services code

This commit is contained in:
Harshavardhana
2015-10-16 11:26:01 -07:00
parent 8c4119cbeb
commit 762b798767
349 changed files with 3704 additions and 76049 deletions
-77
View File
@@ -1,77 +0,0 @@
package logrus
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEntryWithError(t *testing.T) {
assert := assert.New(t)
defer func() {
ErrorKey = "error"
}()
err := fmt.Errorf("kaboom at layer %d", 4711)
assert.Equal(err, WithError(err).Data["error"])
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
assert.Equal(err, entry.WithError(err).Data["error"])
ErrorKey = "err"
assert.Equal(err, entry.WithError(err).Data["err"])
}
func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicln("kaboom")
}
func TestEntryPanicf(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom true", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
}
-98
View File
@@ -1,98 +0,0 @@
package logrus
import (
"fmt"
"testing"
"time"
)
// smallFields is a small size data set for benchmarking
var smallFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
// largeFields is a large size data set for benchmarking
var largeFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
"five": "six",
"seven": "eight",
"nine": "ten",
"eleven": "twelve",
"thirteen": "fourteen",
"fifteen": "sixteen",
"seventeen": "eighteen",
"nineteen": "twenty",
"a": "b",
"c": "d",
"e": "f",
"g": "h",
"i": "j",
"k": "l",
"m": "n",
"o": "p",
"q": "r",
"s": "t",
"u": "v",
"w": "x",
"y": "z",
"this": "will",
"make": "thirty",
"entries": "yeah",
}
var errorFields = Fields{
"foo": fmt.Errorf("bar"),
"baz": fmt.Errorf("qux"),
}
func BenchmarkErrorTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
}
func BenchmarkSmallTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkLargeTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
}
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
}
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
}
func BenchmarkSmallJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, smallFields)
}
func BenchmarkLargeJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, largeFields)
}
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
entry := &Entry{
Time: time.Time{},
Level: InfoLevel,
Message: "message",
Data: fields,
}
var d []byte
var err error
for i := 0; i < b.N; i++ {
d, err = formatter.Format(entry)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(d)))
}
}
-122
View File
@@ -1,122 +0,0 @@
package logrus
import (
"testing"
"github.com/stretchr/testify/assert"
)
type TestHook struct {
Fired bool
}
func (hook *TestHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *TestHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookFires(t *testing.T) {
hook := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
assert.Equal(t, hook.Fired, false)
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}
type ModifyHook struct {
}
func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale"
return nil
}
func (hook *ModifyHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookCanModifyEntry(t *testing.T) {
hook := new(ModifyHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
})
}
func TestCanFireMultipleHooks(t *testing.T) {
hook1 := new(ModifyHook)
hook2 := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook1)
log.Hooks.Add(hook2)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
assert.Equal(t, hook2.Fired, true)
})
}
type ErrorHook struct {
Fired bool
}
func (hook *ErrorHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *ErrorHook) Levels() []Level {
return []Level{
ErrorLevel,
}
}
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, false)
})
}
func TestErrorHookShouldFireOnError(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Error("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}
-120
View File
@@ -1,120 +0,0 @@
package logrus
import (
"encoding/json"
"errors"
"testing"
)
func TestErrorNotLost(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["error"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["omg"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestFieldClashWithTime(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("time", "right now!"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.time"] != "right now!" {
t.Fatal("fields.time not set to original time field")
}
if entry["time"] != "0001-01-01T00:00:00Z" {
t.Fatal("time field not set to current time, was: ", entry["time"])
}
}
func TestFieldClashWithMsg(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("msg", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.msg"] != "something" {
t.Fatal("fields.msg not set to original msg field")
}
}
func TestFieldClashWithLevel(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.level"] != "something" {
t.Fatal("fields.level not set to original level field")
}
}
func TestJSONEntryEndsWithNewline(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
if b[len(b)-1] != '\n' {
t.Fatal("Expected JSON log entry to end with a newline")
}
}
-301
View File
@@ -1,301 +0,0 @@
package logrus
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
log(logger)
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assertions(fields)
}
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
var buffer bytes.Buffer
logger := New()
logger.Out = &buffer
logger.Formatter = &TextFormatter{
DisableColors: true,
}
log(logger)
fields := make(map[string]string)
for _, kv := range strings.Split(buffer.String(), " ") {
if !strings.Contains(kv, "=") {
continue
}
kvArr := strings.Split(kv, "=")
key := strings.TrimSpace(kvArr[0])
val := kvArr[1]
if kvArr[1][0] == '"' {
var err error
val, err = strconv.Unquote(val)
assert.NoError(t, err)
}
fields[key] = val
}
assertions(fields)
}
func TestPrint(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestInfo(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestWarn(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Warn("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "warning")
})
}
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test test")
})
}
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test 10")
})
}
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test10")
})
}
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "testtest")
})
}
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
localLog := logger.WithFields(Fields{
"key1": "value1",
})
localLog.WithField("key2", "value2").Info("test")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assert.Equal(t, "value2", fields["key2"])
assert.Equal(t, "value1", fields["key1"])
buffer = bytes.Buffer{}
fields = Fields{}
localLog.Info("test")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
_, ok := fields["key2"]
assert.Equal(t, false, ok)
assert.Equal(t, "value1", fields["key1"])
}
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
})
}
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["fields.msg"], "hello")
})
}
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("time", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["fields.time"], "hello")
})
}
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("level", 1).Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["level"], "info")
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
})
}
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
LogAndAssertText(t, func(log *Logger) {
ll := log.WithField("herp", "derp")
ll.Info("hello")
ll.Info("bye")
}, func(fields map[string]string) {
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
if _, ok := fields[fieldName]; ok {
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
}
}
})
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
llog := logger.WithField("context", "eating raw fish")
llog.Info("looks delicious")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded first message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "looks delicious")
assert.Equal(t, fields["context"], "eating raw fish")
buffer.Reset()
llog.Warn("omg it is!")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded second message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "omg it is!")
assert.Equal(t, fields["context"], "eating raw fish")
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestConvertLevelToString(t *testing.T) {
assert.Equal(t, "debug", DebugLevel.String())
assert.Equal(t, "info", InfoLevel.String())
assert.Equal(t, "warning", WarnLevel.String())
assert.Equal(t, "error", ErrorLevel.String())
assert.Equal(t, "fatal", FatalLevel.String())
assert.Equal(t, "panic", PanicLevel.String())
}
func TestParseLevel(t *testing.T) {
l, err := ParseLevel("panic")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("fatal")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("error")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("warn")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("warning")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("info")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("debug")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("invalid")
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}
func TestGetSetLevelRace(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
SetLevel(InfoLevel)
} else {
GetLevel()
}
}(i)
}
wg.Wait()
}
-61
View File
@@ -1,61 +0,0 @@
package logrus
import (
"bytes"
"errors"
"testing"
"time"
)
func TestQuoting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
checkQuoting := func(q bool, value interface{}) {
b, _ := tf.Format(WithField("test", value))
idx := bytes.Index(b, ([]byte)("test="))
cont := bytes.Contains(b[idx+5:], []byte{'"'})
if cont != q {
if q {
t.Errorf("quoting expected for: %#v", value)
} else {
t.Errorf("quoting not expected for: %#v", value)
}
}
}
checkQuoting(false, "abcd")
checkQuoting(false, "v1.0")
checkQuoting(false, "1234567890")
checkQuoting(true, "/foobar")
checkQuoting(true, "x y")
checkQuoting(true, "x,y")
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
}
func TestTimestampFormat(t *testing.T) {
checkTimeStr := func(format string) {
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
customStr, _ := customFormatter.Format(WithField("test", "test"))
timeStart := bytes.Index(customStr, ([]byte)("time="))
timeEnd := bytes.Index(customStr, ([]byte)("level="))
timeStr := customStr[timeStart+5 : timeEnd-1]
if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
timeStr = timeStr[1 : len(timeStr)-1]
}
if format == "" {
format = time.RFC3339
}
_, e := time.Parse(format, (string)(timeStr))
if e != nil {
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
}
}
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
checkTimeStr("Mon Jan _2 15:04:05 2006")
checkTimeStr("")
}
// TODO add tests for sorting etc., this requires a parser for the text
// formatter output.
-219
View File
@@ -1,219 +0,0 @@
package humanize
import (
"math/big"
"testing"
)
func TestBigByteParsing(t *testing.T) {
tests := []struct {
in string
exp uint64
}{
{"42", 42},
{"42MB", 42000000},
{"42MiB", 44040192},
{"42mb", 42000000},
{"42mib", 44040192},
{"42MIB", 44040192},
{"42 MB", 42000000},
{"42 MiB", 44040192},
{"42 mb", 42000000},
{"42 mib", 44040192},
{"42 MIB", 44040192},
{"42.5MB", 42500000},
{"42.5MiB", 44564480},
{"42.5 MB", 42500000},
{"42.5 MiB", 44564480},
// No need to say B
{"42M", 42000000},
{"42Mi", 44040192},
{"42m", 42000000},
{"42mi", 44040192},
{"42MI", 44040192},
{"42 M", 42000000},
{"42 Mi", 44040192},
{"42 m", 42000000},
{"42 mi", 44040192},
{"42 MI", 44040192},
{"42.5M", 42500000},
{"42.5Mi", 44564480},
{"42.5 M", 42500000},
{"42.5 Mi", 44564480},
// Large testing, breaks when too much larger than
// this.
{"12.5 EB", uint64(12.5 * float64(EByte))},
{"12.5 E", uint64(12.5 * float64(EByte))},
{"12.5 EiB", uint64(12.5 * float64(EiByte))},
}
for _, p := range tests {
got, err := ParseBigBytes(p.in)
if err != nil {
t.Errorf("Couldn't parse %v: %v", p.in, err)
} else {
if got.Uint64() != p.exp {
t.Errorf("Expected %v for %v, got %v",
p.exp, p.in, got)
}
}
}
}
func TestBigByteErrors(t *testing.T) {
got, err := ParseBigBytes("84 JB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
got, err = ParseBigBytes("")
if err == nil {
t.Errorf("Expected error parsing nothing")
}
}
func bbyte(in uint64) string {
return BigBytes((&big.Int{}).SetUint64(in))
}
func bibyte(in uint64) string {
return BigIBytes((&big.Int{}).SetUint64(in))
}
func TestBigBytes(t *testing.T) {
testList{
{"bytes(0)", bbyte(0), "0B"},
{"bytes(1)", bbyte(1), "1B"},
{"bytes(803)", bbyte(803), "803B"},
{"bytes(999)", bbyte(999), "999B"},
{"bytes(1024)", bbyte(1024), "1.0kB"},
{"bytes(1MB - 1)", bbyte(MByte - Byte), "1000kB"},
{"bytes(1MB)", bbyte(1024 * 1024), "1.0MB"},
{"bytes(1GB - 1K)", bbyte(GByte - KByte), "1000MB"},
{"bytes(1GB)", bbyte(GByte), "1.0GB"},
{"bytes(1TB - 1M)", bbyte(TByte - MByte), "1000GB"},
{"bytes(1TB)", bbyte(TByte), "1.0TB"},
{"bytes(1PB - 1T)", bbyte(PByte - TByte), "999TB"},
{"bytes(1PB)", bbyte(PByte), "1.0PB"},
{"bytes(1PB - 1T)", bbyte(EByte - PByte), "999PB"},
{"bytes(1EB)", bbyte(EByte), "1.0EB"},
// Overflows.
// {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"},
{"bytes(0)", bibyte(0), "0B"},
{"bytes(1)", bibyte(1), "1B"},
{"bytes(803)", bibyte(803), "803B"},
{"bytes(1023)", bibyte(1023), "1023B"},
{"bytes(1024)", bibyte(1024), "1.0KiB"},
{"bytes(1MB - 1)", bibyte(MiByte - IByte), "1024KiB"},
{"bytes(1MB)", bibyte(1024 * 1024), "1.0MiB"},
{"bytes(1GB - 1K)", bibyte(GiByte - KiByte), "1024MiB"},
{"bytes(1GB)", bibyte(GiByte), "1.0GiB"},
{"bytes(1TB - 1M)", bibyte(TiByte - MiByte), "1024GiB"},
{"bytes(1TB)", bibyte(TiByte), "1.0TiB"},
{"bytes(1PB - 1T)", bibyte(PiByte - TiByte), "1023TiB"},
{"bytes(1PB)", bibyte(PiByte), "1.0PiB"},
{"bytes(1PB - 1T)", bibyte(EiByte - PiByte), "1023PiB"},
{"bytes(1EiB)", bibyte(EiByte), "1.0EiB"},
// Overflows.
// {"bytes(1EB - 1P)", bibyte((KIByte*EIByte)-PiByte), "1023EB"},
{"bytes(5.5GiB)", bibyte(5.5 * GiByte), "5.5GiB"},
{"bytes(5.5GB)", bbyte(5.5 * GByte), "5.5GB"},
}.validate(t)
}
func TestVeryBigBytes(t *testing.T) {
b, _ := (&big.Int{}).SetString("15347691069326346944512", 10)
s := BigBytes(b)
if s != "15ZB" {
t.Errorf("Expected 15ZB, got %v", s)
}
s = BigIBytes(b)
if s != "13ZiB" {
t.Errorf("Expected 13ZiB, got %v", s)
}
b, _ = (&big.Int{}).SetString("15716035654990179271180288", 10)
s = BigBytes(b)
if s != "16YB" {
t.Errorf("Expected 16YB, got %v", s)
}
s = BigIBytes(b)
if s != "13YiB" {
t.Errorf("Expected 13YiB, got %v", s)
}
}
func TestVeryVeryBigBytes(t *testing.T) {
b, _ := (&big.Int{}).SetString("16093220510709943573688614912", 10)
s := BigBytes(b)
if s != "16093YB" {
t.Errorf("Expected 16093YB, got %v", s)
}
s = BigIBytes(b)
if s != "13312YiB" {
t.Errorf("Expected 13312YiB, got %v", s)
}
}
func TestParseVeryBig(t *testing.T) {
tests := []struct {
in string
out string
}{
{"16ZB", "16000000000000000000000"},
{"16ZiB", "18889465931478580854784"},
{"16.5ZB", "16500000000000000000000"},
{"16.5ZiB", "19479761741837286506496"},
{"16Z", "16000000000000000000000"},
{"16Zi", "18889465931478580854784"},
{"16.5Z", "16500000000000000000000"},
{"16.5Zi", "19479761741837286506496"},
{"16YB", "16000000000000000000000000"},
{"16YiB", "19342813113834066795298816"},
{"16.5YB", "16500000000000000000000000"},
{"16.5YiB", "19947276023641381382651904"},
{"16Y", "16000000000000000000000000"},
{"16Yi", "19342813113834066795298816"},
{"16.5Y", "16500000000000000000000000"},
{"16.5Yi", "19947276023641381382651904"},
}
for _, test := range tests {
x, err := ParseBigBytes(test.in)
if err != nil {
t.Errorf("Error parsing %q: %v", test.in, err)
continue
}
if x.String() != test.out {
t.Errorf("Expected %q for %q, got %v", test.out, test.in, x)
}
}
}
func BenchmarkParseBigBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseBigBytes("16.5Z")
}
}
func BenchmarkBigBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
bibyte(16.5 * GByte)
}
}
-144
View File
@@ -1,144 +0,0 @@
package humanize
import (
"testing"
)
func TestByteParsing(t *testing.T) {
tests := []struct {
in string
exp uint64
}{
{"42", 42},
{"42MB", 42000000},
{"42MiB", 44040192},
{"42mb", 42000000},
{"42mib", 44040192},
{"42MIB", 44040192},
{"42 MB", 42000000},
{"42 MiB", 44040192},
{"42 mb", 42000000},
{"42 mib", 44040192},
{"42 MIB", 44040192},
{"42.5MB", 42500000},
{"42.5MiB", 44564480},
{"42.5 MB", 42500000},
{"42.5 MiB", 44564480},
// No need to say B
{"42M", 42000000},
{"42Mi", 44040192},
{"42m", 42000000},
{"42mi", 44040192},
{"42MI", 44040192},
{"42 M", 42000000},
{"42 Mi", 44040192},
{"42 m", 42000000},
{"42 mi", 44040192},
{"42 MI", 44040192},
{"42.5M", 42500000},
{"42.5Mi", 44564480},
{"42.5 M", 42500000},
{"42.5 Mi", 44564480},
// Large testing, breaks when too much larger than
// this.
{"12.5 EB", uint64(12.5 * float64(EByte))},
{"12.5 E", uint64(12.5 * float64(EByte))},
{"12.5 EiB", uint64(12.5 * float64(EiByte))},
}
for _, p := range tests {
got, err := ParseBytes(p.in)
if err != nil {
t.Errorf("Couldn't parse %v: %v", p.in, err)
}
if got != p.exp {
t.Errorf("Expected %v for %v, got %v",
p.exp, p.in, got)
}
}
}
func TestByteErrors(t *testing.T) {
got, err := ParseBytes("84 JB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
got, err = ParseBytes("")
if err == nil {
t.Errorf("Expected error parsing nothing")
}
got, err = ParseBytes("16 EiB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
}
func TestBytes(t *testing.T) {
testList{
{"bytes(0)", Bytes(0), "0B"},
{"bytes(1)", Bytes(1), "1B"},
{"bytes(803)", Bytes(803), "803B"},
{"bytes(999)", Bytes(999), "999B"},
{"bytes(1024)", Bytes(1024), "1.0kB"},
{"bytes(9999)", Bytes(9999), "10kB"},
{"bytes(1MB - 1)", Bytes(MByte - Byte), "1000kB"},
{"bytes(1MB)", Bytes(1024 * 1024), "1.0MB"},
{"bytes(1GB - 1K)", Bytes(GByte - KByte), "1000MB"},
{"bytes(1GB)", Bytes(GByte), "1.0GB"},
{"bytes(1TB - 1M)", Bytes(TByte - MByte), "1000GB"},
{"bytes(10MB)", Bytes(9999 * 1000), "10MB"},
{"bytes(1TB)", Bytes(TByte), "1.0TB"},
{"bytes(1PB - 1T)", Bytes(PByte - TByte), "999TB"},
{"bytes(1PB)", Bytes(PByte), "1.0PB"},
{"bytes(1PB - 1T)", Bytes(EByte - PByte), "999PB"},
{"bytes(1EB)", Bytes(EByte), "1.0EB"},
// Overflows.
// {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"},
{"bytes(0)", IBytes(0), "0B"},
{"bytes(1)", IBytes(1), "1B"},
{"bytes(803)", IBytes(803), "803B"},
{"bytes(1023)", IBytes(1023), "1023B"},
{"bytes(1024)", IBytes(1024), "1.0KiB"},
{"bytes(1MB - 1)", IBytes(MiByte - IByte), "1024KiB"},
{"bytes(1MB)", IBytes(1024 * 1024), "1.0MiB"},
{"bytes(1GB - 1K)", IBytes(GiByte - KiByte), "1024MiB"},
{"bytes(1GB)", IBytes(GiByte), "1.0GiB"},
{"bytes(1TB - 1M)", IBytes(TiByte - MiByte), "1024GiB"},
{"bytes(1TB)", IBytes(TiByte), "1.0TiB"},
{"bytes(1PB - 1T)", IBytes(PiByte - TiByte), "1023TiB"},
{"bytes(1PB)", IBytes(PiByte), "1.0PiB"},
{"bytes(1PB - 1T)", IBytes(EiByte - PiByte), "1023PiB"},
{"bytes(1EiB)", IBytes(EiByte), "1.0EiB"},
// Overflows.
// {"bytes(1EB - 1P)", IBytes((KIByte*EIByte)-PiByte), "1023EB"},
{"bytes(5.5GiB)", IBytes(5.5 * GiByte), "5.5GiB"},
{"bytes(5.5GB)", Bytes(5.5 * GByte), "5.5GB"},
}.validate(t)
}
func BenchmarkParseBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseBytes("16.5GB")
}
}
func BenchmarkBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(16.5 * GByte)
}
}
+2 -2
View File
@@ -33,7 +33,7 @@ func Comma(v int64) string {
j--
}
parts[j] = strconv.Itoa(int(v))
return sign + strings.Join(parts[j:len(parts)], ",")
return sign + strings.Join(parts[j:], ",")
}
// Commaf produces a string form of the given number in base 10 with
@@ -97,5 +97,5 @@ func BigComma(b *big.Int) string {
j--
}
parts[j] = strconv.Itoa(int(b.Int64()))
return sign + strings.Join(parts[j:len(parts)], ",")
return sign + strings.Join(parts[j:], ",")
}
-134
View File
@@ -1,134 +0,0 @@
package humanize
import (
"math"
"math/big"
"testing"
)
func TestCommas(t *testing.T) {
testList{
{"0", Comma(0), "0"},
{"10", Comma(10), "10"},
{"100", Comma(100), "100"},
{"1,000", Comma(1000), "1,000"},
{"10,000", Comma(10000), "10,000"},
{"100,000", Comma(100000), "100,000"},
{"10,000,000", Comma(10000000), "10,000,000"},
{"10,100,000", Comma(10100000), "10,100,000"},
{"10,010,000", Comma(10010000), "10,010,000"},
{"10,001,000", Comma(10001000), "10,001,000"},
{"123,456,789", Comma(123456789), "123,456,789"},
{"maxint", Comma(9.223372e+18), "9,223,372,000,000,000,000"},
{"minint", Comma(-9.223372e+18), "-9,223,372,000,000,000,000"},
{"-123,456,789", Comma(-123456789), "-123,456,789"},
{"-10,100,000", Comma(-10100000), "-10,100,000"},
{"-10,010,000", Comma(-10010000), "-10,010,000"},
{"-10,001,000", Comma(-10001000), "-10,001,000"},
{"-10,000,000", Comma(-10000000), "-10,000,000"},
{"-100,000", Comma(-100000), "-100,000"},
{"-10,000", Comma(-10000), "-10,000"},
{"-1,000", Comma(-1000), "-1,000"},
{"-100", Comma(-100), "-100"},
{"-10", Comma(-10), "-10"},
}.validate(t)
}
func TestCommafs(t *testing.T) {
testList{
{"0", Commaf(0), "0"},
{"10.11", Commaf(10.11), "10.11"},
{"100", Commaf(100), "100"},
{"1,000", Commaf(1000), "1,000"},
{"10,000", Commaf(10000), "10,000"},
{"100,000", Commaf(100000), "100,000"},
{"834,142.32", Commaf(834142.32), "834,142.32"},
{"10,000,000", Commaf(10000000), "10,000,000"},
{"10,100,000", Commaf(10100000), "10,100,000"},
{"10,010,000", Commaf(10010000), "10,010,000"},
{"10,001,000", Commaf(10001000), "10,001,000"},
{"123,456,789", Commaf(123456789), "123,456,789"},
{"maxf64", Commaf(math.MaxFloat64), "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000"},
{"minf64", Commaf(math.SmallestNonzeroFloat64), "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"},
{"-123,456,789", Commaf(-123456789), "-123,456,789"},
{"-10,100,000", Commaf(-10100000), "-10,100,000"},
{"-10,010,000", Commaf(-10010000), "-10,010,000"},
{"-10,001,000", Commaf(-10001000), "-10,001,000"},
{"-10,000,000", Commaf(-10000000), "-10,000,000"},
{"-100,000", Commaf(-100000), "-100,000"},
{"-10,000", Commaf(-10000), "-10,000"},
{"-1,000", Commaf(-1000), "-1,000"},
{"-100.11", Commaf(-100.11), "-100.11"},
{"-10", Commaf(-10), "-10"},
}.validate(t)
}
func BenchmarkCommas(b *testing.B) {
for i := 0; i < b.N; i++ {
Comma(1234567890)
}
}
func BenchmarkCommaf(b *testing.B) {
for i := 0; i < b.N; i++ {
Commaf(1234567890.83584)
}
}
func BenchmarkBigCommas(b *testing.B) {
for i := 0; i < b.N; i++ {
BigComma(big.NewInt(1234567890))
}
}
func bigComma(i int64) string {
return BigComma(big.NewInt(i))
}
func TestBigCommas(t *testing.T) {
testList{
{"0", bigComma(0), "0"},
{"10", bigComma(10), "10"},
{"100", bigComma(100), "100"},
{"1,000", bigComma(1000), "1,000"},
{"10,000", bigComma(10000), "10,000"},
{"100,000", bigComma(100000), "100,000"},
{"10,000,000", bigComma(10000000), "10,000,000"},
{"10,100,000", bigComma(10100000), "10,100,000"},
{"10,010,000", bigComma(10010000), "10,010,000"},
{"10,001,000", bigComma(10001000), "10,001,000"},
{"123,456,789", bigComma(123456789), "123,456,789"},
{"maxint", bigComma(9.223372e+18), "9,223,372,000,000,000,000"},
{"minint", bigComma(-9.223372e+18), "-9,223,372,000,000,000,000"},
{"-123,456,789", bigComma(-123456789), "-123,456,789"},
{"-10,100,000", bigComma(-10100000), "-10,100,000"},
{"-10,010,000", bigComma(-10010000), "-10,010,000"},
{"-10,001,000", bigComma(-10001000), "-10,001,000"},
{"-10,000,000", bigComma(-10000000), "-10,000,000"},
{"-100,000", bigComma(-100000), "-100,000"},
{"-10,000", bigComma(-10000), "-10,000"},
{"-1,000", bigComma(-1000), "-1,000"},
{"-100", bigComma(-100), "-100"},
{"-10", bigComma(-10), "-10"},
}.validate(t)
}
func TestVeryBigCommas(t *testing.T) {
tests := []struct{ in, exp string }{
{
"84889279597249724975972597249849757294578485",
"84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
},
{
"-84889279597249724975972597249849757294578485",
"-84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
},
}
for _, test := range tests {
n, _ := (&big.Int{}).SetString(test.in, 10)
got := BigComma(n)
if test.exp != got {
t.Errorf("Expected %q, got %q", test.exp, got)
}
}
}
-18
View File
@@ -1,18 +0,0 @@
package humanize
import (
"testing"
)
type testList []struct {
name, got, exp string
}
func (tl testList) validate(t *testing.T) {
for _, test := range tl {
if test.got != test.exp {
t.Errorf("On %v, expected '%v', but got '%v'",
test.name, test.exp, test.got)
}
}
}
-55
View File
@@ -1,55 +0,0 @@
package humanize
import (
"fmt"
"regexp"
"strconv"
"testing"
)
func TestFtoa(t *testing.T) {
testList{
{"200", Ftoa(200), "200"},
{"2", Ftoa(2), "2"},
{"2.2", Ftoa(2.2), "2.2"},
{"2.02", Ftoa(2.02), "2.02"},
{"200.02", Ftoa(200.02), "200.02"},
}.validate(t)
}
func BenchmarkFtoaRegexTrailing(b *testing.B) {
trailingZerosRegex := regexp.MustCompile(`\.?0+$`)
b.ResetTimer()
for i := 0; i < b.N; i++ {
trailingZerosRegex.ReplaceAllString("2.00000", "")
trailingZerosRegex.ReplaceAllString("2.0000", "")
trailingZerosRegex.ReplaceAllString("2.000", "")
trailingZerosRegex.ReplaceAllString("2.00", "")
trailingZerosRegex.ReplaceAllString("2.0", "")
trailingZerosRegex.ReplaceAllString("2", "")
}
}
func BenchmarkFtoaFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
stripTrailingZeros("2.00000")
stripTrailingZeros("2.0000")
stripTrailingZeros("2.000")
stripTrailingZeros("2.00")
stripTrailingZeros("2.0")
stripTrailingZeros("2")
}
}
func BenchmarkFmtF(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("%f", 2.03584)
}
}
func BenchmarkStrconvF(b *testing.B) {
for i := 0; i < b.N; i++ {
strconv.FormatFloat(2.03584, 'f', 6, 64)
}
}
-78
View File
@@ -1,78 +0,0 @@
package humanize
import (
"math"
"testing"
)
type TestStruct struct {
name string
format string
num float64
formatted string
}
func TestFormatFloat(t *testing.T) {
tests := []TestStruct{
{"default", "", 12345.6789, "12,345.68"},
{"#", "#", 12345.6789, "12345.678900000"},
{"#.", "#.", 12345.6789, "12346"},
{"#,#", "#,#", 12345.6789, "12345,7"},
{"#,##", "#,##", 12345.6789, "12345,68"},
{"#,###", "#,###", 12345.6789, "12345,679"},
{"#,###.", "#,###.", 12345.6789, "12,346"},
{"#,###.##", "#,###.##", 12345.6789, "12,345.68"},
{"#,###.###", "#,###.###", 12345.6789, "12,345.679"},
{"#,###.####", "#,###.####", 12345.6789, "12,345.6789"},
{"#.###,######", "#.###,######", 12345.6789, "12.345,678900"},
{"#\u202f###,##", "#\u202f###,##", 12345.6789, "12345,68"},
// special cases
{"NaN", "#", math.NaN(), "NaN"},
{"+Inf", "#", math.Inf(1), "Infinity"},
{"-Inf", "#", math.Inf(-1), "-Infinity"},
{"signStr <= -0.000000001", "", -0.000000002, "-0.00"},
{"signStr = 0", "", 0, "0.00"},
{"Format directive must start with +", "+000", 12345.6789, "+12345.678900000"},
}
for _, test := range tests {
got := FormatFloat(test.format, test.num)
if got != test.formatted {
t.Errorf("On %v (%v, %v), got %v, wanted %v",
test.name, test.format, test.num, got, test.formatted)
}
}
// Test a single integer
got := FormatInteger("#", 12345)
if got != "12345.000000000" {
t.Errorf("On %v (%v, %v), got %v, wanted %v",
"integerTest", "#", 12345, got, "12345.000000000")
}
// Test the things that could panic
panictests := []TestStruct{
{"RenderFloat(): invalid positive sign directive", "-", 12345.6789, "12,345.68"},
{"RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers", "0.01", 12345.6789, "12,345.68"},
}
for _, test := range panictests {
didPanic := false
var message interface{}
func() {
defer func() {
if message = recover(); message != nil {
didPanic = true
}
}()
// call the target function
_ = FormatFloat(test.format, test.num)
}()
if didPanic != true {
t.Errorf("On %v, should have panic and did not.",
test.name)
}
}
}
-22
View File
@@ -1,22 +0,0 @@
package humanize
import (
"testing"
)
func TestOrdinals(t *testing.T) {
testList{
{"0", Ordinal(0), "0th"},
{"1", Ordinal(1), "1st"},
{"2", Ordinal(2), "2nd"},
{"3", Ordinal(3), "3rd"},
{"4", Ordinal(4), "4th"},
{"10", Ordinal(10), "10th"},
{"11", Ordinal(11), "11th"},
{"12", Ordinal(12), "12th"},
{"13", Ordinal(13), "13th"},
{"101", Ordinal(101), "101st"},
{"102", Ordinal(102), "102nd"},
{"103", Ordinal(103), "103rd"},
}.validate(t)
}
-98
View File
@@ -1,98 +0,0 @@
package humanize
import (
"math"
"testing"
)
func TestSI(t *testing.T) {
tests := []struct {
name string
num float64
formatted string
}{
{"e-24", 1e-24, "1yF"},
{"e-21", 1e-21, "1zF"},
{"e-18", 1e-18, "1aF"},
{"e-15", 1e-15, "1fF"},
{"e-12", 1e-12, "1pF"},
{"e-12", 2.2345e-12, "2.2345pF"},
{"e-12", 2.23e-12, "2.23pF"},
{"e-11", 2.23e-11, "22.3pF"},
{"e-10", 2.2e-10, "220pF"},
{"e-9", 2.2e-9, "2.2nF"},
{"e-8", 2.2e-8, "22nF"},
{"e-7", 2.2e-7, "220nF"},
{"e-6", 2.2e-6, "2.2µF"},
{"e-6", 1e-6, "1µF"},
{"e-5", 2.2e-5, "22µF"},
{"e-4", 2.2e-4, "220µF"},
{"e-3", 2.2e-3, "2.2mF"},
{"e-2", 2.2e-2, "22mF"},
{"e-1", 2.2e-1, "220mF"},
{"e+0", 2.2e-0, "2.2F"},
{"e+0", 2.2, "2.2F"},
{"e+1", 2.2e+1, "22F"},
{"0", 0, "0F"},
{"e+1", 22, "22F"},
{"e+2", 2.2e+2, "220F"},
{"e+2", 220, "220F"},
{"e+3", 2.2e+3, "2.2kF"},
{"e+3", 2200, "2.2kF"},
{"e+4", 2.2e+4, "22kF"},
{"e+4", 22000, "22kF"},
{"e+5", 2.2e+5, "220kF"},
{"e+6", 2.2e+6, "2.2MF"},
{"e+6", 1e+6, "1MF"},
{"e+7", 2.2e+7, "22MF"},
{"e+8", 2.2e+8, "220MF"},
{"e+9", 2.2e+9, "2.2GF"},
{"e+10", 2.2e+10, "22GF"},
{"e+11", 2.2e+11, "220GF"},
{"e+12", 2.2e+12, "2.2TF"},
{"e+15", 2.2e+15, "2.2PF"},
{"e+18", 2.2e+18, "2.2EF"},
{"e+21", 2.2e+21, "2.2ZF"},
{"e+24", 2.2e+24, "2.2YF"},
// special case
{"1F", 1000 * 1000, "1MF"},
{"1F", 1e6, "1MF"},
}
for _, test := range tests {
got := SI(test.num, "F")
if got != test.formatted {
t.Errorf("On %v (%v), got %v, wanted %v",
test.name, test.num, got, test.formatted)
}
gotf, gotu, err := ParseSI(test.formatted)
if err != nil {
t.Errorf("Error parsing %v (%v): %v", test.name, test.formatted, err)
continue
}
if math.Abs(1-(gotf/test.num)) > 0.01 {
t.Errorf("On %v (%v), got %v, wanted %v (±%v)",
test.name, test.formatted, gotf, test.num,
math.Abs(1-(gotf/test.num)))
}
if gotu != "F" {
t.Errorf("On %v (%v), expected unit F, got %v",
test.name, test.formatted, gotu)
}
}
// Parse error
gotf, gotu, err := ParseSI("x1.21JW") // 1.21 jigga whats
if err == nil {
t.Errorf("Expected error on x1.21JW, got %v %v", gotf, gotu)
}
}
func BenchmarkParseSI(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseSI("2.2346ZB")
}
}
-71
View File
@@ -1,71 +0,0 @@
package humanize
import (
"math"
"testing"
"time"
)
func TestPast(t *testing.T) {
now := time.Now().Unix()
testList{
{"now", Time(time.Unix(now, 0)), "now"},
{"1 second ago", Time(time.Unix(now-1, 0)), "1 second ago"},
{"12 seconds ago", Time(time.Unix(now-12, 0)), "12 seconds ago"},
{"30 seconds ago", Time(time.Unix(now-30, 0)), "30 seconds ago"},
{"45 seconds ago", Time(time.Unix(now-45, 0)), "45 seconds ago"},
{"1 minute ago", Time(time.Unix(now-63, 0)), "1 minute ago"},
{"15 minutes ago", Time(time.Unix(now-15*Minute, 0)), "15 minutes ago"},
{"1 hour ago", Time(time.Unix(now-63*Minute, 0)), "1 hour ago"},
{"2 hours ago", Time(time.Unix(now-2*Hour, 0)), "2 hours ago"},
{"21 hours ago", Time(time.Unix(now-21*Hour, 0)), "21 hours ago"},
{"1 day ago", Time(time.Unix(now-26*Hour, 0)), "1 day ago"},
{"2 days ago", Time(time.Unix(now-49*Hour, 0)), "2 days ago"},
{"3 days ago", Time(time.Unix(now-3*Day, 0)), "3 days ago"},
{"1 week ago (1)", Time(time.Unix(now-7*Day, 0)), "1 week ago"},
{"1 week ago (2)", Time(time.Unix(now-12*Day, 0)), "1 week ago"},
{"2 weeks ago", Time(time.Unix(now-15*Day, 0)), "2 weeks ago"},
{"1 month ago", Time(time.Unix(now-39*Day, 0)), "1 month ago"},
{"3 months ago", Time(time.Unix(now-99*Day, 0)), "3 months ago"},
{"1 year ago (1)", Time(time.Unix(now-365*Day, 0)), "1 year ago"},
{"1 year ago (1)", Time(time.Unix(now-400*Day, 0)), "1 year ago"},
{"2 years ago (1)", Time(time.Unix(now-548*Day, 0)), "2 years ago"},
{"2 years ago (2)", Time(time.Unix(now-725*Day, 0)), "2 years ago"},
{"2 years ago (3)", Time(time.Unix(now-800*Day, 0)), "2 years ago"},
{"3 years ago", Time(time.Unix(now-3*Year, 0)), "3 years ago"},
{"long ago", Time(time.Unix(now-LongTime, 0)), "a long while ago"},
}.validate(t)
}
func TestFuture(t *testing.T) {
now := time.Now().Unix()
testList{
{"now", Time(time.Unix(now, 0)), "now"},
{"1 second from now", Time(time.Unix(now+1, 0)), "1 second from now"},
{"12 seconds from now", Time(time.Unix(now+12, 0)), "12 seconds from now"},
{"30 seconds from now", Time(time.Unix(now+30, 0)), "30 seconds from now"},
{"45 seconds from now", Time(time.Unix(now+45, 0)), "45 seconds from now"},
{"15 minutes from now", Time(time.Unix(now+15*Minute, 0)), "15 minutes from now"},
{"2 hours from now", Time(time.Unix(now+2*Hour, 0)), "2 hours from now"},
{"21 hours from now", Time(time.Unix(now+21*Hour, 0)), "21 hours from now"},
{"1 day from now", Time(time.Unix(now+26*Hour, 0)), "1 day from now"},
{"2 days from now", Time(time.Unix(now+49*Hour, 0)), "2 days from now"},
{"3 days from now", Time(time.Unix(now+3*Day, 0)), "3 days from now"},
{"1 week from now (1)", Time(time.Unix(now+7*Day, 0)), "1 week from now"},
{"1 week from now (2)", Time(time.Unix(now+12*Day, 0)), "1 week from now"},
{"2 weeks from now", Time(time.Unix(now+15*Day, 0)), "2 weeks from now"},
{"1 month from now", Time(time.Unix(now+30*Day, 0)), "1 month from now"},
{"1 year from now", Time(time.Unix(now+365*Day, 0)), "1 year from now"},
{"2 years from now", Time(time.Unix(now+2*Year, 0)), "2 years from now"},
{"a while from now", Time(time.Unix(now+LongTime, 0)), "a long while from now"},
}.validate(t)
}
func TestRange(t *testing.T) {
start := time.Time{}
end := time.Unix(math.MaxInt64, math.MaxInt64)
x := RelTime(start, end, "ago", "from now")
if x != "a long while from now" {
t.Errorf("Expected a long while from now, got %q", x)
}
}
-23
View File
@@ -1,23 +0,0 @@
Copyright (c) 2014, Elazar Leibovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-46
View File
@@ -1,46 +0,0 @@
# go-bindata-assetfs
Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`.
[GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs)
### Installation
Install with
$ go get github.com/jteeuwen/go-bindata/...
$ go get github.com/elazarl/go-bindata-assetfs/...
### Creating embedded data
Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage,
instead of running `go-bindata` run `go-bindata-assetfs`.
The tool will create a `bindata_assetfs.go` file, which contains the embedded data.
A typical use case is
$ go-bindata-assetfs data/...
### Using assetFS in your code
The generated file provides an `assetFS()` function that returns a `http.Filesystem`
wrapping the embedded files. What you usually want to do is:
http.Handle("/", http.FileServer(assetFS()))
This would run an HTTP server serving the embedded files.
## Without running binary tool
You can always just run the `go-bindata` tool, and then
use
import "github.com/elazarl/go-bindata-assetfs"
...
http.Handle("/",
http.FileServer(
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
to serve files embedded from the `data` directory.
-147
View File
@@ -1,147 +0,0 @@
package assetfs
import (
"bytes"
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"time"
)
var (
fileTimestamp = time.Now()
)
// FakeFile implements os.FileInfo interface for a given path and size
type FakeFile struct {
// Path is the path of this file
Path string
// Dir marks of the path is a directory
Dir bool
// Len is the length of the fake file, zero if it is a directory
Len int64
}
func (f *FakeFile) Name() string {
_, name := filepath.Split(f.Path)
return name
}
func (f *FakeFile) Mode() os.FileMode {
mode := os.FileMode(0644)
if f.Dir {
return mode | os.ModeDir
}
return mode
}
func (f *FakeFile) ModTime() time.Time {
return fileTimestamp
}
func (f *FakeFile) Size() int64 {
return f.Len
}
func (f *FakeFile) IsDir() bool {
return f.Mode().IsDir()
}
func (f *FakeFile) Sys() interface{} {
return nil
}
// AssetFile implements http.File interface for a no-directory file with content
type AssetFile struct {
*bytes.Reader
io.Closer
FakeFile
}
func NewAssetFile(name string, content []byte) *AssetFile {
return &AssetFile{
bytes.NewReader(content),
ioutil.NopCloser(nil),
FakeFile{name, false, int64(len(content))}}
}
func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, errors.New("not a directory")
}
func (f *AssetFile) Size() int64 {
return f.FakeFile.Size()
}
func (f *AssetFile) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetDirectory implements http.File interface for a directory
type AssetDirectory struct {
AssetFile
ChildrenRead int
Children []os.FileInfo
}
func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
fileinfos := make([]os.FileInfo, 0, len(children))
for _, child := range children {
_, err := fs.AssetDir(filepath.Join(name, child))
fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0})
}
return &AssetDirectory{
AssetFile{
bytes.NewReader(nil),
ioutil.NopCloser(nil),
FakeFile{name, true, 0},
},
0,
fileinfos}
}
func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
if count <= 0 {
return f.Children, nil
}
if f.ChildrenRead+count > len(f.Children) {
count = len(f.Children) - f.ChildrenRead
}
rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
f.ChildrenRead += count
return rv, nil
}
func (f *AssetDirectory) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetFS implements http.FileSystem, allowing
// embedded files to be served from net/http package.
type AssetFS struct {
// Asset should return content of file in path if exists
Asset func(path string) ([]byte, error)
// AssetDir should return list of files in the path
AssetDir func(path string) ([]string, error)
// Prefix would be prepended to http requests
Prefix string
}
func (fs *AssetFS) Open(name string) (http.File, error) {
name = path.Join(fs.Prefix, name)
if len(name) > 0 && name[0] == '/' {
name = name[1:]
}
if b, err := fs.Asset(name); err == nil {
return NewAssetFile(name, b), nil
}
if children, err := fs.AssetDir(name); err == nil {
return NewAssetDirectory(name, children, fs), nil
} else {
return nil, err
}
}
-13
View File
@@ -1,13 +0,0 @@
// assetfs allows packages to serve static content embedded
// with the go-bindata tool with the standard net/http package.
//
// See https://github.com/jteeuwen/go-bindata for more information
// about embedding binary data with go-bindata.
//
// Usage example, after running
// $ go-bindata data/...
// use:
// http.Handle("/",
// http.FileServer(
// &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
package assetfs
-536
View File
@@ -1,536 +0,0 @@
package clock_test
import (
"fmt"
"os"
"runtime"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/facebookgo/clock"
)
// Ensure that the clock's After channel sends at the correct time.
func TestClock_After(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
<-clock.New().After(20 * time.Millisecond)
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's AfterFunc executes at the correct time.
func TestClock_AfterFunc(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
var wg sync.WaitGroup
wg.Add(1)
clock.New().AfterFunc(20*time.Millisecond, func() {
wg.Done()
})
wg.Wait()
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's time matches the standary library.
func TestClock_Now(t *testing.T) {
a := time.Now().Round(time.Second)
b := clock.New().Now().Round(time.Second)
if !a.Equal(b) {
t.Errorf("not equal: %s != %s", a, b)
}
}
// Ensure that the clock sleeps for the appropriate amount of time.
func TestClock_Sleep(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
clock.New().Sleep(20 * time.Millisecond)
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock ticks correctly.
func TestClock_Tick(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(50 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
c := clock.New().Tick(20 * time.Millisecond)
<-c
<-c
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's ticker ticks correctly.
func TestClock_Ticker(t *testing.T) {
var ok bool
go func() {
time.Sleep(100 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(200 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
ticker := clock.New().Ticker(50 * time.Millisecond)
<-ticker.C
<-ticker.C
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's ticker can stop correctly.
func TestClock_Ticker_Stp(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
gosched()
ticker := clock.New().Ticker(20 * time.Millisecond)
<-ticker.C
ticker.Stop()
select {
case <-ticker.C:
t.Fatal("unexpected send")
case <-time.After(30 * time.Millisecond):
}
}
// Ensure that the clock's timer waits correctly.
func TestClock_Timer(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
go func() {
time.Sleep(30 * time.Millisecond)
t.Fatal("too late")
}()
gosched()
timer := clock.New().Timer(20 * time.Millisecond)
<-timer.C
if !ok {
t.Fatal("too early")
}
}
// Ensure that the clock's timer can be stopped.
func TestClock_Timer_Stop(t *testing.T) {
var ok bool
go func() {
time.Sleep(10 * time.Millisecond)
ok = true
}()
timer := clock.New().Timer(20 * time.Millisecond)
timer.Stop()
select {
case <-timer.C:
t.Fatal("unexpected send")
case <-time.After(30 * time.Millisecond):
}
}
// Ensure that the mock's After channel sends at the correct time.
func TestMock_After(t *testing.T) {
var ok int32
clock := clock.NewMock()
// Create a channel to execute after 10 mock seconds.
ch := clock.After(10 * time.Second)
go func(ch <-chan time.Time) {
<-ch
atomic.StoreInt32(&ok, 1)
}(ch)
// Move clock forward to just before the time.
clock.Add(9 * time.Second)
if atomic.LoadInt32(&ok) == 1 {
t.Fatal("too early")
}
// Move clock forward to the after channel's time.
clock.Add(1 * time.Second)
if atomic.LoadInt32(&ok) == 0 {
t.Fatal("too late")
}
}
// Ensure that the mock's AfterFunc executes at the correct time.
func TestMock_AfterFunc(t *testing.T) {
var ok int32
clock := clock.NewMock()
// Execute function after duration.
clock.AfterFunc(10*time.Second, func() {
atomic.StoreInt32(&ok, 1)
})
// Move clock forward to just before the time.
clock.Add(9 * time.Second)
if atomic.LoadInt32(&ok) == 1 {
t.Fatal("too early")
}
// Move clock forward to the after channel's time.
clock.Add(1 * time.Second)
if atomic.LoadInt32(&ok) == 0 {
t.Fatal("too late")
}
}
// Ensure that the mock's AfterFunc doesn't execute if stopped.
func TestMock_AfterFunc_Stop(t *testing.T) {
// Execute function after duration.
clock := clock.NewMock()
timer := clock.AfterFunc(10*time.Second, func() {
t.Fatal("unexpected function execution")
})
gosched()
// Stop timer & move clock forward.
timer.Stop()
clock.Add(10 * time.Second)
gosched()
}
// Ensure that the mock's current time can be changed.
func TestMock_Now(t *testing.T) {
clock := clock.NewMock()
if now := clock.Now(); !now.Equal(time.Unix(0, 0)) {
t.Fatalf("expected epoch, got: ", now)
}
// Add 10 seconds and check the time.
clock.Add(10 * time.Second)
if now := clock.Now(); !now.Equal(time.Unix(10, 0)) {
t.Fatalf("expected epoch, got: ", now)
}
}
// Ensure that the mock can sleep for the correct time.
func TestMock_Sleep(t *testing.T) {
var ok int32
clock := clock.NewMock()
// Create a channel to execute after 10 mock seconds.
go func() {
clock.Sleep(10 * time.Second)
atomic.StoreInt32(&ok, 1)
}()
gosched()
// Move clock forward to just before the sleep duration.
clock.Add(9 * time.Second)
if atomic.LoadInt32(&ok) == 1 {
t.Fatal("too early")
}
// Move clock forward to the after the sleep duration.
clock.Add(1 * time.Second)
if atomic.LoadInt32(&ok) == 0 {
t.Fatal("too late")
}
}
// Ensure that the mock's Tick channel sends at the correct time.
func TestMock_Tick(t *testing.T) {
var n int32
clock := clock.NewMock()
// Create a channel to increment every 10 seconds.
go func() {
tick := clock.Tick(10 * time.Second)
for {
<-tick
atomic.AddInt32(&n, 1)
}
}()
gosched()
// Move clock forward to just before the first tick.
clock.Add(9 * time.Second)
if atomic.LoadInt32(&n) != 0 {
t.Fatalf("expected 0, got %d", n)
}
// Move clock forward to the start of the first tick.
clock.Add(1 * time.Second)
if atomic.LoadInt32(&n) != 1 {
t.Fatalf("expected 1, got %d", n)
}
// Move clock forward over several ticks.
clock.Add(30 * time.Second)
if atomic.LoadInt32(&n) != 4 {
t.Fatalf("expected 4, got %d", n)
}
}
// Ensure that the mock's Ticker channel sends at the correct time.
func TestMock_Ticker(t *testing.T) {
var n int32
clock := clock.NewMock()
// Create a channel to increment every microsecond.
go func() {
ticker := clock.Ticker(1 * time.Microsecond)
for {
<-ticker.C
atomic.AddInt32(&n, 1)
}
}()
gosched()
// Move clock forward.
clock.Add(10 * time.Microsecond)
if atomic.LoadInt32(&n) != 10 {
t.Fatalf("unexpected: %d", n)
}
}
// Ensure that the mock's Ticker channel won't block if not read from.
func TestMock_Ticker_Overflow(t *testing.T) {
clock := clock.NewMock()
ticker := clock.Ticker(1 * time.Microsecond)
clock.Add(10 * time.Microsecond)
ticker.Stop()
}
// Ensure that the mock's Ticker can be stopped.
func TestMock_Ticker_Stop(t *testing.T) {
var n int32
clock := clock.NewMock()
// Create a channel to increment every second.
ticker := clock.Ticker(1 * time.Second)
go func() {
for {
<-ticker.C
atomic.AddInt32(&n, 1)
}
}()
gosched()
// Move clock forward.
clock.Add(5 * time.Second)
if atomic.LoadInt32(&n) != 5 {
t.Fatalf("expected 5, got: %d", n)
}
ticker.Stop()
// Move clock forward again.
clock.Add(5 * time.Second)
if atomic.LoadInt32(&n) != 5 {
t.Fatalf("still expected 5, got: %d", n)
}
}
// Ensure that multiple tickers can be used together.
func TestMock_Ticker_Multi(t *testing.T) {
var n int32
clock := clock.NewMock()
go func() {
a := clock.Ticker(1 * time.Microsecond)
b := clock.Ticker(3 * time.Microsecond)
for {
select {
case <-a.C:
atomic.AddInt32(&n, 1)
case <-b.C:
atomic.AddInt32(&n, 100)
}
}
}()
gosched()
// Move clock forward.
clock.Add(10 * time.Microsecond)
gosched()
if atomic.LoadInt32(&n) != 310 {
t.Fatalf("unexpected: %d", n)
}
}
func ExampleMock_After() {
// Create a new mock clock.
clock := clock.NewMock()
count := 0
// Create a channel to execute after 10 mock seconds.
go func() {
<-clock.After(10 * time.Second)
count = 100
}()
runtime.Gosched()
// Print the starting value.
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Move the clock forward 5 seconds and print the value again.
clock.Add(5 * time.Second)
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Move the clock forward 5 seconds to the tick time and check the value.
clock.Add(5 * time.Second)
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Output:
// 1970-01-01 00:00:00 +0000 UTC: 0
// 1970-01-01 00:00:05 +0000 UTC: 0
// 1970-01-01 00:00:10 +0000 UTC: 100
}
func ExampleMock_AfterFunc() {
// Create a new mock clock.
clock := clock.NewMock()
count := 0
// Execute a function after 10 mock seconds.
clock.AfterFunc(10*time.Second, func() {
count = 100
})
runtime.Gosched()
// Print the starting value.
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Move the clock forward 10 seconds and print the new value.
clock.Add(10 * time.Second)
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Output:
// 1970-01-01 00:00:00 +0000 UTC: 0
// 1970-01-01 00:00:10 +0000 UTC: 100
}
func ExampleMock_Sleep() {
// Create a new mock clock.
clock := clock.NewMock()
count := 0
// Execute a function after 10 mock seconds.
go func() {
clock.Sleep(10 * time.Second)
count = 100
}()
runtime.Gosched()
// Print the starting value.
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Move the clock forward 10 seconds and print the new value.
clock.Add(10 * time.Second)
fmt.Printf("%s: %d\n", clock.Now().UTC(), count)
// Output:
// 1970-01-01 00:00:00 +0000 UTC: 0
// 1970-01-01 00:00:10 +0000 UTC: 100
}
func ExampleMock_Ticker() {
// Create a new mock clock.
clock := clock.NewMock()
count := 0
// Increment count every mock second.
go func() {
ticker := clock.Ticker(1 * time.Second)
for {
<-ticker.C
count++
}
}()
runtime.Gosched()
// Move the clock forward 10 seconds and print the new value.
clock.Add(10 * time.Second)
fmt.Printf("Count is %d after 10 seconds\n", count)
// Move the clock forward 5 more seconds and print the new value.
clock.Add(5 * time.Second)
fmt.Printf("Count is %d after 15 seconds\n", count)
// Output:
// Count is 10 after 10 seconds
// Count is 15 after 15 seconds
}
func ExampleMock_Timer() {
// Create a new mock clock.
clock := clock.NewMock()
count := 0
// Increment count after a mock second.
go func() {
timer := clock.Timer(1 * time.Second)
<-timer.C
count++
}()
runtime.Gosched()
// Move the clock forward 10 seconds and print the new value.
clock.Add(10 * time.Second)
fmt.Printf("Count is %d after 10 seconds\n", count)
// Output:
// Count is 1 after 10 seconds
}
func warn(v ...interface{}) { fmt.Fprintln(os.Stderr, v...) }
func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) }
func gosched() { time.Sleep(1 * time.Millisecond) }
-677
View File
@@ -1,677 +0,0 @@
package httpdown_test
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"regexp"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/facebookgo/clock"
"github.com/facebookgo/ensure"
"github.com/facebookgo/freeport"
"github.com/facebookgo/httpdown"
"github.com/facebookgo/stats"
)
type onCloseListener struct {
net.Listener
mutex sync.Mutex
onClose chan struct{}
}
func (o *onCloseListener) Close() error {
// Listener is closed twice, once by Grace, and once by the http library, so
// we guard against a double close of the chan.
defer func() {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.onClose != nil {
close(o.onClose)
o.onClose = nil
}
}()
return o.Listener.Close()
}
func NewOnCloseListener(l net.Listener) (net.Listener, chan struct{}) {
c := make(chan struct{})
return &onCloseListener{Listener: l, onClose: c}, c
}
type closeErrListener struct {
net.Listener
err error
}
func (c *closeErrListener) Close() error {
c.Listener.Close()
return c.err
}
type acceptErrListener struct {
net.Listener
err chan error
}
func (c *acceptErrListener) Accept() (net.Conn, error) {
return nil, <-c.err
}
type closeErrConn struct {
net.Conn
unblockClose chan chan struct{}
}
func (c *closeErrConn) Close() error {
ch := <-c.unblockClose
// Close gets called multiple times, but only the first one gets this ch
if ch != nil {
defer close(ch)
}
return c.Conn.Close()
}
type closeErrConnListener struct {
net.Listener
unblockClose chan chan struct{}
}
func (l *closeErrConnListener) Accept() (net.Conn, error) {
c, err := l.Listener.Accept()
if err != nil {
return c, err
}
return &closeErrConn{Conn: c, unblockClose: l.unblockClose}, nil
}
func TestHTTPStopWithNoRequest(t *testing.T) {
t.Parallel()
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
statsDone := make(chan struct{}, 2)
hc := &stats.HookClient{
BumpSumHook: func(key string, val float64) {
if key == "serve" && val == 1 {
statsDone <- struct{}{}
}
if key == "stop" && val == 1 {
statsDone <- struct{}{}
}
},
}
server := &http.Server{}
down := &httpdown.HTTP{Stats: hc}
s := down.Serve(server, listener)
ensure.Nil(t, s.Stop())
<-statsDone
<-statsDone
}
func TestHTTPStopWithFinishedRequest(t *testing.T) {
t.Parallel()
hello := []byte("hello")
fin := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(fin)
w.Write(hello)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, hello)
ensure.Nil(t, res.Body.Close())
// At this point the request is finished, and the connection should be alive
// but idle (because we have keep alive enabled by default in our Transport).
ensure.Nil(t, s.Stop())
<-fin
ensure.Nil(t, s.Wait())
}
func TestHTTPStopWithActiveRequest(t *testing.T) {
t.Parallel()
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.WriteHeader(200)
for i := 0; i < count; i++ {
w.Write(hello)
}
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
ensure.Nil(t, res.Body.Close())
<-finOkHandler
<-finStop
}
func TestNewRequestAfterStop(t *testing.T) {
t.Parallel()
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
unblockOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.WriteHeader(200)
const diff = 500
for i := 0; i < count-diff; i++ {
w.Write(hello)
}
<-unblockOkHandler
for i := 0; i < diff; i++ {
w.Write(hello)
}
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
listener, onClose := NewOnCloseListener(listener)
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
// Wait until the listener is closed.
<-onClose
// Now the next request should not be able to connect as the listener is
// now closed.
_, err = client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
// We should just get "connection refused" here, but sometimes, very rarely,
// we get a "connection reset" instead. Unclear why this happens.
ensure.Err(t, err, regexp.MustCompile("(connection refused|connection reset by peer)$"))
// Unblock the handler and ensure we finish writing the rest of the body
// successfully.
close(unblockOkHandler)
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
ensure.Nil(t, res.Body.Close())
<-finOkHandler
<-finStop
}
func TestHTTPListenerCloseError(t *testing.T) {
t.Parallel()
expectedError := errors.New("foo")
listener, err := net.Listen("tcp", "127.0.0.1:0")
listener = &closeErrListener{Listener: listener, err: expectedError}
ensure.Nil(t, err)
server := &http.Server{}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
ensure.DeepEqual(t, s.Stop(), expectedError)
}
func TestHTTPServeError(t *testing.T) {
t.Parallel()
expectedError := errors.New("foo")
listener, err := net.Listen("tcp", "127.0.0.1:0")
errChan := make(chan error)
listener = &acceptErrListener{Listener: listener, err: errChan}
ensure.Nil(t, err)
server := &http.Server{}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
errChan <- expectedError
ensure.DeepEqual(t, s.Wait(), expectedError)
ensure.Nil(t, s.Stop())
}
func TestHTTPWithinStopTimeout(t *testing.T) {
t.Parallel()
hello := []byte("hello")
finOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.WriteHeader(200)
w.Write(hello)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{StopTimeout: time.Minute}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, hello)
ensure.Nil(t, res.Body.Close())
<-finOkHandler
<-finStop
}
func TestHTTPStopTimeoutMissed(t *testing.T) {
t.Parallel()
klock := clock.NewMock()
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
unblockOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
w.WriteHeader(200)
for i := 0; i < count/2; i++ {
w.Write(hello)
}
<-unblockOkHandler
for i := 0; i < count/2; i++ {
w.Write(hello)
}
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{
StopTimeout: time.Minute,
Clock: klock,
}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
klock.Add(down.StopTimeout)
_, err = ioutil.ReadAll(res.Body)
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
ensure.Nil(t, res.Body.Close())
close(unblockOkHandler)
<-finOkHandler
<-finStop
}
func TestHTTPKillTimeout(t *testing.T) {
t.Parallel()
klock := clock.NewMock()
statsDone := make(chan struct{}, 1)
hc := &stats.HookClient{
BumpSumHook: func(key string, val float64) {
if key == "kill" && val == 1 {
statsDone <- struct{}{}
}
},
}
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
unblockOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
w.WriteHeader(200)
for i := 0; i < count/2; i++ {
w.Write(hello)
}
<-unblockOkHandler
for i := 0; i < count/2; i++ {
w.Write(hello)
}
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{
StopTimeout: time.Minute,
KillTimeout: time.Minute,
Stats: hc,
Clock: klock,
}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
klock.Add(down.StopTimeout)
_, err = ioutil.ReadAll(res.Body)
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
ensure.Nil(t, res.Body.Close())
close(unblockOkHandler)
<-finOkHandler
<-finStop
<-statsDone
}
func TestHTTPKillTimeoutMissed(t *testing.T) {
t.Parallel()
klock := clock.NewMock()
statsDone := make(chan struct{}, 1)
hc := &stats.HookClient{
BumpSumHook: func(key string, val float64) {
if key == "kill.timeout" && val == 1 {
statsDone <- struct{}{}
}
},
}
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
unblockOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
w.WriteHeader(200)
for i := 0; i < count/2; i++ {
w.Write(hello)
}
<-unblockOkHandler
for i := 0; i < count/2; i++ {
w.Write(hello)
}
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
unblockConnClose := make(chan chan struct{}, 1)
listener = &closeErrConnListener{
Listener: listener,
unblockClose: unblockConnClose,
}
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{
StopTimeout: time.Minute,
KillTimeout: time.Minute,
Stats: hc,
Clock: klock,
}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
// Start the Stop process.
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
klock.Add(down.StopTimeout) // trigger stop timeout
klock.Wait(clock.Calls{After: 2}) // wait for Kill to call After
klock.Add(down.KillTimeout) // trigger kill timeout
// We hit both the StopTimeout & the KillTimeout.
<-finStop
// Then we unblock the Close, so we get an unexpected EOF since we close
// before we finish writing the response.
connCloseDone := make(chan struct{})
unblockConnClose <- connCloseDone
<-connCloseDone
close(unblockConnClose)
// Then we unblock the handler which tries to write the rest of the data.
close(unblockOkHandler)
_, err = ioutil.ReadAll(res.Body)
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
ensure.Nil(t, res.Body.Close())
<-finOkHandler
<-statsDone
}
func TestDoubleStop(t *testing.T) {
t.Parallel()
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
ensure.Nil(t, s.Stop())
ensure.Nil(t, s.Stop())
}
func TestExistingConnState(t *testing.T) {
t.Parallel()
hello := []byte("hello")
fin := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(fin)
w.Write(hello)
}
var called int32
listener, err := net.Listen("tcp", "127.0.0.1:0")
ensure.Nil(t, err)
server := &http.Server{
Handler: http.HandlerFunc(okHandler),
ConnState: func(c net.Conn, s http.ConnState) {
atomic.AddInt32(&called, 1)
},
}
transport := &http.Transport{}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{}
s := down.Serve(server, listener)
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
ensure.Nil(t, err)
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, hello)
ensure.Nil(t, res.Body.Close())
ensure.Nil(t, s.Stop())
<-fin
ensure.True(t, atomic.LoadInt32(&called) > 0)
}
func TestHTTPDefaultListenError(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("cant run this test as root")
}
statsDone := make(chan struct{}, 1)
hc := &stats.HookClient{
BumpSumHook: func(key string, val float64) {
if key == "listen.error" && val == 1 {
statsDone <- struct{}{}
}
},
}
t.Parallel()
down := &httpdown.HTTP{Stats: hc}
_, err := down.ListenAndServe(&http.Server{})
ensure.Err(t, err, regexp.MustCompile("listen tcp :80: bind: permission denied"))
<-statsDone
}
func TestHTTPSDefaultListenError(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("cant run this test as root")
}
t.Parallel()
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Fatalf("error loading cert: %v", err)
}
down := &httpdown.HTTP{}
_, err = down.ListenAndServe(&http.Server{
TLSConfig: &tls.Config{
NextProtos: []string{"http/1.1"},
Certificates: []tls.Certificate{cert},
},
})
ensure.Err(t, err, regexp.MustCompile("listen tcp :443: bind: permission denied"))
}
func TestTLS(t *testing.T) {
t.Parallel()
port, err := freeport.Get()
ensure.Nil(t, err)
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Fatalf("error loading cert: %v", err)
}
const count = 10000
hello := []byte("hello")
finOkHandler := make(chan struct{})
okHandler := func(w http.ResponseWriter, r *http.Request) {
defer close(finOkHandler)
w.WriteHeader(200)
for i := 0; i < count; i++ {
w.Write(hello)
}
}
server := &http.Server{
Addr: fmt.Sprintf("0.0.0.0:%d", port),
Handler: http.HandlerFunc(okHandler),
TLSConfig: &tls.Config{
NextProtos: []string{"http/1.1"},
Certificates: []tls.Certificate{cert},
},
}
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: transport}
down := &httpdown.HTTP{}
s, err := down.ListenAndServe(server)
ensure.Nil(t, err)
res, err := client.Get(fmt.Sprintf("https://%s/", server.Addr))
ensure.Nil(t, err)
finStop := make(chan struct{})
go func() {
defer close(finStop)
ensure.Nil(t, s.Stop())
}()
actualBody, err := ioutil.ReadAll(res.Body)
ensure.Nil(t, err)
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
ensure.Nil(t, res.Body.Close())
<-finOkHandler
<-finStop
}
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
// generated from src/pkg/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBALyCfqwwip8BvTKgVKGdmjZTU8DD
ndR+WALmFPIRqn89bOU3s30olKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEA
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAr/09uy108p51rheIOSnz4zgduyTl
M+4AmRo8/U1twEZLgfAGG/GZjREv2y4mCEUIM3HebCAqlA5jpRg76Rf8jw==
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALyCfqwwip8BvTKgVKGdmjZTU8DDndR+WALmFPIRqn89bOU3s30o
lKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEAAQJAPXuWUxTV8XyAt8VhNQER
LgzJcUKb9JVsoS1nwXgPksXnPDKnL9ax8VERrdNr+nZbj2Q9cDSXBUovfdtehcdP
qQIhAO48ZsPylbTrmtjDEKiHT2Ik04rLotZYS2U873J6I7WlAiEAypDjYxXyafv/
Yo1pm9onwcetQKMW8CS3AjuV9Axzj6cCIEx2Il19fEMG4zny0WPlmbrcKvD/DpJQ
4FHrzsYlIVTpAiAas7S1uAvneqd0l02HlN9OxQKKlbUNXNme+rnOnOGS2wIgS0jW
zl1jvrOSJeP1PpAHohWz6LOhEr8uvltWkN6x3vE=
-----END RSA PRIVATE KEY-----`)
-77
View File
@@ -1,77 +0,0 @@
package stats_test
import (
"testing"
"github.com/facebookgo/ensure"
"github.com/facebookgo/stats"
)
// Ensure calling End works even when a BumpTimeHook isn't provided.
func TestHookClientBumpTime(t *testing.T) {
(&stats.HookClient{}).BumpTime("foo").End()
}
func TestPrefixClient(t *testing.T) {
const (
prefix1 = "prefix1"
prefix2 = "prefix2"
avgKey = "avg"
avgVal = float64(1)
sumKey = "sum"
sumVal = float64(2)
histogramKey = "histogram"
histogramVal = float64(3)
timeKey = "time"
)
var keys []string
hc := &stats.HookClient{
BumpAvgHook: func(key string, val float64) {
keys = append(keys, key)
ensure.DeepEqual(t, val, avgVal)
},
BumpSumHook: func(key string, val float64) {
keys = append(keys, key)
ensure.DeepEqual(t, val, sumVal)
},
BumpHistogramHook: func(key string, val float64) {
keys = append(keys, key)
ensure.DeepEqual(t, val, histogramVal)
},
BumpTimeHook: func(key string) interface {
End()
} {
return multiEnderTest{
EndHook: func() {
keys = append(keys, key)
},
}
},
}
pc := stats.PrefixClient([]string{prefix1, prefix2}, hc)
pc.BumpAvg(avgKey, avgVal)
pc.BumpSum(sumKey, sumVal)
pc.BumpHistogram(histogramKey, histogramVal)
pc.BumpTime(timeKey).End()
ensure.SameElements(t, keys, []string{
prefix1 + avgKey,
prefix1 + sumKey,
prefix1 + histogramKey,
prefix1 + timeKey,
prefix2 + avgKey,
prefix2 + sumKey,
prefix2 + histogramKey,
prefix2 + timeKey,
})
}
type multiEnderTest struct {
EndHook func()
}
func (e multiEnderTest) End() {
e.EndHook()
}
-212
View File
@@ -1,212 +0,0 @@
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
NoColor = false
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 TestColorEquals(t *testing.T) {
fgblack1 := New(FgBlack)
fgblack2 := New(FgBlack)
bgblack := New(BgBlack)
fgbgblack := New(FgBlack, BgBlack)
fgblackbgred := New(FgBlack, BgRed)
fgred := New(FgRed)
bgred := New(BgRed)
if !fgblack1.Equals(fgblack2) {
t.Error("Two black colors are not equal")
}
if fgblack1.Equals(bgblack) {
t.Error("Fg and bg black colors are equal")
}
if fgblack1.Equals(fgbgblack) {
t.Error("Fg black equals fg/bg black color")
}
if fgblack1.Equals(fgred) {
t.Error("Fg black equals Fg red")
}
if fgblack1.Equals(bgred) {
t.Error("Fg black equals Bg red")
}
if fgblack1.Equals(fgblackbgred) {
t.Error("Fg black equals fg black bg red")
}
}
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"))
}
-324
View File
@@ -1,324 +0,0 @@
package structs
import (
"reflect"
"testing"
)
// A test struct that defines all cases
type Foo struct {
A string
B int `structs:"y"`
C bool `json:"c"`
d string // not exported
E *Baz
x string `xml:"x"` // not exported, with tag
Y []string
Z map[string]interface{}
*Bar // embedded
}
type Baz struct {
A string
B int
}
type Bar struct {
E string
F int
g []string
}
func newStruct() *Struct {
b := &Bar{
E: "example",
F: 2,
g: []string{"zeynep", "fatih"},
}
// B and x is not initialized for testing
f := &Foo{
A: "gopher",
C: true,
d: "small",
E: nil,
Y: []string{"example"},
Z: nil,
}
f.Bar = b
return New(f)
}
func TestField_Set(t *testing.T) {
s := newStruct()
f := s.Field("A")
err := f.Set("fatih")
if err != nil {
t.Error(err)
}
if f.Value().(string) != "fatih" {
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
}
f = s.Field("Y")
err = f.Set([]string{"override", "with", "this"})
if err != nil {
t.Error(err)
}
sliceLen := len(f.Value().([]string))
if sliceLen != 3 {
t.Errorf("Setted values slice length is wrong: %d, want: %d", sliceLen, 3)
}
f = s.Field("C")
err = f.Set(false)
if err != nil {
t.Error(err)
}
if f.Value().(bool) {
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(bool), false)
}
// let's pass a different type
f = s.Field("A")
err = f.Set(123) // Field A is of type string, but we are going to pass an integer
if err == nil {
t.Error("Setting a field's value with a different type than the field's type should return an error")
}
// old value should be still there :)
if f.Value().(string) != "fatih" {
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
}
// let's access an unexported field, which should give an error
f = s.Field("d")
err = f.Set("large")
if err != errNotExported {
t.Error(err)
}
// let's set a pointer to struct
b := &Bar{
E: "gopher",
F: 2,
}
f = s.Field("Bar")
err = f.Set(b)
if err != nil {
t.Error(err)
}
baz := &Baz{
A: "helloWorld",
B: 42,
}
f = s.Field("E")
err = f.Set(baz)
if err != nil {
t.Error(err)
}
ba := s.Field("E").Value().(*Baz)
if ba.A != "helloWorld" {
t.Errorf("could not set baz. Got: %s Want: helloWorld", ba.A)
}
}
func TestField(t *testing.T) {
s := newStruct()
defer func() {
err := recover()
if err == nil {
t.Error("Retrieveing a non existing field from the struct should panic")
}
}()
_ = s.Field("no-field")
}
func TestField_Kind(t *testing.T) {
s := newStruct()
f := s.Field("A")
if f.Kind() != reflect.String {
t.Errorf("Field A has wrong kind: %s want: %s", f.Kind(), reflect.String)
}
f = s.Field("B")
if f.Kind() != reflect.Int {
t.Errorf("Field B has wrong kind: %s want: %s", f.Kind(), reflect.Int)
}
// unexported
f = s.Field("d")
if f.Kind() != reflect.String {
t.Errorf("Field d has wrong kind: %s want: %s", f.Kind(), reflect.String)
}
}
func TestField_Tag(t *testing.T) {
s := newStruct()
v := s.Field("B").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a non existing tag should return empty, got: %s", v)
}
v = s.Field("C").Tag("json")
if v != "c" {
t.Errorf("Field's tag value of the existing field C should return 'c', got: %s", v)
}
v = s.Field("d").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a non exported field should return empty, got: %s", v)
}
v = s.Field("x").Tag("xml")
if v != "x" {
t.Errorf("Field's tag value of a non exported field with a tag should return 'x', got: %s", v)
}
v = s.Field("A").Tag("json")
if v != "" {
t.Errorf("Field's tag value of a existing field without a tag should return empty, got: %s", v)
}
}
func TestField_Value(t *testing.T) {
s := newStruct()
v := s.Field("A").Value()
val, ok := v.(string)
if !ok {
t.Errorf("Field's value of a A should be string")
}
if val != "gopher" {
t.Errorf("Field's value of a existing tag should return 'gopher', got: %s", val)
}
defer func() {
err := recover()
if err == nil {
t.Error("Value of a non exported field from the field should panic")
}
}()
// should panic
_ = s.Field("d").Value()
}
func TestField_IsEmbedded(t *testing.T) {
s := newStruct()
if !s.Field("Bar").IsEmbedded() {
t.Errorf("Fields 'Bar' field is an embedded field")
}
if s.Field("d").IsEmbedded() {
t.Errorf("Fields 'd' field is not an embedded field")
}
}
func TestField_IsExported(t *testing.T) {
s := newStruct()
if !s.Field("Bar").IsExported() {
t.Errorf("Fields 'Bar' field is an exported field")
}
if !s.Field("A").IsExported() {
t.Errorf("Fields 'A' field is an exported field")
}
if s.Field("d").IsExported() {
t.Errorf("Fields 'd' field is not an exported field")
}
}
func TestField_IsZero(t *testing.T) {
s := newStruct()
if s.Field("A").IsZero() {
t.Errorf("Fields 'A' field is an initialized field")
}
if !s.Field("B").IsZero() {
t.Errorf("Fields 'B' field is not an initialized field")
}
}
func TestField_Name(t *testing.T) {
s := newStruct()
if s.Field("A").Name() != "A" {
t.Errorf("Fields 'A' field should have the name 'A'")
}
}
func TestField_Field(t *testing.T) {
s := newStruct()
e := s.Field("Bar").Field("E")
val, ok := e.Value().(string)
if !ok {
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
}
if val != "example" {
t.Errorf("The value of 'e' should be 'example, got: %s", val)
}
defer func() {
err := recover()
if err == nil {
t.Error("Field of a non existing nested struct should panic")
}
}()
_ = s.Field("Bar").Field("e")
}
func TestField_Fields(t *testing.T) {
s := newStruct()
fields := s.Field("Bar").Fields()
if len(fields) != 3 {
t.Errorf("We expect 3 fields in embedded struct, was: %d", len(fields))
}
}
func TestField_FieldOk(t *testing.T) {
s := newStruct()
b, ok := s.FieldOk("Bar")
if !ok {
t.Error("The field 'Bar' should exists.")
}
e, ok := b.FieldOk("E")
if !ok {
t.Error("The field 'E' should exists.")
}
val, ok := e.Value().(string)
if !ok {
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
}
if val != "example" {
t.Errorf("The value of 'e' should be 'example, got: %s", val)
}
}
-351
View File
@@ -1,351 +0,0 @@
package structs
import (
"fmt"
"time"
)
func ExampleNew() {
type Server struct {
Name string
ID int32
Enabled bool
}
server := &Server{
Name: "Arslan",
ID: 123456,
Enabled: true,
}
s := New(server)
fmt.Printf("Name : %v\n", s.Name())
fmt.Printf("Values : %v\n", s.Values())
fmt.Printf("Value of ID : %v\n", s.Field("ID").Value())
// Output:
// Name : Server
// Values : [Arslan 123456 true]
// Value of ID : 123456
}
func ExampleMap() {
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "Arslan",
ID: 123456,
Enabled: true,
}
m := Map(s)
fmt.Printf("%#v\n", m["Name"])
fmt.Printf("%#v\n", m["ID"])
fmt.Printf("%#v\n", m["Enabled"])
// Output:
// "Arslan"
// 123456
// true
}
func ExampleMap_tags() {
// Custom tags can change the map keys instead of using the fields name
type Server struct {
Name string `structs:"server_name"`
ID int32 `structs:"server_id"`
Enabled bool `structs:"enabled"`
}
s := &Server{
Name: "Zeynep",
ID: 789012,
}
m := Map(s)
// access them by the custom tags defined above
fmt.Printf("%#v\n", m["server_name"])
fmt.Printf("%#v\n", m["server_id"])
fmt.Printf("%#v\n", m["enabled"])
// Output:
// "Zeynep"
// 789012
// false
}
func ExampleMap_nested() {
// By default field with struct types are processed too. We can stop
// processing them via "omitnested" tag option.
type Server struct {
Name string `structs:"server_name"`
ID int32 `structs:"server_id"`
Time time.Time `structs:"time,omitnested"` // do not convert to map[string]interface{}
}
const shortForm = "2006-Jan-02"
t, _ := time.Parse("2006-Jan-02", "2013-Feb-03")
s := &Server{
Name: "Zeynep",
ID: 789012,
Time: t,
}
m := Map(s)
// access them by the custom tags defined above
fmt.Printf("%v\n", m["server_name"])
fmt.Printf("%v\n", m["server_id"])
fmt.Printf("%v\n", m["time"].(time.Time))
// Output:
// Zeynep
// 789012
// 2013-02-03 00:00:00 +0000 UTC
}
func ExampleMap_omitEmpty() {
// By default field with struct types of zero values are processed too. We
// can stop processing them via "omitempty" tag option.
type Server struct {
Name string `structs:",omitempty"`
ID int32 `structs:"server_id,omitempty"`
Location string
}
// Only add location
s := &Server{
Location: "Tokyo",
}
m := Map(s)
// map contains only the Location field
fmt.Printf("%v\n", m)
// Output:
// map[Location:Tokyo]
}
func ExampleValues() {
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "Fatih",
ID: 135790,
Enabled: false,
}
m := Values(s)
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Fatih 135790 false]
}
func ExampleValues_omitEmpty() {
// By default field with struct types of zero values are processed too. We
// can stop processing them via "omitempty" tag option.
type Server struct {
Name string `structs:",omitempty"`
ID int32 `structs:"server_id,omitempty"`
Location string
}
// Only add location
s := &Server{
Location: "Ankara",
}
m := Values(s)
// values contains only the Location field
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Ankara]
}
func ExampleValues_tags() {
type Location struct {
City string
Country string
}
type Server struct {
Name string
ID int32
Enabled bool
Location Location `structs:"-"` // values from location are not included anymore
}
s := &Server{
Name: "Fatih",
ID: 135790,
Enabled: false,
Location: Location{City: "Ankara", Country: "Turkey"},
}
// Let get all values from the struct s. Note that we don't include values
// from the Location field
m := Values(s)
fmt.Printf("Values: %+v\n", m)
// Output:
// Values: [Fatih 135790 false]
}
func ExampleFields() {
type Access struct {
Name string
LastAccessed time.Time
Number int
}
s := &Access{
Name: "Fatih",
LastAccessed: time.Now(),
Number: 1234567,
}
fields := Fields(s)
for i, field := range fields {
fmt.Printf("[%d] %+v\n", i, field.Name())
}
// Output:
// [0] Name
// [1] LastAccessed
// [2] Number
}
func ExampleFields_nested() {
type Person struct {
Name string
Number int
}
type Access struct {
Person Person
HasPermission bool
LastAccessed time.Time
}
s := &Access{
Person: Person{Name: "fatih", Number: 1234567},
LastAccessed: time.Now(),
HasPermission: true,
}
// Let's get all fields from the struct s.
fields := Fields(s)
for _, field := range fields {
if field.Name() == "Person" {
fmt.Printf("Access.Person.Name: %+v\n", field.Field("Name").Value())
}
}
// Output:
// Access.Person.Name: fatih
}
func ExampleField() {
type Person struct {
Name string
Number int
}
type Access struct {
Person Person
HasPermission bool
LastAccessed time.Time
}
access := &Access{
Person: Person{Name: "fatih", Number: 1234567},
LastAccessed: time.Now(),
HasPermission: true,
}
// Create a new Struct type
s := New(access)
// Get the Field type for "Person" field
p := s.Field("Person")
// Get the underlying "Name field" and print the value of it
name := p.Field("Name")
fmt.Printf("Value of Person.Access.Name: %+v\n", name.Value())
// Output:
// Value of Person.Access.Name: fatih
}
func ExampleIsZero() {
type Server struct {
Name string
ID int32
Enabled bool
}
// Nothing is initalized
a := &Server{}
isZeroA := IsZero(a)
// Name and Enabled is initialized, but not ID
b := &Server{
Name: "Golang",
Enabled: true,
}
isZeroB := IsZero(b)
fmt.Printf("%#v\n", isZeroA)
fmt.Printf("%#v\n", isZeroB)
// Output:
// true
// false
}
func ExampleHasZero() {
// Let's define an Access struct. Note that the "Enabled" field is not
// going to be checked because we added the "structs" tag to the field.
type Access struct {
Name string
LastAccessed time.Time
Number int
Enabled bool `structs:"-"`
}
// Name and Number is not initialized.
a := &Access{
LastAccessed: time.Now(),
}
hasZeroA := HasZero(a)
// Name and Number is initialized.
b := &Access{
Name: "Fatih",
LastAccessed: time.Now(),
Number: 12345,
}
hasZeroB := HasZero(b)
fmt.Printf("%#v\n", hasZeroA)
fmt.Printf("%#v\n", hasZeroB)
// Output:
// true
// false
}
-898
View File
@@ -1,898 +0,0 @@
package structs
import (
"fmt"
"reflect"
"testing"
"time"
)
func TestMapNonStruct(t *testing.T) {
foo := []string{"foo"}
defer func() {
err := recover()
if err == nil {
t.Error("Passing a non struct into Map should panic")
}
}()
// this should panic. We are going to recover and and test it
_ = Map(foo)
}
func TestStructIndexes(t *testing.T) {
type C struct {
something int
Props map[string]interface{}
}
defer func() {
err := recover()
if err != nil {
fmt.Printf("err %+v\n", err)
t.Error("Using mixed indexes should not panic")
}
}()
// They should not panic
_ = Map(&C{})
_ = Fields(&C{})
_ = Values(&C{})
_ = IsZero(&C{})
_ = HasZero(&C{})
}
func TestMap(t *testing.T) {
var T = struct {
A string
B int
C bool
}{
A: "a-value",
B: 2,
C: true,
}
a := Map(T)
if typ := reflect.TypeOf(a).Kind(); typ != reflect.Map {
t.Errorf("Map should return a map type, got: %v", typ)
}
// we have three fields
if len(a) != 3 {
t.Errorf("Map should return a map of len 3, got: %d", len(a))
}
inMap := func(val interface{}) bool {
for _, v := range a {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []interface{}{"a-value", 2, true} {
if !inMap(val) {
t.Errorf("Map should have the value %v", val)
}
}
}
func TestMap_Tag(t *testing.T) {
var T = struct {
A string `structs:"x"`
B int `structs:"y"`
C bool `structs:"z"`
}{
A: "a-value",
B: 2,
C: true,
}
a := Map(T)
inMap := func(key interface{}) bool {
for k := range a {
if reflect.DeepEqual(k, key) {
return true
}
}
return false
}
for _, key := range []string{"x", "y", "z"} {
if !inMap(key) {
t.Errorf("Map should have the key %v", key)
}
}
}
func TestMap_CustomTag(t *testing.T) {
var T = struct {
A string `json:"x"`
B int `json:"y"`
C bool `json:"z"`
D struct {
E string `json:"jkl"`
} `json:"nested"`
}{
A: "a-value",
B: 2,
C: true,
}
T.D.E = "e-value"
s := New(T)
s.TagName = "json"
a := s.Map()
inMap := func(key interface{}) bool {
for k := range a {
if reflect.DeepEqual(k, key) {
return true
}
}
return false
}
for _, key := range []string{"x", "y", "z"} {
if !inMap(key) {
t.Errorf("Map should have the key %v", key)
}
}
nested, ok := a["nested"].(map[string]interface{})
if !ok {
t.Fatalf("Map should contain the D field that is tagged as 'nested'")
}
e, ok := nested["jkl"].(string)
if !ok {
t.Fatalf("Map should contain the D.E field that is tagged as 'jkl'")
}
if e != "e-value" {
t.Errorf("D.E field should be equal to 'e-value', got: '%v'", e)
}
}
func TestMap_MultipleCustomTag(t *testing.T) {
var A = struct {
X string `aa:"ax"`
}{"a_value"}
aStruct := New(A)
aStruct.TagName = "aa"
var B = struct {
X string `bb:"bx"`
}{"b_value"}
bStruct := New(B)
bStruct.TagName = "bb"
a, b := aStruct.Map(), bStruct.Map()
if !reflect.DeepEqual(a, map[string]interface{}{"ax": "a_value"}) {
t.Error("Map should have field ax with value a_value")
}
if !reflect.DeepEqual(b, map[string]interface{}{"bx": "b_value"}) {
t.Error("Map should have field bx with value b_value")
}
}
func TestMap_OmitEmpty(t *testing.T) {
type A struct {
Name string
Value string `structs:",omitempty"`
Time time.Time `structs:",omitempty"`
}
a := A{}
m := Map(a)
_, ok := m["Value"].(map[string]interface{})
if ok {
t.Error("Map should not contain the Value field that is tagged as omitempty")
}
_, ok = m["Time"].(map[string]interface{})
if ok {
t.Error("Map should not contain the Time field that is tagged as omitempty")
}
}
func TestMap_OmitNested(t *testing.T) {
type A struct {
Name string
Value string
Time time.Time `structs:",omitnested"`
}
a := A{Time: time.Now()}
type B struct {
Desc string
A A
}
b := &B{A: a}
m := Map(b)
in, ok := m["A"].(map[string]interface{})
if !ok {
t.Error("Map nested structs is not available in the map")
}
// should not happen
if _, ok := in["Time"].(map[string]interface{}); ok {
t.Error("Map nested struct should omit recursiving parsing of Time")
}
if _, ok := in["Time"].(time.Time); !ok {
t.Error("Map nested struct should stop parsing of Time at is current value")
}
}
func TestMap_Nested(t *testing.T) {
type A struct {
Name string
}
a := &A{Name: "example"}
type B struct {
A *A
}
b := &B{A: a}
m := Map(b)
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
t.Errorf("Map should return a map type, got: %v", typ)
}
in, ok := m["A"].(map[string]interface{})
if !ok {
t.Error("Map nested structs is not available in the map")
}
if name := in["Name"].(string); name != "example" {
t.Errorf("Map nested struct's name field should give example, got: %s", name)
}
}
func TestMap_Anonymous(t *testing.T) {
type A struct {
Name string
}
a := &A{Name: "example"}
type B struct {
*A
}
b := &B{}
b.A = a
m := Map(b)
if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map {
t.Errorf("Map should return a map type, got: %v", typ)
}
in, ok := m["A"].(map[string]interface{})
if !ok {
t.Error("Embedded structs is not available in the map")
}
if name := in["Name"].(string); name != "example" {
t.Errorf("Embedded A struct's Name field should give example, got: %s", name)
}
}
func TestStruct(t *testing.T) {
var T = struct{}{}
if !IsStruct(T) {
t.Errorf("T should be a struct, got: %T", T)
}
if !IsStruct(&T) {
t.Errorf("T should be a struct, got: %T", T)
}
}
func TestValues(t *testing.T) {
var T = struct {
A string
B int
C bool
}{
A: "a-value",
B: 2,
C: true,
}
s := Values(T)
if typ := reflect.TypeOf(s).Kind(); typ != reflect.Slice {
t.Errorf("Values should return a slice type, got: %v", typ)
}
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []interface{}{"a-value", 2, true} {
if !inSlice(val) {
t.Errorf("Values should have the value %v", val)
}
}
}
func TestValues_OmitEmpty(t *testing.T) {
type A struct {
Name string
Value int `structs:",omitempty"`
}
a := A{Name: "example"}
s := Values(a)
if len(s) != 1 {
t.Errorf("Values of omitted empty fields should be not counted")
}
if s[0].(string) != "example" {
t.Errorf("Values of omitted empty fields should left the value example")
}
}
func TestValues_OmitNested(t *testing.T) {
type A struct {
Name string
Value int
}
a := A{
Name: "example",
Value: 123,
}
type B struct {
A A `structs:",omitnested"`
C int
}
b := &B{A: a, C: 123}
s := Values(b)
if len(s) != 2 {
t.Errorf("Values of omitted nested struct should be not counted")
}
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []interface{}{123, a} {
if !inSlice(val) {
t.Errorf("Values should have the value %v", val)
}
}
}
func TestValues_Nested(t *testing.T) {
type A struct {
Name string
}
a := A{Name: "example"}
type B struct {
A A
C int
}
b := &B{A: a, C: 123}
s := Values(b)
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []interface{}{"example", 123} {
if !inSlice(val) {
t.Errorf("Values should have the value %v", val)
}
}
}
func TestValues_Anonymous(t *testing.T) {
type A struct {
Name string
}
a := A{Name: "example"}
type B struct {
A
C int
}
b := &B{C: 123}
b.A = a
s := Values(b)
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []interface{}{"example", 123} {
if !inSlice(val) {
t.Errorf("Values should have the value %v", val)
}
}
}
func TestNames(t *testing.T) {
var T = struct {
A string
B int
C bool
}{
A: "a-value",
B: 2,
C: true,
}
s := Names(T)
if len(s) != 3 {
t.Errorf("Names should return a slice of len 3, got: %d", len(s))
}
inSlice := func(val string) bool {
for _, v := range s {
if reflect.DeepEqual(v, val) {
return true
}
}
return false
}
for _, val := range []string{"A", "B", "C"} {
if !inSlice(val) {
t.Errorf("Names should have the value %v", val)
}
}
}
func TestFields(t *testing.T) {
var T = struct {
A string
B int
C bool
}{
A: "a-value",
B: 2,
C: true,
}
s := Fields(T)
if len(s) != 3 {
t.Errorf("Fields should return a slice of len 3, got: %d", len(s))
}
inSlice := func(val string) bool {
for _, v := range s {
if reflect.DeepEqual(v.Name(), val) {
return true
}
}
return false
}
for _, val := range []string{"A", "B", "C"} {
if !inSlice(val) {
t.Errorf("Fields should have the value %v", val)
}
}
}
func TestFields_OmitNested(t *testing.T) {
type A struct {
Name string
Enabled bool
}
a := A{Name: "example"}
type B struct {
A A
C int
Value string `structs:"-"`
Number int
}
b := &B{A: a, C: 123}
s := Fields(b)
if len(s) != 3 {
t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s))
}
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v.Name(), val) {
return true
}
}
return false
}
for _, val := range []interface{}{"A", "C"} {
if !inSlice(val) {
t.Errorf("Fields should have the value %v", val)
}
}
}
func TestFields_Anonymous(t *testing.T) {
type A struct {
Name string
}
a := A{Name: "example"}
type B struct {
A
C int
}
b := &B{C: 123}
b.A = a
s := Fields(b)
inSlice := func(val interface{}) bool {
for _, v := range s {
if reflect.DeepEqual(v.Name(), val) {
return true
}
}
return false
}
for _, val := range []interface{}{"A", "C"} {
if !inSlice(val) {
t.Errorf("Fields should have the value %v", val)
}
}
}
func TestIsZero(t *testing.T) {
var T = struct {
A string
B int
C bool `structs:"-"`
D []string
}{}
ok := IsZero(T)
if !ok {
t.Error("IsZero should return true because none of the fields are initialized.")
}
var X = struct {
A string
F *bool
}{
A: "a-value",
}
ok = IsZero(X)
if ok {
t.Error("IsZero should return false because A is initialized")
}
var Y = struct {
A string
B int
}{
A: "a-value",
B: 123,
}
ok = IsZero(Y)
if ok {
t.Error("IsZero should return false because A and B is initialized")
}
}
func TestIsZero_OmitNested(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A A `structs:",omitnested"`
C int
}
b := &B{A: a, C: 123}
ok := IsZero(b)
if ok {
t.Error("IsZero should return false because A, B and C are initialized")
}
aZero := A{}
bZero := &B{A: aZero}
ok = IsZero(bZero)
if !ok {
t.Error("IsZero should return true because neither A nor B is initialized")
}
}
func TestIsZero_Nested(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A A
C int
}
b := &B{A: a, C: 123}
ok := IsZero(b)
if ok {
t.Error("IsZero should return false because A, B and C are initialized")
}
aZero := A{}
bZero := &B{A: aZero}
ok = IsZero(bZero)
if !ok {
t.Error("IsZero should return true because neither A nor B is initialized")
}
}
func TestIsZero_Anonymous(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A
C int
}
b := &B{C: 123}
b.A = a
ok := IsZero(b)
if ok {
t.Error("IsZero should return false because A, B and C are initialized")
}
aZero := A{}
bZero := &B{}
bZero.A = aZero
ok = IsZero(bZero)
if !ok {
t.Error("IsZero should return true because neither A nor B is initialized")
}
}
func TestHasZero(t *testing.T) {
var T = struct {
A string
B int
C bool `structs:"-"`
D []string
}{
A: "a-value",
B: 2,
}
ok := HasZero(T)
if !ok {
t.Error("HasZero should return true because A and B are initialized.")
}
var X = struct {
A string
F *bool
}{
A: "a-value",
}
ok = HasZero(X)
if !ok {
t.Error("HasZero should return true because A is initialized")
}
var Y = struct {
A string
B int
}{
A: "a-value",
B: 123,
}
ok = HasZero(Y)
if ok {
t.Error("HasZero should return false because A and B is initialized")
}
}
func TestHasZero_OmitNested(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A A `structs:",omitnested"`
C int
}
b := &B{A: a, C: 123}
// Because the Field A inside B is omitted HasZero should return false
// because it will stop iterating deeper andnot going to lookup for D
ok := HasZero(b)
if ok {
t.Error("HasZero should return false because A and C are initialized")
}
}
func TestHasZero_Nested(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A A
C int
}
b := &B{A: a, C: 123}
ok := HasZero(b)
if !ok {
t.Error("HasZero should return true because D is not initialized")
}
}
func TestHasZero_Anonymous(t *testing.T) {
type A struct {
Name string
D string
}
a := A{Name: "example"}
type B struct {
A
C int
}
b := &B{C: 123}
b.A = a
ok := HasZero(b)
if !ok {
t.Error("HasZero should return false because D is not initialized")
}
}
func TestName(t *testing.T) {
type Foo struct {
A string
B bool
}
f := &Foo{}
n := Name(f)
if n != "Foo" {
t.Errorf("Name should return Foo, got: %s", n)
}
unnamed := struct{ Name string }{Name: "Cihangir"}
m := Name(unnamed)
if m != "" {
t.Errorf("Name should return empty string for unnamed struct, got: %s", n)
}
defer func() {
err := recover()
if err == nil {
t.Error("Name should panic if a non struct is passed")
}
}()
Name([]string{})
}
func TestNestedNilPointer(t *testing.T) {
type Collar struct {
Engraving string
}
type Dog struct {
Name string
Collar *Collar
}
type Person struct {
Name string
Dog *Dog
}
person := &Person{
Name: "John",
}
personWithDog := &Person{
Name: "Ron",
Dog: &Dog{
Name: "Rover",
},
}
personWithDogWithCollar := &Person{
Name: "Kon",
Dog: &Dog{
Name: "Ruffles",
Collar: &Collar{
Engraving: "If lost, call Kon",
},
},
}
defer func() {
err := recover()
if err != nil {
fmt.Printf("err %+v\n", err)
t.Error("Internal nil pointer should not panic")
}
}()
_ = Map(person) // Panics
_ = Map(personWithDog) // Panics
_ = Map(personWithDogWithCollar) // Doesn't panic
}
-46
View File
@@ -1,46 +0,0 @@
package structs
import "testing"
func TestParseTag_Name(t *testing.T) {
tags := []struct {
tag string
has bool
}{
{"", false},
{"name", true},
{"name,opt", true},
{"name , opt, opt2", false}, // has a single whitespace
{", opt, opt2", false},
}
for _, tag := range tags {
name, _ := parseTag(tag.tag)
if (name != "name") && tag.has {
t.Errorf("Parse tag should return name: %#v", tag)
}
}
}
func TestParseTag_Opts(t *testing.T) {
tags := []struct {
opts string
has bool
}{
{"name", false},
{"name,opt", true},
{"name , opt, opt2", false}, // has a single whitespace
{",opt, opt2", true},
{", opt3, opt4", false},
}
// search for "opt"
for _, tag := range tags {
_, opts := parseTag(tag.opts)
if opts.Has("opt") != tag.has {
t.Errorf("Tag opts should have opt: %#v", tag)
}
}
}
-161
View File
@@ -1,161 +0,0 @@
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"net/http"
"testing"
)
type keyType int
const (
key1 keyType = iota
key2
)
func TestContext(t *testing.T) {
assertEqual := func(val interface{}, exp interface{}) {
if val != exp {
t.Errorf("Expected %v, got %v.", exp, val)
}
}
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
// Get()
assertEqual(Get(r, key1), nil)
// Set()
Set(r, key1, "1")
assertEqual(Get(r, key1), "1")
assertEqual(len(data[r]), 1)
Set(r, key2, "2")
assertEqual(Get(r, key2), "2")
assertEqual(len(data[r]), 2)
//GetOk
value, ok := GetOk(r, key1)
assertEqual(value, "1")
assertEqual(ok, true)
value, ok = GetOk(r, "not exists")
assertEqual(value, nil)
assertEqual(ok, false)
Set(r, "nil value", nil)
value, ok = GetOk(r, "nil value")
assertEqual(value, nil)
assertEqual(ok, true)
// GetAll()
values := GetAll(r)
assertEqual(len(values), 3)
// GetAll() for empty request
values = GetAll(emptyR)
if values != nil {
t.Error("GetAll didn't return nil value for invalid request")
}
// GetAllOk()
values, ok = GetAllOk(r)
assertEqual(len(values), 3)
assertEqual(ok, true)
// GetAllOk() for empty request
values, ok = GetAllOk(emptyR)
assertEqual(value, nil)
assertEqual(ok, false)
// Delete()
Delete(r, key1)
assertEqual(Get(r, key1), nil)
assertEqual(len(data[r]), 2)
Delete(r, key2)
assertEqual(Get(r, key2), nil)
assertEqual(len(data[r]), 1)
// Clear()
Clear(r)
assertEqual(len(data), 0)
}
func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
<-wait
for i := 0; i < iterations; i++ {
Get(r, key)
}
done <- struct{}{}
}
func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
<-wait
for i := 0; i < iterations; i++ {
Set(r, key, value)
}
done <- struct{}{}
}
func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
b.StopTimer()
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
done := make(chan struct{})
b.StartTimer()
for i := 0; i < b.N; i++ {
wait := make(chan struct{})
for i := 0; i < numReaders; i++ {
go parallelReader(r, "test", iterations, wait, done)
}
for i := 0; i < numWriters; i++ {
go parallelWriter(r, "test", "123", iterations, wait, done)
}
close(wait)
for i := 0; i < numReaders+numWriters; i++ {
<-done
}
}
}
func BenchmarkMutexSameReadWrite1(b *testing.B) {
benchmarkMutex(b, 1, 1, 32)
}
func BenchmarkMutexSameReadWrite2(b *testing.B) {
benchmarkMutex(b, 2, 2, 32)
}
func BenchmarkMutexSameReadWrite4(b *testing.B) {
benchmarkMutex(b, 4, 4, 32)
}
func BenchmarkMutex1(b *testing.B) {
benchmarkMutex(b, 2, 8, 32)
}
func BenchmarkMutex2(b *testing.B) {
benchmarkMutex(b, 16, 4, 64)
}
func BenchmarkMutex3(b *testing.B) {
benchmarkMutex(b, 1, 2, 128)
}
func BenchmarkMutex4(b *testing.B) {
benchmarkMutex(b, 128, 32, 256)
}
func BenchmarkMutex5(b *testing.B) {
benchmarkMutex(b, 1024, 2048, 64)
}
func BenchmarkMutex6(b *testing.B) {
benchmarkMutex(b, 2048, 1024, 512)
}
+7 -7
View File
@@ -1,6 +1,6 @@
mux
===
[![GoDoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie)
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
Package gorilla/mux implements a request router and dispatcher.
@@ -60,8 +60,8 @@ Routes can also be restricted to a domain or subdomain. Just define a host
pattern to be matched. They can also have variables:
r := mux.NewRouter()
// Only matches if domain is "www.domain.com".
r.Host("www.domain.com")
// Only matches if domain is "www.example.com".
r.Host("www.example.com")
// Matches a dynamic subdomain.
r.Host("{subdomain:[a-z]+}.domain.com")
@@ -94,7 +94,7 @@ There are several other matchers that can be added. To match path prefixes:
...and finally, it is possible to combine several matchers in a single route:
r.HandleFunc("/products", ProductsHandler).
Host("www.domain.com").
Host("www.example.com").
Methods("GET").
Schemes("http")
@@ -103,11 +103,11 @@ a way to group several routes that share the same requirements.
We call it "subrouting".
For example, let's say we have several URLs that should only match when the
host is "www.domain.com". Create a route for that host and get a "subrouter"
host is `www.example.com`. Create a route for that host and get a "subrouter"
from it:
r := mux.NewRouter()
s := r.Host("www.domain.com").Subrouter()
s := r.Host("www.example.com").Subrouter()
Then register routes in the subrouter:
@@ -116,7 +116,7 @@ Then register routes in the subrouter:
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
The three URL paths we registered above will only be tested if the domain is
"www.domain.com", because the subrouter is tested first. This is not
`www.example.com`, because the subrouter is tested first. This is not
only convenient, but also optimizes request matching. You can create
subrouters combining any attribute matchers accepted by a route.
-21
View File
@@ -1,21 +0,0 @@
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mux
import (
"net/http"
"testing"
)
func BenchmarkMux(b *testing.B) {
router := new(Router)
handler := func(w http.ResponseWriter, r *http.Request) {}
router.HandleFunc("/v1/{v1}", handler)
request, _ := http.NewRequest("GET", "/v1/anything", nil)
for i := 0; i < b.N; i++ {
router.ServeHTTP(nil, request)
}
}
+6 -6
View File
@@ -60,8 +60,8 @@ Routes can also be restricted to a domain or subdomain. Just define a host
pattern to be matched. They can also have variables:
r := mux.NewRouter()
// Only matches if domain is "www.domain.com".
r.Host("www.domain.com")
// Only matches if domain is "www.example.com".
r.Host("www.example.com")
// Matches a dynamic subdomain.
r.Host("{subdomain:[a-z]+}.domain.com")
@@ -94,7 +94,7 @@ There are several other matchers that can be added. To match path prefixes:
...and finally, it is possible to combine several matchers in a single route:
r.HandleFunc("/products", ProductsHandler).
Host("www.domain.com").
Host("www.example.com").
Methods("GET").
Schemes("http")
@@ -103,11 +103,11 @@ a way to group several routes that share the same requirements.
We call it "subrouting".
For example, let's say we have several URLs that should only match when the
host is "www.domain.com". Create a route for that host and get a "subrouter"
host is "www.example.com". Create a route for that host and get a "subrouter"
from it:
r := mux.NewRouter()
s := r.Host("www.domain.com").Subrouter()
s := r.Host("www.example.com").Subrouter()
Then register routes in the subrouter:
@@ -116,7 +116,7 @@ Then register routes in the subrouter:
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
The three URL paths we registered above will only be tested if the domain is
"www.domain.com", because the subrouter is tested first. This is not
"www.example.com", because the subrouter is tested first. This is not
only convenient, but also optimizes request matching. You can create
subrouters combining any attribute matchers accepted by a route.
-1334
View File
File diff suppressed because it is too large Load Diff
-714
View File
@@ -1,714 +0,0 @@
// Old tests ported to Go1. This is a mess. Want to drop it one day.
// Copyright 2011 Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mux
import (
"bytes"
"net/http"
"testing"
)
// ----------------------------------------------------------------------------
// ResponseRecorder
// ----------------------------------------------------------------------------
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
Code int // the HTTP response code from WriteHeader
HeaderMap http.Header // the HTTP response headers
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
Flushed bool
}
// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
HeaderMap: make(http.Header),
Body: new(bytes.Buffer),
}
}
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
const DefaultRemoteAddr = "1.2.3.4"
// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
return rw.HeaderMap
}
// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
if rw.Body != nil {
rw.Body.Write(buf)
}
if rw.Code == 0 {
rw.Code = http.StatusOK
}
return len(buf), nil
}
// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
rw.Code = code
}
// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
rw.Flushed = true
}
// ----------------------------------------------------------------------------
func TestRouteMatchers(t *testing.T) {
var scheme, host, path, query, method string
var headers map[string]string
var resultVars map[bool]map[string]string
router := NewRouter()
router.NewRoute().Host("{var1}.google.com").
Path("/{var2:[a-z]+}/{var3:[0-9]+}").
Queries("foo", "bar").
Methods("GET").
Schemes("https").
Headers("x-requested-with", "XMLHttpRequest")
router.NewRoute().Host("www.{var4}.com").
PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
Queries("baz", "ding").
Methods("POST").
Schemes("http").
Headers("Content-Type", "application/json")
reset := func() {
// Everything match.
scheme = "https"
host = "www.google.com"
path = "/product/42"
query = "?foo=bar"
method = "GET"
headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
resultVars = map[bool]map[string]string{
true: {"var1": "www", "var2": "product", "var3": "42"},
false: {},
}
}
reset2 := func() {
// Everything match.
scheme = "http"
host = "www.google.com"
path = "/foo/product/42/path/that/is/ignored"
query = "?baz=ding"
method = "POST"
headers = map[string]string{"Content-Type": "application/json"}
resultVars = map[bool]map[string]string{
true: {"var4": "google", "var5": "product", "var6": "42"},
false: {},
}
}
match := func(shouldMatch bool) {
url := scheme + "://" + host + path + query
request, _ := http.NewRequest(method, url, nil)
for key, value := range headers {
request.Header.Add(key, value)
}
var routeMatch RouteMatch
matched := router.Match(request, &routeMatch)
if matched != shouldMatch {
// Need better messages. :)
if matched {
t.Errorf("Should match.")
} else {
t.Errorf("Should not match.")
}
}
if matched {
currentRoute := routeMatch.Route
if currentRoute == nil {
t.Errorf("Expected a current route.")
}
vars := routeMatch.Vars
expectedVars := resultVars[shouldMatch]
if len(vars) != len(expectedVars) {
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
}
for name, value := range vars {
if expectedVars[name] != value {
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
}
}
}
}
// 1st route --------------------------------------------------------------
// Everything match.
reset()
match(true)
// Scheme doesn't match.
reset()
scheme = "http"
match(false)
// Host doesn't match.
reset()
host = "www.mygoogle.com"
match(false)
// Path doesn't match.
reset()
path = "/product/notdigits"
match(false)
// Query doesn't match.
reset()
query = "?foo=baz"
match(false)
// Method doesn't match.
reset()
method = "POST"
match(false)
// Header doesn't match.
reset()
headers = map[string]string{}
match(false)
// Everything match, again.
reset()
match(true)
// 2nd route --------------------------------------------------------------
// Everything match.
reset2()
match(true)
// Scheme doesn't match.
reset2()
scheme = "https"
match(false)
// Host doesn't match.
reset2()
host = "sub.google.com"
match(false)
// Path doesn't match.
reset2()
path = "/bar/product/42"
match(false)
// Query doesn't match.
reset2()
query = "?foo=baz"
match(false)
// Method doesn't match.
reset2()
method = "GET"
match(false)
// Header doesn't match.
reset2()
headers = map[string]string{}
match(false)
// Everything match, again.
reset2()
match(true)
}
type headerMatcherTest struct {
matcher headerMatcher
headers map[string]string
result bool
}
var headerMatcherTests = []headerMatcherTest{
{
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
result: true,
},
{
matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
headers: map[string]string{"X-Requested-With": "anything"},
result: true,
},
{
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
headers: map[string]string{},
result: false,
},
}
type hostMatcherTest struct {
matcher *Route
url string
vars map[string]string
result bool
}
var hostMatcherTests = []hostMatcherTest{
{
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
url: "http://abc.def.ghi/",
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
result: true,
},
{
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
url: "http://a.b.c/",
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
result: false,
},
}
type methodMatcherTest struct {
matcher methodMatcher
method string
result bool
}
var methodMatcherTests = []methodMatcherTest{
{
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
method: "GET",
result: true,
},
{
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
method: "POST",
result: true,
},
{
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
method: "PUT",
result: true,
},
{
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
method: "DELETE",
result: false,
},
}
type pathMatcherTest struct {
matcher *Route
url string
vars map[string]string
result bool
}
var pathMatcherTests = []pathMatcherTest{
{
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
url: "http://localhost:8080/123/456/789",
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
result: true,
},
{
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
url: "http://localhost:8080/1/2/3",
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
result: false,
},
}
type schemeMatcherTest struct {
matcher schemeMatcher
url string
result bool
}
var schemeMatcherTests = []schemeMatcherTest{
{
matcher: schemeMatcher([]string{"http", "https"}),
url: "http://localhost:8080/",
result: true,
},
{
matcher: schemeMatcher([]string{"http", "https"}),
url: "https://localhost:8080/",
result: true,
},
{
matcher: schemeMatcher([]string{"https"}),
url: "http://localhost:8080/",
result: false,
},
{
matcher: schemeMatcher([]string{"http"}),
url: "https://localhost:8080/",
result: false,
},
}
type urlBuildingTest struct {
route *Route
vars []string
url string
}
var urlBuildingTests = []urlBuildingTest{
{
route: new(Route).Host("foo.domain.com"),
vars: []string{},
url: "http://foo.domain.com",
},
{
route: new(Route).Host("{subdomain}.domain.com"),
vars: []string{"subdomain", "bar"},
url: "http://bar.domain.com",
},
{
route: new(Route).Host("foo.domain.com").Path("/articles"),
vars: []string{},
url: "http://foo.domain.com/articles",
},
{
route: new(Route).Path("/articles"),
vars: []string{},
url: "/articles",
},
{
route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
vars: []string{"category", "technology", "id", "42"},
url: "/articles/technology/42",
},
{
route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
url: "http://foo.domain.com/articles/technology/42",
},
}
func TestHeaderMatcher(t *testing.T) {
for _, v := range headerMatcherTests {
request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
for key, value := range v.headers {
request.Header.Add(key, value)
}
var routeMatch RouteMatch
result := v.matcher.Match(request, &routeMatch)
if result != v.result {
if v.result {
t.Errorf("%#v: should match %v.", v.matcher, request.Header)
} else {
t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
}
}
}
}
func TestHostMatcher(t *testing.T) {
for _, v := range hostMatcherTests {
request, _ := http.NewRequest("GET", v.url, nil)
var routeMatch RouteMatch
result := v.matcher.Match(request, &routeMatch)
vars := routeMatch.Vars
if result != v.result {
if v.result {
t.Errorf("%#v: should match %v.", v.matcher, v.url)
} else {
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
}
}
if result {
if len(vars) != len(v.vars) {
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
}
for name, value := range vars {
if v.vars[name] != value {
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
}
}
} else {
if len(vars) != 0 {
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
}
}
}
}
func TestMethodMatcher(t *testing.T) {
for _, v := range methodMatcherTests {
request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
var routeMatch RouteMatch
result := v.matcher.Match(request, &routeMatch)
if result != v.result {
if v.result {
t.Errorf("%#v: should match %v.", v.matcher, v.method)
} else {
t.Errorf("%#v: should not match %v.", v.matcher, v.method)
}
}
}
}
func TestPathMatcher(t *testing.T) {
for _, v := range pathMatcherTests {
request, _ := http.NewRequest("GET", v.url, nil)
var routeMatch RouteMatch
result := v.matcher.Match(request, &routeMatch)
vars := routeMatch.Vars
if result != v.result {
if v.result {
t.Errorf("%#v: should match %v.", v.matcher, v.url)
} else {
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
}
}
if result {
if len(vars) != len(v.vars) {
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
}
for name, value := range vars {
if v.vars[name] != value {
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
}
}
} else {
if len(vars) != 0 {
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
}
}
}
}
func TestSchemeMatcher(t *testing.T) {
for _, v := range schemeMatcherTests {
request, _ := http.NewRequest("GET", v.url, nil)
var routeMatch RouteMatch
result := v.matcher.Match(request, &routeMatch)
if result != v.result {
if v.result {
t.Errorf("%#v: should match %v.", v.matcher, v.url)
} else {
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
}
}
}
}
func TestUrlBuilding(t *testing.T) {
for _, v := range urlBuildingTests {
u, _ := v.route.URL(v.vars...)
url := u.String()
if url != v.url {
t.Errorf("expected %v, got %v", v.url, url)
/*
reversePath := ""
reverseHost := ""
if v.route.pathTemplate != nil {
reversePath = v.route.pathTemplate.Reverse
}
if v.route.hostTemplate != nil {
reverseHost = v.route.hostTemplate.Reverse
}
t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
*/
}
}
ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
}
router := NewRouter()
router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
url, _ := router.Get("article").URL("category", "technology", "id", "42")
expected := "/articles/technology/42"
if url.String() != expected {
t.Errorf("Expected %v, got %v", expected, url.String())
}
}
func TestMatchedRouteName(t *testing.T) {
routeName := "stock"
router := NewRouter()
route := router.NewRoute().Path("/products/").Name(routeName)
url := "http://www.domain.com/products/"
request, _ := http.NewRequest("GET", url, nil)
var rv RouteMatch
ok := router.Match(request, &rv)
if !ok || rv.Route != route {
t.Errorf("Expected same route, got %+v.", rv.Route)
}
retName := rv.Route.GetName()
if retName != routeName {
t.Errorf("Expected %q, got %q.", routeName, retName)
}
}
func TestSubRouting(t *testing.T) {
// Example from docs.
router := NewRouter()
subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
route := subrouter.NewRoute().Path("/products/").Name("products")
url := "http://www.domain.com/products/"
request, _ := http.NewRequest("GET", url, nil)
var rv RouteMatch
ok := router.Match(request, &rv)
if !ok || rv.Route != route {
t.Errorf("Expected same route, got %+v.", rv.Route)
}
u, _ := router.Get("products").URL()
builtUrl := u.String()
// Yay, subroute aware of the domain when building!
if builtUrl != url {
t.Errorf("Expected %q, got %q.", url, builtUrl)
}
}
func TestVariableNames(t *testing.T) {
route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
if route.err == nil {
t.Errorf("Expected error for duplicated variable names")
}
}
func TestRedirectSlash(t *testing.T) {
var route *Route
var routeMatch RouteMatch
r := NewRouter()
r.StrictSlash(false)
route = r.NewRoute()
if route.strictSlash != false {
t.Errorf("Expected false redirectSlash.")
}
r.StrictSlash(true)
route = r.NewRoute()
if route.strictSlash != true {
t.Errorf("Expected true redirectSlash.")
}
route = new(Route)
route.strictSlash = true
route.Path("/{arg1}/{arg2:[0-9]+}/")
request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
routeMatch = RouteMatch{}
_ = route.Match(request, &routeMatch)
vars := routeMatch.Vars
if vars["arg1"] != "foo" {
t.Errorf("Expected foo.")
}
if vars["arg2"] != "123" {
t.Errorf("Expected 123.")
}
rsp := NewRecorder()
routeMatch.Handler.ServeHTTP(rsp, request)
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
t.Errorf("Expected redirect header.")
}
route = new(Route)
route.strictSlash = true
route.Path("/{arg1}/{arg2:[0-9]+}")
request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
routeMatch = RouteMatch{}
_ = route.Match(request, &routeMatch)
vars = routeMatch.Vars
if vars["arg1"] != "foo" {
t.Errorf("Expected foo.")
}
if vars["arg2"] != "123" {
t.Errorf("Expected 123.")
}
rsp = NewRecorder()
routeMatch.Handler.ServeHTTP(rsp, request)
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
t.Errorf("Expected redirect header.")
}
}
// Test for the new regexp library, still not available in stable Go.
func TestNewRegexp(t *testing.T) {
var p *routeRegexp
var matches []string
tests := map[string]map[string][]string{
"/{foo:a{2}}": {
"/a": nil,
"/aa": {"aa"},
"/aaa": nil,
"/aaaa": nil,
},
"/{foo:a{2,}}": {
"/a": nil,
"/aa": {"aa"},
"/aaa": {"aaa"},
"/aaaa": {"aaaa"},
},
"/{foo:a{2,3}}": {
"/a": nil,
"/aa": {"aa"},
"/aaa": {"aaa"},
"/aaaa": nil,
},
"/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
"/a": nil,
"/ab": nil,
"/abc": nil,
"/abcd": nil,
"/abc/ab": {"abc", "ab"},
"/abc/abc": nil,
"/abcd/ab": nil,
},
`/{foo:\w{3,}}/{bar:\d{2,}}`: {
"/a": nil,
"/ab": nil,
"/abc": nil,
"/abc/1": nil,
"/abc/12": {"abc", "12"},
"/abcd/12": {"abcd", "12"},
"/abcd/123": {"abcd", "123"},
},
}
for pattern, paths := range tests {
p, _ = newRouteRegexp(pattern, false, false, false, false)
for path, result := range paths {
matches = p.regexp.FindStringSubmatch(path)
if result == nil {
if matches != nil {
t.Errorf("%v should not match %v.", pattern, path)
}
} else {
if len(matches) != len(result)+1 {
t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
} else {
for k, v := range result {
if matches[k+1] != v {
t.Errorf("Expected %v, got %v.", v, matches[k+1])
}
}
}
}
}
}
}
+2 -2
View File
@@ -255,7 +255,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route {
// For example:
//
// r := mux.NewRouter()
// r.Host("www.domain.com")
// r.Host("www.example.com")
// r.Host("{subdomain}.domain.com")
// r.Host("{subdomain:[a-z]+}.domain.com")
//
@@ -414,7 +414,7 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
// It will test the inner routes only if the parent route matched. For example:
//
// r := mux.NewRouter()
// s := r.Host("www.domain.com").Subrouter()
// s := r.Host("www.example.com").Subrouter()
// s.HandleFunc("/products/", ProductsHandler)
// s.HandleFunc("/products/{key}", ProductHandler)
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
-27
View File
@@ -1,27 +0,0 @@
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-6
View File
@@ -1,6 +0,0 @@
rpc
===
gorilla/rpc is a foundation for RPC over HTTP services, providing access to the exported methods of an object through HTTP requests.
Read the full documentation here: http://www.gorillatoolkit.org/pkg/rpc
-90
View File
@@ -1,90 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"compress/flate"
"compress/gzip"
"io"
"net/http"
"strings"
"unicode"
)
// gzipWriter writes and closes the gzip writer.
type gzipWriter struct {
w *gzip.Writer
}
func (gw *gzipWriter) Write(p []byte) (n int, err error) {
defer gw.w.Close()
return gw.w.Write(p)
}
// gzipEncoder implements the gzip compressed http encoder.
type gzipEncoder struct {
}
func (enc *gzipEncoder) Encode(w http.ResponseWriter) io.Writer {
w.Header().Set("Content-Encoding", "gzip")
return &gzipWriter{gzip.NewWriter(w)}
}
// flateWriter writes and closes the flate writer.
type flateWriter struct {
w *flate.Writer
}
func (fw *flateWriter) Write(p []byte) (n int, err error) {
defer fw.w.Close()
return fw.w.Write(p)
}
// flateEncoder implements the flate compressed http encoder.
type flateEncoder struct {
}
func (enc *flateEncoder) Encode(w http.ResponseWriter) io.Writer {
fw, err := flate.NewWriter(w, flate.DefaultCompression)
if err != nil {
return w
}
w.Header().Set("Content-Encoding", "deflate")
return &flateWriter{fw}
}
// CompressionSelector generates the compressed http encoder.
type CompressionSelector struct {
}
// acceptedEnc returns the first compression type in "Accept-Encoding" header
// field of the request.
func acceptedEnc(req *http.Request) string {
encHeader := req.Header.Get("Accept-Encoding")
if encHeader == "" {
return ""
}
encTypes := strings.FieldsFunc(encHeader, func(r rune) bool {
return unicode.IsSpace(r) || r == ','
})
for _, enc := range encTypes {
if enc == "gzip" || enc == "deflate" {
return enc
}
}
return ""
}
// Select method selects the correct compression encoder based on http HEADER.
func (_ *CompressionSelector) Select(r *http.Request) Encoder {
switch acceptedEnc(r) {
case "gzip":
return &gzipEncoder{}
case "flate":
return &flateEncoder{}
}
return DefaultEncoder
}
-81
View File
@@ -1,81 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package gorilla/rpc is a foundation for RPC over HTTP services, providing
access to the exported methods of an object through HTTP requests.
This package derives from the standard net/rpc package but uses a single HTTP
request per call instead of persistent connections. Other differences
compared to net/rpc:
- Multiple codecs can be registered in the same server.
- A codec is chosen based on the "Content-Type" header from the request.
- Service methods also receive http.Request as parameter.
- This package can be used on Google App Engine.
Let's setup a server and register a codec and service:
import (
"http"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json"
)
func init() {
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterService(new(HelloService), "")
http.Handle("/rpc", s)
}
This server handles requests to the "/rpc" path using a JSON codec.
A codec is tied to a content type. In the example above, the JSON codec is
registered to serve requests with "application/json" as the value for the
"Content-Type" header. If the header includes a charset definition, it is
ignored; only the media-type part is taken into account.
A service can be registered using a name. If the name is empty, like in the
example above, it will be inferred from the service type.
That's all about the server setup. Now let's define a simple service:
type HelloArgs struct {
Who string
}
type HelloReply struct {
Message string
}
type HelloService struct {}
func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
reply.Message = "Hello, " + args.Who + "!"
return nil
}
The example above defines a service with a method "HelloService.Say" and
the arguments and reply related to that method.
The service must be exported (begin with an upper case letter) or local
(defined in the package registering the service).
When a service is registered, the server inspects the service methods
and make available the ones that follow these rules:
- The method name is exported.
- The method has three arguments: *http.Request, *args, *reply.
- All three arguments are pointers.
- The second and third arguments are exported or local.
- The method has return type error.
All other methods are ignored.
Gorilla has packages with common RPC codecs. Check out their documentation:
JSON: http://gorilla-web.appspot.com/pkg/rpc/json
*/
package rpc
-43
View File
@@ -1,43 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"io"
"net/http"
)
// Encoder interface contains the encoder for http response.
// Eg. gzip, flate compressions.
type Encoder interface {
Encode(w http.ResponseWriter) io.Writer
}
type encoder struct {
}
func (_ *encoder) Encode(w http.ResponseWriter) io.Writer {
return w
}
var DefaultEncoder = &encoder{}
// EncoderSelector interface provides a way to select encoder using the http
// request. Typically people can use this to check HEADER of the request and
// figure out client capabilities.
// Eg. "Accept-Encoding" tells about supported compressions.
type EncoderSelector interface {
Select(r *http.Request) Encoder
}
type encoderSelector struct {
}
func (_ *encoderSelector) Select(_ *http.Request) Encoder {
return DefaultEncoder
}
var DefaultEncoderSelector = &encoderSelector{}
-61
View File
@@ -1,61 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012-2013 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"encoding/json"
"fmt"
"io"
"math/rand"
)
// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------
// clientRequest represents a JSON-RPC request sent by a client.
type clientRequest struct {
// A String containing the name of the method to be invoked.
Method string `json:"method"`
// Object to pass as request parameter to the method.
Params [1]interface{} `json:"params"`
// The request id. This can be of any type. It is used to match the
// response with the request that it is replying to.
Id uint64 `json:"id"`
}
// clientResponse represents a JSON-RPC response returned to a client.
type clientResponse struct {
Result *json.RawMessage `json:"result"`
Error interface{} `json:"error"`
Id uint64 `json:"id"`
}
// EncodeClientRequest encodes parameters for a JSON-RPC client request.
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
c := &clientRequest{
Method: method,
Params: [1]interface{}{args},
Id: uint64(rand.Int63()),
}
return json.Marshal(c)
}
// DecodeClientResponse decodes the response body of a client request into
// the interface reply.
func DecodeClientResponse(r io.Reader, reply interface{}) error {
var c clientResponse
if err := json.NewDecoder(r).Decode(&c); err != nil {
return err
}
if c.Error != nil {
return &Error{Data: c.Error}
}
if c.Result == nil {
return fmt.Errorf("Unexpected null result")
}
return json.Unmarshal(*c.Result, reply)
}
-58
View File
@@ -1,58 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package gorilla/rpc/json provides a codec for JSON-RPC over HTTP services.
To register the codec in a RPC server:
import (
"http"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json"
)
func init() {
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
// [...]
http.Handle("/rpc", s)
}
A codec is tied to a content type. In the example above, the server will use
the JSON codec for requests with "application/json" as the value for the
"Content-Type" header.
This package follows the JSON-RPC 1.0 specification:
http://json-rpc.org/wiki/specification
Request format is:
method:
The name of the method to be invoked, as a string in dotted notation
as in "Service.Method".
params:
An array with a single object to pass as argument to the method.
id:
The request id, a uint. It is used to match the response with the
request that it is replying to.
Response format is:
result:
The Object that was returned by the invoked method,
or null in case there was an error invoking the method.
error:
An Error object if there was an error invoking the method,
or null if there was no error.
id:
The same id as the request it is responding to.
Check the gorilla/rpc documentation for more details:
http://gorilla-web.appspot.com/pkg/rpc
*/
package json
-132
View File
@@ -1,132 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"github.com/gorilla/rpc/v2"
)
var (
ErrResponseError = errors.New("response error")
ErrResponseJsonError = &Error{Data: map[string]interface{}{
"stackstrace": map[string]interface{}{"0": "foo()"},
"error": "a message",
}}
)
type Service1Request struct {
A int
B int
}
type Service1Response struct {
Result int
}
type Service1 struct {
}
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
res.Result = req.A * req.B
return nil
}
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
return ErrResponseError
}
func (t *Service1) ResponseJsonError(r *http.Request, req *Service1Request, res *Service1Response) error {
return ErrResponseJsonError
}
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
if !s.HasMethod(method) {
t.Fatal("Expected to be registered:", method)
}
buf, _ := EncodeClientRequest(method, req)
body := bytes.NewBuffer(buf)
r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
s.ServeHTTP(w, r)
return DecodeClientResponse(w.Body, res)
}
func executeRaw(t *testing.T, s *rpc.Server, req json.RawMessage) (int, *bytes.Buffer) {
r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(req))
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
s.ServeHTTP(w, r)
return w.Code, w.Body
}
func field(name string, blob json.RawMessage) (v interface{}, ok bool) {
var obj map[string]interface{}
if err := json.Unmarshal(blob, &obj); err != nil {
return nil, false
}
v, ok = obj[name]
return
}
func TestService(t *testing.T) {
s := rpc.NewServer()
s.RegisterCodec(NewCodec(), "application/json")
s.RegisterService(new(Service1), "")
var res Service1Response
if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
t.Error("Expected err to be nil, but got", err)
}
if res.Result != 8 {
t.Error("Expected res.Result to be 8, but got", res.Result)
}
if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
t.Errorf("Expected to get %q, but got nil", ErrResponseError)
} else if err.Error() != ErrResponseError.Error() {
t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
}
if code, res := executeRaw(t, s, json.RawMessage(`{"method":"Service1.Multiply","params":null,"id":5}`)); code != 400 {
t.Error("Expected response code to be 400, but got", code)
} else if v, ok := field("result", res.Bytes()); !ok || v != nil {
t.Errorf("Expected ok to be true and v to be nil, but got %v and %v", ok, v)
}
if err := execute(t, s, "Service1.ResponseJsonError", &Service1Request{4, 2}, &res); err == nil {
t.Errorf("Expected to get %q, but got nil", ErrResponseError)
} else if jsonErr, ok := err.(*Error); !ok {
t.Error("Expected err to be of a *json.Error type")
} else if !reflect.DeepEqual(jsonErr.Data, ErrResponseJsonError.Data) {
t.Errorf("Expected jsonErr to be %q, but got %q", ErrResponseJsonError, jsonErr)
}
}
func TestClientNullResult(t *testing.T) {
data := `{"jsonrpc": "2.0", "id": 8674665223082153551, "result": null}`
reader := bytes.NewReader([]byte(data))
var reply interface{}
err := DecodeClientResponse(reader, &reply)
if err == nil {
t.Fatal(err)
}
if err.Error() != "Unexpected null result" {
t.Fatalf("Unexpected error: %s", err)
}
}
-155
View File
@@ -1,155 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/gorilla/rpc/v2"
)
var null = json.RawMessage([]byte("null"))
// An Error is a wrapper for a JSON interface value. It can be used by either
// a service's handler func to write more complex JSON data to an error field
// of a server's response, or by a client to read it.
type Error struct {
Data interface{}
}
func (e *Error) Error() string {
return fmt.Sprintf("%v", e.Data)
}
// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------
// serverRequest represents a JSON-RPC request received by the server.
type serverRequest struct {
// A String containing the name of the method to be invoked.
Method string `json:"method"`
// An Array of objects to pass as arguments to the method.
Params *json.RawMessage `json:"params"`
// The request id. This can be of any type. It is used to match the
// response with the request that it is replying to.
Id *json.RawMessage `json:"id"`
}
// serverResponse represents a JSON-RPC response returned by the server.
type serverResponse struct {
// The Object that was returned by the invoked method. This must be null
// in case there was an error invoking the method.
Result interface{} `json:"result"`
// An Error object if there was an error invoking the method. It must be
// null if there was no error.
Error interface{} `json:"error"`
// This must be the same id as the request it is responding to.
Id *json.RawMessage `json:"id"`
}
// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------
// NewCodec returns a new JSON Codec.
func NewCodec() *Codec {
return &Codec{}
}
// Codec creates a CodecRequest to process each request.
type Codec struct {
}
// NewRequest returns a CodecRequest.
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
return newCodecRequest(r)
}
// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------
// newCodecRequest returns a new CodecRequest.
func newCodecRequest(r *http.Request) rpc.CodecRequest {
// Decode the request body and check if RPC method is valid.
req := new(serverRequest)
err := json.NewDecoder(r.Body).Decode(req)
r.Body.Close()
return &CodecRequest{request: req, err: err}
}
// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
request *serverRequest
err error
}
// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
if c.err == nil {
return c.request.Method, nil
}
return "", c.err
}
// ReadRequest fills the request object for the RPC method.
func (c *CodecRequest) ReadRequest(args interface{}) error {
if c.err == nil {
if c.request.Params != nil {
// JSON params is array value. RPC params is struct.
// Unmarshal into array containing the request struct.
params := [1]interface{}{args}
c.err = json.Unmarshal(*c.request.Params, &params)
} else {
c.err = errors.New("rpc: method request ill-formed: missing params field")
}
}
return c.err
}
// WriteResponse encodes the response and writes it to the ResponseWriter.
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
if c.request.Id != nil {
// Id is null for notifications and they don't have a response.
res := &serverResponse{
Result: reply,
Error: &null,
Id: c.request.Id,
}
c.writeServerResponse(w, 200, res)
}
}
func (c *CodecRequest) WriteError(w http.ResponseWriter, _ int, err error) {
res := &serverResponse{
Result: &null,
Id: c.request.Id,
}
if jsonErr, ok := err.(*Error); ok {
res.Error = jsonErr.Data
} else {
res.Error = err.Error()
}
c.writeServerResponse(w, 400, res)
}
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, status int, res *serverResponse) {
b, err := json.Marshal(res)
if err == nil {
w.WriteHeader(status)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(b)
} else {
// Not sure in which case will this happen. But seems harmless.
rpc.WriteError(w, 400, err.Error())
}
}
-164
View File
@@ -1,164 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"fmt"
"net/http"
"reflect"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
var (
// Precompute the reflect.Type of error and http.Request
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
typeOfRequest = reflect.TypeOf((*http.Request)(nil)).Elem()
)
// ----------------------------------------------------------------------------
// service
// ----------------------------------------------------------------------------
type service struct {
name string // name of service
rcvr reflect.Value // receiver of methods for the service
rcvrType reflect.Type // type of the receiver
methods map[string]*serviceMethod // registered methods
}
type serviceMethod struct {
method reflect.Method // receiver method
argsType reflect.Type // type of the request argument
replyType reflect.Type // type of the response argument
}
// ----------------------------------------------------------------------------
// serviceMap
// ----------------------------------------------------------------------------
// serviceMap is a registry for services.
type serviceMap struct {
mutex sync.Mutex
services map[string]*service
}
// register adds a new service using reflection to extract its methods.
func (m *serviceMap) register(rcvr interface{}, name string) error {
// Setup service.
s := &service{
name: name,
rcvr: reflect.ValueOf(rcvr),
rcvrType: reflect.TypeOf(rcvr),
methods: make(map[string]*serviceMethod),
}
if name == "" {
s.name = reflect.Indirect(s.rcvr).Type().Name()
if !isExported(s.name) {
return fmt.Errorf("rpc: type %q is not exported", s.name)
}
}
if s.name == "" {
return fmt.Errorf("rpc: no service name for type %q",
s.rcvrType.String())
}
// Setup methods.
for i := 0; i < s.rcvrType.NumMethod(); i++ {
method := s.rcvrType.Method(i)
mtype := method.Type
// Method must be exported.
if method.PkgPath != "" {
continue
}
// Method needs four ins: receiver, *http.Request, *args, *reply.
if mtype.NumIn() != 4 {
continue
}
// First argument must be a pointer and must be http.Request.
reqType := mtype.In(1)
if reqType.Kind() != reflect.Ptr || reqType.Elem() != typeOfRequest {
continue
}
// Second argument must be a pointer and must be exported.
args := mtype.In(2)
if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
continue
}
// Third argument must be a pointer and must be exported.
reply := mtype.In(3)
if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
continue
}
// Method needs one out: error.
if mtype.NumOut() != 1 {
continue
}
if returnType := mtype.Out(0); returnType != typeOfError {
continue
}
s.methods[method.Name] = &serviceMethod{
method: method,
argsType: args.Elem(),
replyType: reply.Elem(),
}
}
if len(s.methods) == 0 {
return fmt.Errorf("rpc: %q has no exported methods of suitable type",
s.name)
}
// Add to the map.
m.mutex.Lock()
defer m.mutex.Unlock()
if m.services == nil {
m.services = make(map[string]*service)
} else if _, ok := m.services[s.name]; ok {
return fmt.Errorf("rpc: service already defined: %q", s.name)
}
m.services[s.name] = s
return nil
}
// get returns a registered service given a method name.
//
// The method name uses a dotted notation as in "Service.Method".
func (m *serviceMap) get(method string) (*service, *serviceMethod, error) {
parts := strings.Split(method, ".")
if len(parts) != 2 {
err := fmt.Errorf("rpc: service/method request ill-formed: %q", method)
return nil, nil, err
}
m.mutex.Lock()
service := m.services[parts[0]]
m.mutex.Unlock()
if service == nil {
err := fmt.Errorf("rpc: can't find service %q", method)
return nil, nil, err
}
serviceMethod := service.methods[parts[1]]
if serviceMethod == nil {
err := fmt.Errorf("rpc: can't find method %q", method)
return nil, nil, err
}
return service, serviceMethod, nil
}
// isExported returns true of a string is an exported (upper case) name.
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// isExportedOrBuiltin returns true if a type is exported or a builtin.
func isExportedOrBuiltin(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
-158
View File
@@ -1,158 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"fmt"
"net/http"
"reflect"
"strings"
)
// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------
// Codec creates a CodecRequest to process each request.
type Codec interface {
NewRequest(*http.Request) CodecRequest
}
// CodecRequest decodes a request and encodes a response using a specific
// serialization scheme.
type CodecRequest interface {
// Reads the request and returns the RPC method name.
Method() (string, error)
// Reads the request filling the RPC method args.
ReadRequest(interface{}) error
// Writes the response using the RPC method reply.
WriteResponse(http.ResponseWriter, interface{})
// Writes an error produced by the server.
WriteError(w http.ResponseWriter, status int, err error)
}
// ----------------------------------------------------------------------------
// Server
// ----------------------------------------------------------------------------
// NewServer returns a new RPC server.
func NewServer() *Server {
return &Server{
codecs: make(map[string]Codec),
services: new(serviceMap),
}
}
// Server serves registered RPC services using registered codecs.
type Server struct {
codecs map[string]Codec
services *serviceMap
}
// RegisterCodec adds a new codec to the server.
//
// Codecs are defined to process a given serialization scheme, e.g., JSON or
// XML. A codec is chosen based on the "Content-Type" header from the request,
// excluding the charset definition.
func (s *Server) RegisterCodec(codec Codec, contentType string) {
s.codecs[strings.ToLower(contentType)] = codec
}
// RegisterService adds a new service to the server.
//
// The name parameter is optional: if empty it will be inferred from
// the receiver type name.
//
// Methods from the receiver will be extracted if these rules are satisfied:
//
// - The receiver is exported (begins with an upper case letter) or local
// (defined in the package registering the service).
// - The method name is exported.
// - The method has three arguments: *http.Request, *args, *reply.
// - All three arguments are pointers.
// - The second and third arguments are exported or local.
// - The method has return type error.
//
// All other methods are ignored.
func (s *Server) RegisterService(receiver interface{}, name string) error {
return s.services.register(receiver, name)
}
// HasMethod returns true if the given method is registered.
//
// The method uses a dotted notation as in "Service.Method".
func (s *Server) HasMethod(method string) bool {
if _, _, err := s.services.get(method); err == nil {
return true
}
return false
}
// ServeHTTP
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
WriteError(w, 405, "rpc: POST method required, received "+r.Method)
return
}
contentType := r.Header.Get("Content-Type")
idx := strings.Index(contentType, ";")
if idx != -1 {
contentType = contentType[:idx]
}
codec := s.codecs[strings.ToLower(contentType)]
if codec == nil {
WriteError(w, 415, "rpc: unrecognized Content-Type: "+contentType)
return
}
// Create a new codec request.
codecReq := codec.NewRequest(r)
// Get service method to be called.
method, errMethod := codecReq.Method()
if errMethod != nil {
codecReq.WriteError(w, 400, errMethod)
return
}
serviceSpec, methodSpec, errGet := s.services.get(method)
if errGet != nil {
codecReq.WriteError(w, 400, errGet)
return
}
// Decode the args.
args := reflect.New(methodSpec.argsType)
if errRead := codecReq.ReadRequest(args.Interface()); errRead != nil {
codecReq.WriteError(w, 400, errRead)
return
}
// Call the service method.
reply := reflect.New(methodSpec.replyType)
errValue := methodSpec.method.Func.Call([]reflect.Value{
serviceSpec.rcvr,
reflect.ValueOf(r),
args,
reply,
})
// Cast the result to error if needed.
var errResult error
errInter := errValue[0].Interface()
if errInter != nil {
errResult = errInter.(error)
}
// Prevents Internet Explorer from MIME-sniffing a response away
// from the declared content-type
w.Header().Set("x-content-type-options", "nosniff")
// Encode the response.
if errResult == nil {
codecReq.WriteResponse(w, reply.Interface())
} else {
codecReq.WriteError(w, 400, errResult)
}
}
func WriteError(w http.ResponseWriter, status int, msg string) {
w.WriteHeader(status)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, msg)
}
-54
View File
@@ -1,54 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rpc
import (
"net/http"
"testing"
)
type Service1Request struct {
A int
B int
}
type Service1Response struct {
Result int
}
type Service1 struct {
}
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
res.Result = req.A * req.B
return nil
}
type Service2 struct {
}
func TestRegisterService(t *testing.T) {
var err error
s := NewServer()
service1 := new(Service1)
service2 := new(Service2)
// Inferred name.
err = s.RegisterService(service1, "")
if err != nil || !s.HasMethod("Service1.Multiply") {
t.Errorf("Expected to be registered: Service1.Multiply")
}
// Provided name.
err = s.RegisterService(service1, "Foo")
if err != nil || !s.HasMethod("Foo.Multiply") {
t.Errorf("Expected to be registered: Foo.Multiply")
}
// No methods.
err = s.RegisterService(service2, "")
if err == nil {
t.Errorf("Expected error on service2")
}
}
+7 -1
View File
@@ -1,6 +1,7 @@
package cli
import (
"errors"
"fmt"
"io"
"os"
@@ -18,8 +19,10 @@ import (
type App struct {
// The name of the program. Defaults to os.Args[0]
Name string
// Description of the program.
// Usage of the program.
Usage string
// Description of the program.
Description string
// Version of the program
Version string
// List of commands to execute
@@ -290,6 +293,9 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
if c != nil {
return c.Run(context)
}
fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
ShowSubcommandHelp(context)
return errors.New("Command not found")
}
// Run default Action
-587
View File
@@ -1,587 +0,0 @@
package cli_test
import (
"flag"
"fmt"
"os"
"testing"
"github.com/minio/cli"
)
func ExampleApp() {
// set args for examples sake
os.Args = []string{"greet", "--name", "Jeremy"}
app := cli.NewApp()
app.Name = "greet"
app.Flags = []cli.Flag{
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
}
app.Action = func(c *cli.Context) {
fmt.Printf("Hello %v\n", c.String("name"))
}
app.Author = "Harrison"
app.Email = "harrison@lolwut.com"
app.Authors = []cli.Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
app.Run(os.Args)
// Output:
// Hello Jeremy
}
func ExampleAppSubcommand() {
// set args for examples sake
os.Args = []string{"say", "hi", "english", "--name", "Jeremy"}
app := cli.NewApp()
app.Name = "say"
app.Commands = []cli.Command{
{
Name: "hello",
Aliases: []string{"hi"},
Usage: "use it to see a description",
Description: "This is how we describe hello the function",
Subcommands: []cli.Command{
{
Name: "english",
Aliases: []string{"en"},
Usage: "sends a greeting in english",
Description: "greets someone in english",
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Value: "Bob",
Usage: "Name of the person to greet",
},
},
Action: func(c *cli.Context) {
fmt.Println("Hello,", c.String("name"))
},
},
},
},
}
app.Run(os.Args)
// Output:
// Hello, Jeremy
}
func ExampleAppHelp() {
// set args for examples sake
os.Args = []string{"greet", "h", "describeit"}
app := cli.NewApp()
app.Name = "greet"
app.Flags = []cli.Flag{
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
}
app.Commands = []cli.Command{
{
Name: "describeit",
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
Action: func(c *cli.Context) {
fmt.Printf("i like to describe things")
},
},
}
app.Run(os.Args)
// Output:
// NAME:
// describeit - use it to see a description
//
// USAGE:
// command describeit [arguments...]
//
// DESCRIPTION:
// This is how we describe describeit the function
}
func TestApp_Run(t *testing.T) {
s := ""
app := cli.NewApp()
app.Action = func(c *cli.Context) {
s = s + c.Args().First()
}
err := app.Run([]string{"command", "foo"})
expect(t, err, nil)
err = app.Run([]string{"command", "bar"})
expect(t, err, nil)
expect(t, s, "foobar")
}
var commandAppTests = []struct {
name string
expected bool
}{
{"foobar", true},
{"batbaz", true},
{"b", true},
{"f", true},
{"bat", false},
{"nothing", false},
}
func TestApp_Command(t *testing.T) {
app := cli.NewApp()
fooCommand := cli.Command{Name: "foobar", Aliases: []string{"f"}}
batCommand := cli.Command{Name: "batbaz", Aliases: []string{"b"}}
app.Commands = []cli.Command{
fooCommand,
batCommand,
}
for _, test := range commandAppTests {
expect(t, app.Command(test.name) != nil, test.expected)
}
}
func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
var parsedOption, firstArg string
app := cli.NewApp()
command := cli.Command{
Name: "cmd",
Flags: []cli.Flag{
cli.StringFlag{Name: "option", Value: "", Usage: "some option"},
},
Action: func(c *cli.Context) {
parsedOption = c.String("option")
firstArg = c.Args().First()
},
}
app.Commands = []cli.Command{command}
app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"})
expect(t, parsedOption, "my-option")
expect(t, firstArg, "my-arg")
}
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
var context *cli.Context
a := cli.NewApp()
a.Commands = []cli.Command{
{
Name: "foo",
Action: func(c *cli.Context) {
context = c
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
},
Before: func(_ *cli.Context) error { return nil },
},
}
a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
expect(t, context.Args().Get(0), "abcd")
expect(t, context.String("lang"), "spanish")
}
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
var parsedOption string
var args []string
app := cli.NewApp()
command := cli.Command{
Name: "cmd",
Flags: []cli.Flag{
cli.StringFlag{Name: "option", Value: "", Usage: "some option"},
},
Action: func(c *cli.Context) {
parsedOption = c.String("option")
args = c.Args()
},
}
app.Commands = []cli.Command{command}
app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"})
expect(t, parsedOption, "my-option")
expect(t, args[0], "my-arg")
expect(t, args[1], "--")
expect(t, args[2], "--notARealFlag")
}
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
var args []string
app := cli.NewApp()
command := cli.Command{
Name: "cmd",
Action: func(c *cli.Context) {
args = c.Args()
},
}
app.Commands = []cli.Command{command}
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
expect(t, args[0], "my-arg")
expect(t, args[1], "--")
expect(t, args[2], "notAFlagAtAll")
}
func TestApp_Float64Flag(t *testing.T) {
var meters float64
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
}
app.Action = func(c *cli.Context) {
meters = c.Float64("height")
}
app.Run([]string{"", "--height", "1.93"})
expect(t, meters, 1.93)
}
func TestApp_ParseSliceFlags(t *testing.T) {
var parsedOption, firstArg string
var parsedIntSlice []int
var parsedStringSlice []string
app := cli.NewApp()
command := cli.Command{
Name: "cmd",
Flags: []cli.Flag{
cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"},
cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"},
},
Action: func(c *cli.Context) {
parsedIntSlice = c.IntSlice("p")
parsedStringSlice = c.StringSlice("ip")
parsedOption = c.String("option")
firstArg = c.Args().First()
},
}
app.Commands = []cli.Command{command}
app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"})
IntsEquals := func(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
StrsEquals := func(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
var expectedIntSlice = []int{22, 80}
var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"}
if !IntsEquals(parsedIntSlice, expectedIntSlice) {
t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice)
}
if !StrsEquals(parsedStringSlice, expectedStringSlice) {
t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice)
}
}
func TestApp_DefaultStdout(t *testing.T) {
app := cli.NewApp()
if app.Writer != os.Stdout {
t.Error("Default output writer not set.")
}
}
type mockWriter struct {
written []byte
}
func (fw *mockWriter) Write(p []byte) (n int, err error) {
if fw.written == nil {
fw.written = p
} else {
fw.written = append(fw.written, p...)
}
return len(p), nil
}
func (fw *mockWriter) GetWritten() (b []byte) {
return fw.written
}
func TestApp_SetStdout(t *testing.T) {
w := &mockWriter{}
app := cli.NewApp()
app.Name = "test"
app.Writer = w
err := app.Run([]string{"help"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if len(w.written) == 0 {
t.Error("App did not write output to desired writer.")
}
}
func TestApp_BeforeFunc(t *testing.T) {
beforeRun, subcommandRun := false, false
beforeError := fmt.Errorf("fail")
var err error
app := cli.NewApp()
app.Before = func(c *cli.Context) error {
beforeRun = true
s := c.String("opt")
if s == "fail" {
return beforeError
}
return nil
}
app.Commands = []cli.Command{
{
Name: "sub",
Action: func(c *cli.Context) {
subcommandRun = true
},
},
}
app.Flags = []cli.Flag{
cli.StringFlag{Name: "opt"},
}
// run with the Before() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if beforeRun == false {
t.Errorf("Before() not executed when expected")
}
if subcommandRun == false {
t.Errorf("Subcommand not executed when expected")
}
// reset
beforeRun, subcommandRun = false, false
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func
if err != beforeError {
t.Errorf("Run error expected, but not received")
}
if beforeRun == false {
t.Errorf("Before() not executed when expected")
}
if subcommandRun == true {
t.Errorf("Subcommand executed when NOT expected")
}
}
func TestApp_AfterFunc(t *testing.T) {
afterRun, subcommandRun := false, false
afterError := fmt.Errorf("fail")
var err error
app := cli.NewApp()
app.After = func(c *cli.Context) error {
afterRun = true
s := c.String("opt")
if s == "fail" {
return afterError
}
return nil
}
app.Commands = []cli.Command{
{
Name: "sub",
Action: func(c *cli.Context) {
subcommandRun = true
},
},
}
app.Flags = []cli.Flag{
cli.StringFlag{Name: "opt"},
}
// run with the After() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if afterRun == false {
t.Errorf("After() not executed when expected")
}
if subcommandRun == false {
t.Errorf("Subcommand not executed when expected")
}
// reset
afterRun, subcommandRun = false, false
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func
if err != afterError {
t.Errorf("Run error expected, but not received")
}
if afterRun == false {
t.Errorf("After() not executed when expected")
}
if subcommandRun == false {
t.Errorf("Subcommand not executed when expected")
}
}
func TestAppNoHelpFlag(t *testing.T) {
oldFlag := cli.HelpFlag
defer func() {
cli.HelpFlag = oldFlag
}()
cli.HelpFlag = cli.BoolFlag{}
app := cli.NewApp()
err := app.Run([]string{"test", "-h"})
if err != flag.ErrHelp {
t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err)
}
}
func TestAppHelpPrinter(t *testing.T) {
oldPrinter := cli.HelpPrinter
defer func() {
cli.HelpPrinter = oldPrinter
}()
var wasCalled = false
cli.HelpPrinter = func(template string, data interface{}) {
wasCalled = true
}
app := cli.NewApp()
app.Run([]string{"-h"})
if wasCalled == false {
t.Errorf("Help printer expected to be called, but was not")
}
}
func TestAppVersionPrinter(t *testing.T) {
oldPrinter := cli.VersionPrinter
defer func() {
cli.VersionPrinter = oldPrinter
}()
var wasCalled = false
cli.VersionPrinter = func(c *cli.Context) {
wasCalled = true
}
app := cli.NewApp()
ctx := cli.NewContext(app, nil, nil)
cli.ShowVersion(ctx)
if wasCalled == false {
t.Errorf("Version printer expected to be called, but was not")
}
}
func TestAppCommandNotFound(t *testing.T) {
beforeRun, subcommandRun := false, false
app := cli.NewApp()
app.CommandNotFound = func(c *cli.Context, command string) {
beforeRun = true
}
app.Commands = []cli.Command{
{
Name: "bar",
Action: func(c *cli.Context) {
subcommandRun = true
},
},
}
app.Run([]string{"command", "foo"})
expect(t, beforeRun, true)
expect(t, subcommandRun, false)
}
func TestGlobalFlagsInSubcommands(t *testing.T) {
subcommandRun := false
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
}
app.Commands = []cli.Command{
{
Name: "foo",
Subcommands: []cli.Command{
{
Name: "bar",
Action: func(c *cli.Context) {
if c.GlobalBool("debug") {
subcommandRun = true
}
},
},
},
},
}
app.Run([]string{"command", "-d", "foo", "bar"})
expect(t, subcommandRun, true)
}
-100
View File
@@ -1,100 +0,0 @@
package cli_test
import (
"os"
"github.com/minio/cli"
)
func Example() {
app := cli.NewApp()
app.Name = "todo"
app.Usage = "task list on the command line"
app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) {
println("added task: ", c.Args().First())
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())
},
},
}
app.Run(os.Args)
}
func ExampleSubcommand() {
app := cli.NewApp()
app.Name = "say"
app.Commands = []cli.Command{
{
Name: "hello",
Aliases: []string{"hi"},
Usage: "use it to see a description",
Description: "This is how we describe hello the function",
Subcommands: []cli.Command{
{
Name: "english",
Aliases: []string{"en"},
Usage: "sends a greeting in english",
Description: "greets someone in english",
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Value: "Bob",
Usage: "Name of the person to greet",
},
},
Action: func(c *cli.Context) {
println("Hello, ", c.String("name"))
},
}, {
Name: "spanish",
Aliases: []string{"sp"},
Usage: "sends a greeting in spanish",
Flags: []cli.Flag{
cli.StringFlag{
Name: "surname",
Value: "Jones",
Usage: "Surname of the person to greet",
},
},
Action: func(c *cli.Context) {
println("Hola, ", c.String("surname"))
},
}, {
Name: "french",
Aliases: []string{"fr"},
Usage: "sends a greeting in french",
Flags: []cli.Flag{
cli.StringFlag{
Name: "nickname",
Value: "Stevie",
Usage: "Nickname of the person to greet",
},
},
Action: func(c *cli.Context) {
println("Bonjour, ", c.String("nickname"))
},
},
},
}, {
Name: "bye",
Usage: "says goodbye",
Action: func(c *cli.Context) {
println("bye")
},
},
}
app.Run(os.Args)
}
+3
View File
@@ -163,6 +163,9 @@ func (c Command) startApp(ctx *Context) error {
// set CommandNotFound
app.CommandNotFound = ctx.App.CommandNotFound
app.CustomAppHelpTemplate = c.CustomHelpTemplate
app.HideVersion = ctx.App.HideVersion
// set the flags and commands
app.Commands = c.Subcommands
app.Flags = c.Flags
-49
View File
@@ -1,49 +0,0 @@
package cli_test
import (
"flag"
"testing"
"github.com/minio/cli"
)
func TestCommandDoNotIgnoreFlags(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
test := []string{"blah", "blah", "-break"}
set.Parse(test)
c := cli.NewContext(app, set, set)
command := cli.Command{
Name: "test-cmd",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(_ *cli.Context) {},
}
err := command.Run(c)
expect(t, err.Error(), "flag provided but not defined: -break")
}
func TestCommandIgnoreFlags(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
test := []string{"blah", "blah"}
set.Parse(test)
c := cli.NewContext(app, set, set)
command := cli.Command{
Name: "test-cmd",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(_ *cli.Context) {},
SkipFlagParsing: true,
}
err := command.Run(c)
expect(t, err, nil)
}
-111
View File
@@ -1,111 +0,0 @@
package cli_test
import (
"flag"
"testing"
"time"
"github.com/minio/cli"
)
func TestNewContext(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Int("myflag", 42, "doc")
command := cli.Command{Name: "mycommand"}
c := cli.NewContext(nil, set, globalSet)
c.Command = command
expect(t, c.Int("myflag"), 12)
expect(t, c.GlobalInt("myflag"), 42)
expect(t, c.Command.Name, "mycommand")
}
func TestContext_Int(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc")
c := cli.NewContext(nil, set, set)
expect(t, c.Int("myflag"), 12)
}
func TestContext_Duration(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Duration("myflag", time.Duration(12*time.Second), "doc")
c := cli.NewContext(nil, set, set)
expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
}
func TestContext_String(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.String("myflag", "hello world", "doc")
c := cli.NewContext(nil, set, set)
expect(t, c.String("myflag"), "hello world")
}
func TestContext_Bool(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
c := cli.NewContext(nil, set, set)
expect(t, c.Bool("myflag"), false)
}
func TestContext_BoolT(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", true, "doc")
c := cli.NewContext(nil, set, set)
expect(t, c.BoolT("myflag"), true)
}
func TestContext_Args(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
c := cli.NewContext(nil, set, set)
set.Parse([]string{"--myflag", "bat", "baz"})
expect(t, len(c.Args()), 2)
expect(t, c.Bool("myflag"), true)
}
func TestContext_IsSet(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc")
c := cli.NewContext(nil, set, globalSet)
set.Parse([]string{"--myflag", "bat", "baz"})
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
expect(t, c.IsSet("myflag"), true)
expect(t, c.IsSet("otherflag"), false)
expect(t, c.IsSet("bogusflag"), false)
expect(t, c.IsSet("myflagGlobal"), false)
}
func TestContext_GlobalIsSet(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc")
globalSet.Bool("myflagGlobalUnset", true, "doc")
c := cli.NewContext(nil, set, globalSet)
set.Parse([]string{"--myflag", "bat", "baz"})
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
expect(t, c.GlobalIsSet("myflag"), false)
expect(t, c.GlobalIsSet("otherflag"), false)
expect(t, c.GlobalIsSet("bogusflag"), false)
expect(t, c.GlobalIsSet("myflagGlobal"), true)
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
expect(t, c.GlobalIsSet("bogusGlobal"), false)
}
func TestContext_NumFlags(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc")
set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc")
c := cli.NewContext(nil, set, globalSet)
set.Parse([]string{"--myflag", "--otherflag=foo"})
globalSet.Parse([]string{"--myflagGlobal"})
expect(t, c.NumFlags(), 2)
}
-742
View File
@@ -1,742 +0,0 @@
package cli_test
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
"github.com/minio/cli"
)
var boolFlagTests = []struct {
name string
expected string
}{
{"help", "--help\t"},
{"h", "-h\t"},
}
func TestBoolFlagHelpOutput(t *testing.T) {
for _, test := range boolFlagTests {
flag := cli.BoolFlag{Name: test.name}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
var stringFlagTests = []struct {
name string
value string
expected string
}{
{"help", "", "--help \t"},
{"h", "", "-h \t"},
{"h", "", "-h \t"},
{"test", "Something", "--test \"Something\"\t"},
}
func TestStringFlagHelpOutput(t *testing.T) {
for _, test := range stringFlagTests {
flag := cli.StringFlag{Name: test.name, Value: test.value}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_FOO", "derp")
for _, test := range stringFlagTests {
flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_FOO]") {
t.Errorf("%s does not end with [$APP_FOO]", output)
}
}
}
var stringSliceFlagTests = []struct {
name string
value *cli.StringSlice
expected string
}{
{"help", func() *cli.StringSlice {
s := &cli.StringSlice{}
s.Set("")
return s
}(), "--help [--help option --help option]\t"},
{"h", func() *cli.StringSlice {
s := &cli.StringSlice{}
s.Set("")
return s
}(), "-h [-h option -h option]\t"},
{"h", func() *cli.StringSlice {
s := &cli.StringSlice{}
s.Set("")
return s
}(), "-h [-h option -h option]\t"},
{"test", func() *cli.StringSlice {
s := &cli.StringSlice{}
s.Set("Something")
return s
}(), "--test [--test option --test option]\t"},
}
func TestStringSliceFlagHelpOutput(t *testing.T) {
for _, test := range stringSliceFlagTests {
flag := cli.StringSliceFlag{Name: test.name, Value: test.value}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_QWWX", "11,4")
for _, test := range stringSliceFlagTests {
flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_QWWX]") {
t.Errorf("%q does not end with [$APP_QWWX]", output)
}
}
}
var intFlagTests = []struct {
name string
expected string
}{
{"help", "--help \"0\"\t"},
{"h", "-h \"0\"\t"},
}
func TestIntFlagHelpOutput(t *testing.T) {
for _, test := range intFlagTests {
flag := cli.IntFlag{Name: test.name}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAR", "2")
for _, test := range intFlagTests {
flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_BAR]") {
t.Errorf("%s does not end with [$APP_BAR]", output)
}
}
}
var durationFlagTests = []struct {
name string
expected string
}{
{"help", "--help \"0\"\t"},
{"h", "-h \"0\"\t"},
}
func TestDurationFlagHelpOutput(t *testing.T) {
for _, test := range durationFlagTests {
flag := cli.DurationFlag{Name: test.name}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAR", "2h3m6s")
for _, test := range durationFlagTests {
flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_BAR]") {
t.Errorf("%s does not end with [$APP_BAR]", output)
}
}
}
var intSliceFlagTests = []struct {
name string
value *cli.IntSlice
expected string
}{
{"help", &cli.IntSlice{}, "--help [--help option --help option]\t"},
{"h", &cli.IntSlice{}, "-h [-h option -h option]\t"},
{"h", &cli.IntSlice{}, "-h [-h option -h option]\t"},
{"test", func() *cli.IntSlice {
i := &cli.IntSlice{}
i.Set("9")
return i
}(), "--test [--test option --test option]\t"},
}
func TestIntSliceFlagHelpOutput(t *testing.T) {
for _, test := range intSliceFlagTests {
flag := cli.IntSliceFlag{Name: test.name, Value: test.value}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_SMURF", "42,3")
for _, test := range intSliceFlagTests {
flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_SMURF]") {
t.Errorf("%q does not end with [$APP_SMURF]", output)
}
}
}
var float64FlagTests = []struct {
name string
expected string
}{
{"help", "--help \"0\"\t"},
{"h", "-h \"0\"\t"},
}
func TestFloat64FlagHelpOutput(t *testing.T) {
for _, test := range float64FlagTests {
flag := cli.Float64Flag{Name: test.name}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAZ", "99.4")
for _, test := range float64FlagTests {
flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_BAZ]") {
t.Errorf("%s does not end with [$APP_BAZ]", output)
}
}
}
var genericFlagTests = []struct {
name string
value cli.Generic
expected string
}{
{"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"},
{"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"},
}
func TestGenericFlagHelpOutput(t *testing.T) {
for _, test := range genericFlagTests {
flag := cli.GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_ZAP", "3")
for _, test := range genericFlagTests {
flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
output := flag.String()
if !strings.HasSuffix(output, " [$APP_ZAP]") {
t.Errorf("%s does not end with [$APP_ZAP]", output)
}
}
}
func TestParseMultiString(t *testing.T) {
(&cli.App{
Flags: []cli.Flag{
cli.StringFlag{Name: "serve, s"},
},
Action: func(ctx *cli.Context) {
if ctx.String("serve") != "10" {
t.Errorf("main name not set")
}
if ctx.String("s") != "10" {
t.Errorf("short name not set")
}
},
}).Run([]string{"run", "-s", "10"})
}
func TestParseMultiStringFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_COUNT", "20")
(&cli.App{
Flags: []cli.Flag{
cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
},
Action: func(ctx *cli.Context) {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
},
}).Run([]string{"run"})
}
func TestParseMultiStringFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_COUNT", "20")
(&cli.App{
Flags: []cli.Flag{
cli.StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
},
Action: func(ctx *cli.Context) {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
},
}).Run([]string{"run"})
}
func TestParseMultiStringSlice(t *testing.T) {
(&cli.App{
Flags: []cli.Flag{
cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
t.Errorf("short name not set")
}
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiStringSliceFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,40")
(&cli.App{
Flags: []cli.Flag{
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,40")
(&cli.App{
Flags: []cli.Flag{
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
},
}).Run([]string{"run"})
}
func TestParseMultiInt(t *testing.T) {
a := cli.App{
Flags: []cli.Flag{
cli.IntFlag{Name: "serve, s"},
},
Action: func(ctx *cli.Context) {
if ctx.Int("serve") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("s") != 10 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run", "-s", "10"})
}
func TestParseMultiIntFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_TIMEOUT_SECONDS", "10")
a := cli.App{
Flags: []cli.Flag{
cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *cli.Context) {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiIntFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_TIMEOUT_SECONDS", "10")
a := cli.App{
Flags: []cli.Flag{
cli.IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *cli.Context) {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiIntSlice(t *testing.T) {
(&cli.App{
Flags: []cli.Flag{
cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set")
}
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiIntSliceFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,40")
(&cli.App{
Flags: []cli.Flag{
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
},
}).Run([]string{"run"})
}
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,40")
(&cli.App{
Flags: []cli.Flag{
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
},
}).Run([]string{"run"})
}
func TestParseMultiFloat64(t *testing.T) {
a := cli.App{
Flags: []cli.Flag{
cli.Float64Flag{Name: "serve, s"},
},
Action: func(ctx *cli.Context) {
if ctx.Float64("serve") != 10.2 {
t.Errorf("main name not set")
}
if ctx.Float64("s") != 10.2 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run", "-s", "10.2"})
}
func TestParseMultiFloat64FromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
a := cli.App{
Flags: []cli.Flag{
cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *cli.Context) {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
a := cli.App{
Flags: []cli.Flag{
cli.Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *cli.Context) {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiBool(t *testing.T) {
a := cli.App{
Flags: []cli.Flag{
cli.BoolFlag{Name: "serve, s"},
},
Action: func(ctx *cli.Context) {
if ctx.Bool("serve") != true {
t.Errorf("main name not set")
}
if ctx.Bool("s") != true {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run", "--serve"})
}
func TestParseMultiBoolFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_DEBUG", "1")
a := cli.App{
Flags: []cli.Flag{
cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
Action: func(ctx *cli.Context) {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_DEBUG", "1")
a := cli.App{
Flags: []cli.Flag{
cli.BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
Action: func(ctx *cli.Context) {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiBoolT(t *testing.T) {
a := cli.App{
Flags: []cli.Flag{
cli.BoolTFlag{Name: "serve, s"},
},
Action: func(ctx *cli.Context) {
if ctx.BoolT("serve") != true {
t.Errorf("main name not set")
}
if ctx.BoolT("s") != true {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run", "--serve"})
}
func TestParseMultiBoolTFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_DEBUG", "0")
a := cli.App{
Flags: []cli.Flag{
cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
Action: func(ctx *cli.Context) {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
},
}
a.Run([]string{"run"})
}
func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_DEBUG", "0")
a := cli.App{
Flags: []cli.Flag{
cli.BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
Action: func(ctx *cli.Context) {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
},
}
a.Run([]string{"run"})
}
type Parser [2]string
func (p *Parser) Set(value string) error {
parts := strings.Split(value, ",")
if len(parts) != 2 {
return fmt.Errorf("invalid format")
}
(*p)[0] = parts[0]
(*p)[1] = parts[1]
return nil
}
func (p *Parser) String() string {
return fmt.Sprintf("%s,%s", p[0], p[1])
}
func TestParseGeneric(t *testing.T) {
a := cli.App{
Flags: []cli.Flag{
cli.GenericFlag{Name: "serve, s", Value: &Parser{}},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
t.Errorf("short name not set")
}
},
}
a.Run([]string{"run", "-s", "10,20"})
}
func TestParseGenericFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_SERVE", "20,30")
a := cli.App{
Flags: []cli.Flag{
cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
t.Errorf("short name not set from env")
}
},
}
a.Run([]string{"run"})
}
func TestParseGenericFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_FOO", "99,2000")
a := cli.App{
Flags: []cli.Flag{
cli.GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
},
Action: func(ctx *cli.Context) {
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
t.Errorf("value not set from env")
}
},
}
a.Run([]string{"run"})
}
-19
View File
@@ -1,19 +0,0 @@
package cli_test
import (
"reflect"
"testing"
)
/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
func refute(t *testing.T, a interface{}, b interface{}) {
if a == b {
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
+77
View File
@@ -0,0 +1,77 @@
/*
* Minio Client (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// NOTE - Rename() not guaranteed to be atomic on all filesystems which are not fully POSIX compatible
// Package atomic provides atomic file write semantics by leveraging Rename's() atomicity.
package atomic
import (
"io/ioutil"
"os"
"path/filepath"
)
// File container provided for atomic file writes
type File struct {
*os.File
file string
}
// Close the file replacing, returns an error if any
func (f *File) Close() error {
// close the embedded fd
if err := f.File.Close(); err != nil {
return err
}
// atomic rename to final destination
if err := os.Rename(f.Name(), f.file); err != nil {
return err
}
return nil
}
// CloseAndPurge removes the temp file, closes the transaction and returns an error if any
func (f *File) CloseAndPurge() error {
// close the embedded fd
if err := f.File.Close(); err != nil {
return err
}
if err := os.Remove(f.Name()); err != nil {
return err
}
return nil
}
// FileCreate creates a new file at filePath for atomic writes, it also creates parent directories if they don't exist
func FileCreate(filePath string) (*File, error) {
// if parent directories do not exist, ioutil.TempFile doesn't create them
// handle such a case with os.MkdirAll()
if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil {
return nil, err
}
f, err := ioutil.TempFile(filepath.Dir(filePath), filepath.Base(filePath))
if err != nil {
return nil, err
}
if err := os.Chmod(f.Name(), 0600); err != nil {
if err := os.Remove(f.Name()); err != nil {
return nil, err
}
return nil, err
}
return &File{File: f, file: filePath}, nil
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Minio Client (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package atomic
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
. "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
type MySuite struct {
root string
}
var _ = Suite(&MySuite{})
func (s *MySuite) SetUpSuite(c *C) {
root, err := ioutil.TempDir("/tmp", "atomic-")
c.Assert(err, IsNil)
s.root = root
}
func (s *MySuite) TearDownSuite(c *C) {
os.RemoveAll(s.root)
}
func (s *MySuite) TestAtomic(c *C) {
f, err := FileCreate(filepath.Join(s.root, "testfile"))
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.root, "testfile"))
c.Assert(err, Not(IsNil))
err = f.Close()
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.root, "testfile"))
c.Assert(err, IsNil)
}
func (s *MySuite) TestAtomicPurge(c *C) {
f, err := FileCreate(filepath.Join(s.root, "purgefile"))
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.root, "purgefile"))
c.Assert(err, Not(IsNil))
err = f.CloseAndPurge()
c.Assert(err, IsNil)
err = f.Close()
c.Assert(err, Not(IsNil))
}
+39
View File
@@ -0,0 +1,39 @@
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cpu
// cpuid, cpuidex
func cpuid(op uint32) (eax, ebx, ecx, edx uint32)
func cpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
// HasSSE41 - CPUID instruction verification wrapper for SSE41 extensions
func HasSSE41() bool {
_, _, c, _ := cpuid(1)
return ((c & (1 << 19)) != 0)
}
// HasAVX - CPUID instruction verification wrapper for AVX extensions
func HasAVX() bool {
_, _, c, _ := cpuid(1)
return ((c & (1 << 28)) != 0)
}
// HasAVX2 - CPUID instruction verification wrapper for AVX2 extensions
func HasAVX2() bool {
_, b, _, _ := cpuidex(7, 0)
return ((b & (1 << 5)) != 0)
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
//
// See https://github.com/klauspost/cpuid/blob/master/LICENSE
//
// Using this inside Minio with modifications
//
// func cpuid(op uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB),7,$0
MOVL op+0(FP),AX
CPUID
MOVL AX,eax+8(FP)
MOVL BX,ebx+12(FP)
MOVL CX,ecx+16(FP)
MOVL DX,edx+20(FP)
RET
// func cpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuidex(SB),7,$0
MOVL op+0(FP),AX
MOVL op2+4(FP),CX
CPUID
MOVL AX,eax+8(FP)
MOVL BX,ebx+12(FP)
MOVL CX,ecx+16(FP)
MOVL DX,edx+20(FP)
RET
+76
View File
@@ -0,0 +1,76 @@
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cpu_test
import (
"errors"
"os/exec"
"runtime"
"strings"
"testing"
"github.com/minio/minio-xl/pkg/cpu"
. "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
type MySuite struct{}
var _ = Suite(&MySuite{})
func hasCPUFeatureFromOS(feature string) (bool, error) {
if runtime.GOOS == "linux" {
command := exec.Command("/bin/cat", "/proc/cpuinfo")
output, err := command.Output()
if err != nil {
return false, err
}
if strings.Contains(string(output), feature) {
return true, nil
}
return false, nil
}
return false, errors.New("Not Implemented on this platform")
}
func (s *MySuite) TestHasSSE41(c *C) {
if runtime.GOOS == "linux" {
var flag = cpu.HasSSE41()
osCheck, err := hasCPUFeatureFromOS("sse4_1")
c.Assert(err, IsNil)
c.Check(flag, Equals, osCheck)
}
}
func (s *MySuite) TestHasAVX(c *C) {
if runtime.GOOS == "linux" {
var flag = cpu.HasAVX()
osFlag, err := hasCPUFeatureFromOS("avx")
c.Assert(err, IsNil)
c.Check(osFlag, Equals, flag)
}
}
func (s *MySuite) TestHasAVX2(c *C) {
if runtime.GOOS == "linux" {
var flag = cpu.HasAVX2()
osFlag, err := hasCPUFeatureFromOS("avx2")
c.Assert(err, IsNil)
c.Check(osFlag, Equals, flag)
}
}
+11
View File
@@ -0,0 +1,11 @@
// Package cpu provides wrapper around assembly functions for checking processor
// instruction capabilities for SSE4.1, AVX, AVX2 support
//
// Example
//
// ``cpu.HasSSE41()`` returns true for SSE4.1 instruction support, false otherwise
//
// ``cpu.HasAVX()`` returns true for AVX instruction support, false otherwise
//
// ``cpu.HasAVX2()`` returns true for AVX2 instruction support, false otherwise
package cpu
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,759 @@
########################################################################
# Implement fast SHA-256 with AVX1 instructions. (x86_64)
#
# Copyright (C) 2013 Intel Corporation.
#
# Authors:
# James Guilford <james.guilford@intel.com>
# Kirk Yap <kirk.s.yap@intel.com>
# Tim Chen <tim.c.chen@linux.intel.com>
#
# This software is available to you under a choice of one of two
# licenses. You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# - Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# - Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 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.
########################################################################
#
# This code is described in an Intel White-Paper:
# "Fast SHA-256 Implementations on Intel Architecture Processors"
#
# To find it, surf to http://www.intel.com/p/en_US/embedded
# and search for that title.
#
########################################################################
# This code schedules 1 block at a time, with 4 lanes per block
########################################################################
# Using this part of Minio codebase under the license
# Apache License Version 2.0 with modifications
##
#ifdef HAS_AVX
#ifndef ENTRY
#define ENTRY(name) \
.globl name ; \
.align 4,0x90 ; \
name:
#endif
#ifndef END
#define END(name) \
.size name, .-name
#endif
#ifndef ENDPROC
#define ENDPROC(name) \
.type name, @function ; \
END(name)
#endif
#define NUM_INVALID 100
#define TYPE_R32 0
#define TYPE_R64 1
#define TYPE_XMM 2
#define TYPE_INVALID 100
.macro R32_NUM opd r32
\opd = NUM_INVALID
.ifc \r32,%eax
\opd = 0
.endif
.ifc \r32,%ecx
\opd = 1
.endif
.ifc \r32,%edx
\opd = 2
.endif
.ifc \r32,%ebx
\opd = 3
.endif
.ifc \r32,%esp
\opd = 4
.endif
.ifc \r32,%ebp
\opd = 5
.endif
.ifc \r32,%esi
\opd = 6
.endif
.ifc \r32,%edi
\opd = 7
.endif
#ifdef X86_64
.ifc \r32,%r8d
\opd = 8
.endif
.ifc \r32,%r9d
\opd = 9
.endif
.ifc \r32,%r10d
\opd = 10
.endif
.ifc \r32,%r11d
\opd = 11
.endif
.ifc \r32,%r12d
\opd = 12
.endif
.ifc \r32,%r13d
\opd = 13
.endif
.ifc \r32,%r14d
\opd = 14
.endif
.ifc \r32,%r15d
\opd = 15
.endif
#endif
.endm
.macro R64_NUM opd r64
\opd = NUM_INVALID
#ifdef X86_64
.ifc \r64,%rax
\opd = 0
.endif
.ifc \r64,%rcx
\opd = 1
.endif
.ifc \r64,%rdx
\opd = 2
.endif
.ifc \r64,%rbx
\opd = 3
.endif
.ifc \r64,%rsp
\opd = 4
.endif
.ifc \r64,%rbp
\opd = 5
.endif
.ifc \r64,%rsi
\opd = 6
.endif
.ifc \r64,%rdi
\opd = 7
.endif
.ifc \r64,%r8
\opd = 8
.endif
.ifc \r64,%r9
\opd = 9
.endif
.ifc \r64,%r10
\opd = 10
.endif
.ifc \r64,%r11
\opd = 11
.endif
.ifc \r64,%r12
\opd = 12
.endif
.ifc \r64,%r13
\opd = 13
.endif
.ifc \r64,%r14
\opd = 14
.endif
.ifc \r64,%r15
\opd = 15
.endif
#endif
.endm
.macro XMM_NUM opd xmm
\opd = NUM_INVALID
.ifc \xmm,%xmm0
\opd = 0
.endif
.ifc \xmm,%xmm1
\opd = 1
.endif
.ifc \xmm,%xmm2
\opd = 2
.endif
.ifc \xmm,%xmm3
\opd = 3
.endif
.ifc \xmm,%xmm4
\opd = 4
.endif
.ifc \xmm,%xmm5
\opd = 5
.endif
.ifc \xmm,%xmm6
\opd = 6
.endif
.ifc \xmm,%xmm7
\opd = 7
.endif
.ifc \xmm,%xmm8
\opd = 8
.endif
.ifc \xmm,%xmm9
\opd = 9
.endif
.ifc \xmm,%xmm10
\opd = 10
.endif
.ifc \xmm,%xmm11
\opd = 11
.endif
.ifc \xmm,%xmm12
\opd = 12
.endif
.ifc \xmm,%xmm13
\opd = 13
.endif
.ifc \xmm,%xmm14
\opd = 14
.endif
.ifc \xmm,%xmm15
\opd = 15
.endif
.endm
.macro TYPE type reg
R32_NUM reg_type_r32 \reg
R64_NUM reg_type_r64 \reg
XMM_NUM reg_type_xmm \reg
.if reg_type_r64 <> NUM_INVALID
\type = TYPE_R64
.elseif reg_type_r32 <> NUM_INVALID
\type = TYPE_R32
.elseif reg_type_xmm <> NUM_INVALID
\type = TYPE_XMM
.else
\type = TYPE_INVALID
.endif
.endm
.macro PFX_OPD_SIZE
.byte 0x66
.endm
.macro PFX_REX opd1 opd2 W=0
.if ((\opd1 | \opd2) & 8) || \W
.byte 0x40 | ((\opd1 & 8) >> 3) | ((\opd2 & 8) >> 1) | (\W << 3)
.endif
.endm
.macro MODRM mod opd1 opd2
.byte \mod | (\opd1 & 7) | ((\opd2 & 7) << 3)
.endm
.macro PSHUFB_XMM xmm1 xmm2
XMM_NUM pshufb_opd1 \xmm1
XMM_NUM pshufb_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX pshufb_opd1 pshufb_opd2
.byte 0x0f, 0x38, 0x00
MODRM 0xc0 pshufb_opd1 pshufb_opd2
.endm
.macro PCLMULQDQ imm8 xmm1 xmm2
XMM_NUM clmul_opd1 \xmm1
XMM_NUM clmul_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX clmul_opd1 clmul_opd2
.byte 0x0f, 0x3a, 0x44
MODRM 0xc0 clmul_opd1 clmul_opd2
.byte \imm8
.endm
.macro PEXTRD imm8 xmm gpr
R32_NUM extrd_opd1 \gpr
XMM_NUM extrd_opd2 \xmm
PFX_OPD_SIZE
PFX_REX extrd_opd1 extrd_opd2
.byte 0x0f, 0x3a, 0x16
MODRM 0xc0 extrd_opd1 extrd_opd2
.byte \imm8
.endm
.macro MOVQ_R64_XMM opd1 opd2
TYPE movq_r64_xmm_opd1_type \opd1
.if movq_r64_xmm_opd1_type == TYPE_XMM
XMM_NUM movq_r64_xmm_opd1 \opd1
R64_NUM movq_r64_xmm_opd2 \opd2
.else
R64_NUM movq_r64_xmm_opd1 \opd1
XMM_NUM movq_r64_xmm_opd2 \opd2
.endif
PFX_OPD_SIZE
PFX_REX movq_r64_xmm_opd1 movq_r64_xmm_opd2 1
.if movq_r64_xmm_opd1_type == TYPE_XMM
.byte 0x0f, 0x7e
.else
.byte 0x0f, 0x6e
.endif
MODRM 0xc0 movq_r64_xmm_opd1 movq_r64_xmm_opd2
.endm
## assume buffers not aligned
#define VMOVDQ vmovdqu
################################ Define Macros
# addm [mem], reg
# Add reg to mem using reg-mem add and store
.macro addm p1 p2
add \p1, \p2
mov \p2, \p1
.endm
.macro MY_ROR p1 p2
shld $(32-(\p1)), \p2, \p2
.endm
################################
# COPY_XMM_AND_BSWAP xmm, [mem], byte_flip_mask
# Load xmm with mem and byte swap each dword
.macro COPY_XMM_AND_BSWAP p1 p2 p3
VMOVDQ \p2, \p1
vpshufb \p3, \p1, \p1
.endm
################################
X0 = %xmm4
X1 = %xmm5
X2 = %xmm6
X3 = %xmm7
XTMP0 = %xmm0
XTMP1 = %xmm1
XTMP2 = %xmm2
XTMP3 = %xmm3
XTMP4 = %xmm8
XFER = %xmm9
XTMP5 = %xmm11
SHUF_00BA = %xmm10 # shuffle xBxA -> 00BA
SHUF_DC00 = %xmm12 # shuffle xDxC -> DC00
BYTE_FLIP_MASK = %xmm13
NUM_BLKS = %rdx # 3rd arg
CTX = %rsi # 2nd arg
INP = %rdi # 1st arg
SRND = %rdi # clobbers INP
c = %ecx
d = %r8d
e = %edx
TBL = %rbp
a = %eax
b = %ebx
f = %r9d
g = %r10d
h = %r11d
y0 = %r13d
y1 = %r14d
y2 = %r15d
_INP_END_SIZE = 8
_INP_SIZE = 8
_XFER_SIZE = 16
_XMM_SAVE_SIZE = 0
_INP_END = 0
_INP = _INP_END + _INP_END_SIZE
_XFER = _INP + _INP_SIZE
_XMM_SAVE = _XFER + _XFER_SIZE
STACK_SIZE = _XMM_SAVE + _XMM_SAVE_SIZE
# rotate_Xs
# Rotate values of symbols X0...X3
.macro rotate_Xs
X_ = X0
X0 = X1
X1 = X2
X2 = X3
X3 = X_
.endm
# ROTATE_ARGS
# Rotate values of symbols a...h
.macro ROTATE_ARGS
TMP_ = h
h = g
g = f
f = e
e = d
d = c
c = b
b = a
a = TMP_
.endm
.macro FOUR_ROUNDS_AND_SCHED
## compute s0 four at a time and s1 two at a time
## compute W[-16] + W[-7] 4 at a time
mov e, y0 # y0 = e
MY_ROR (25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
vpalignr $4, X2, X3, XTMP0 # XTMP0 = W[-7]
MY_ROR (22-13), y1 # y1 = a >> (22-13)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
MY_ROR (11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
xor a, y1 # y1 = a ^ (a >> (22-13)
xor g, y2 # y2 = f^g
vpaddd X0, XTMP0, XTMP0 # XTMP0 = W[-7] + W[-16]
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
MY_ROR (13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
## compute s0
vpalignr $4, X0, X1, XTMP1 # XTMP1 = W[-15]
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
MY_ROR 6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
xor g, y2 # y2 = CH = ((f^g)&e)^g
MY_ROR 2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
add y0, y2 # y2 = S1 + CH
add _XFER(%rsp), y2 # y2 = k + w + S1 + CH
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
vpsrld $7, XTMP1, XTMP2
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
vpslld $(32-7), XTMP1, XTMP3
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
vpor XTMP2, XTMP3, XTMP3 # XTMP1 = W[-15] MY_ROR 7
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
mov e, y0 # y0 = e
mov a, y1 # y1 = a
MY_ROR (25-11), y0 # y0 = e >> (25-11)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
MY_ROR (22-13), y1 # y1 = a >> (22-13)
vpsrld $18, XTMP1, XTMP2 #
xor a, y1 # y1 = a ^ (a >> (22-13)
MY_ROR (11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
xor g, y2 # y2 = f^g
vpsrld $3, XTMP1, XTMP4 # XTMP4 = W[-15] >> 3
MY_ROR (13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
MY_ROR 6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
vpslld $(32-18), XTMP1, XTMP1
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
xor g, y2 # y2 = CH = ((f^g)&e)^g
vpxor XTMP1, XTMP3, XTMP3 #
add y0, y2 # y2 = S1 + CH
add (1*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
MY_ROR 2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
vpxor XTMP2, XTMP3, XTMP3 # XTMP1 = W[-15] MY_ROR 7 ^ W[-15] MY_ROR
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
vpxor XTMP4, XTMP3, XTMP1 # XTMP1 = s0
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
## compute low s1
vpshufd $0b11111010, X3, XTMP2 # XTMP2 = W[-2] {BBAA}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
vpaddd XTMP1, XTMP0, XTMP0 # XTMP0 = W[-16] + W[-7] + s0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
mov e, y0 # y0 = e
mov a, y1 # y1 = a
MY_ROR (25-11), y0 # y0 = e >> (25-11)
xor e, y0 # y0 = e ^ (e >> (25-11))
MY_ROR (22-13), y1 # y1 = a >> (22-13)
mov f, y2 # y2 = f
xor a, y1 # y1 = a ^ (a >> (22-13)
MY_ROR (11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
vpsrld $10, XTMP2, XTMP4 # XTMP4 = W[-2] >> 10 {BBAA}
xor g, y2 # y2 = f^g
vpsrlq $19, XTMP2, XTMP3 # XTMP3 = W[-2] MY_ROR 19 {xBxA}
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
vpsrlq $17, XTMP2, XTMP2 # XTMP2 = W[-2] MY_ROR 17 {xBxA}
MY_ROR (13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
xor g, y2 # y2 = CH = ((f^g)&e)^g
MY_ROR 6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
vpxor XTMP3, XTMP2, XTMP2 #
add y0, y2 # y2 = S1 + CH
MY_ROR 2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
add (2*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
vpxor XTMP2, XTMP4, XTMP4 # XTMP4 = s1 {xBxA}
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
vpshufb SHUF_00BA, XTMP4, XTMP4 # XTMP4 = s1 {00BA}
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
vpaddd XTMP4, XTMP0, XTMP0 # XTMP0 = {..., ..., W[1], W[0]}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
## compute high s1
vpshufd $0b01010000, XTMP0, XTMP2 # XTMP2 = W[-2] {DDCC}
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
mov e, y0 # y0 = e
MY_ROR (25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
MY_ROR (22-13), y1 # y1 = a >> (22-13)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
MY_ROR (11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
vpsrld $10, XTMP2, XTMP5 # XTMP5 = W[-2] >> 10 {DDCC}
xor a, y1 # y1 = a ^ (a >> (22-13)
xor g, y2 # y2 = f^g
vpsrlq $19, XTMP2, XTMP3 # XTMP3 = W[-2] MY_ROR 19 {xDxC}
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
MY_ROR (13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
vpsrlq $17, XTMP2, XTMP2 # XTMP2 = W[-2] MY_ROR 17 {xDxC}
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
MY_ROR 6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
xor g, y2 # y2 = CH = ((f^g)&e)^g
vpxor XTMP3, XTMP2, XTMP2
MY_ROR 2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
add y0, y2 # y2 = S1 + CH
add (3*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
vpxor XTMP2, XTMP5, XTMP5 # XTMP5 = s1 {xDxC}
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
vpshufb SHUF_DC00, XTMP5, XTMP5 # XTMP5 = s1 {DC00}
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
vpaddd XTMP0, XTMP5, X0 # X0 = {W[3], W[2], W[1], W[0]}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
rotate_Xs
.endm
## input is [rsp + _XFER + %1 * 4]
.macro DO_ROUND round
mov e, y0 # y0 = e
MY_ROR (25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
xor e, y0 # y0 = e ^ (e >> (25-11))
MY_ROR (22-13), y1 # y1 = a >> (22-13)
mov f, y2 # y2 = f
xor a, y1 # y1 = a ^ (a >> (22-13)
MY_ROR (11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
xor g, y2 # y2 = f^g
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
MY_ROR (13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
and e, y2 # y2 = (f^g)&e
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
MY_ROR 6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
xor g, y2 # y2 = CH = ((f^g)&e)^g
add y0, y2 # y2 = S1 + CH
MY_ROR 2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
offset = \round * 4 + _XFER #
add offset(%rsp), y2 # y2 = k + w + S1 + CH
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
.endm
########################################################################
## void sha256_transform_avx(void *input_data, UINT32 digest[8], UINT64 num_blks)
## arg 1 : pointer to input data
## arg 2 : pointer to digest
## arg 3 : Num blocks
########################################################################
.text
ENTRY(sha256_transform_avx)
.align 32
pushq %rbx
pushq %rbp
pushq %r13
pushq %r14
pushq %r15
pushq %r12
mov %rsp, %r12
subq $STACK_SIZE, %rsp # allocate stack space
and $~15, %rsp # align stack pointer
shl $6, NUM_BLKS # convert to bytes
jz done_hash
add INP, NUM_BLKS # pointer to end of data
mov NUM_BLKS, _INP_END(%rsp)
## load initial digest
mov 4*0(CTX), a
mov 4*1(CTX), b
mov 4*2(CTX), c
mov 4*3(CTX), d
mov 4*4(CTX), e
mov 4*5(CTX), f
mov 4*6(CTX), g
mov 4*7(CTX), h
vmovdqa PSHUFFLE_BYTE_FLIP_MASK(%rip), BYTE_FLIP_MASK
vmovdqa _SHUF_00BA(%rip), SHUF_00BA
vmovdqa _SHUF_DC00(%rip), SHUF_DC00
loop0:
lea K256(%rip), TBL
## byte swap first 16 dwords
COPY_XMM_AND_BSWAP X0, 0*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X1, 1*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X2, 2*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X3, 3*16(INP), BYTE_FLIP_MASK
mov INP, _INP(%rsp)
## schedule 48 input dwords, by doing 3 rounds of 16 each
mov $3, SRND
.align 16
loop1:
vpaddd (TBL), X0, XFER
vmovdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
vpaddd 1*16(TBL), X0, XFER
vmovdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
vpaddd 2*16(TBL), X0, XFER
vmovdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
vpaddd 3*16(TBL), X0, XFER
vmovdqa XFER, _XFER(%rsp)
add $4*16, TBL
FOUR_ROUNDS_AND_SCHED
sub $1, SRND
jne loop1
mov $2, SRND
loop2:
vpaddd (TBL), X0, XFER
vmovdqa XFER, _XFER(%rsp)
DO_ROUND 0
DO_ROUND 1
DO_ROUND 2
DO_ROUND 3
vpaddd 1*16(TBL), X1, XFER
vmovdqa XFER, _XFER(%rsp)
add $2*16, TBL
DO_ROUND 0
DO_ROUND 1
DO_ROUND 2
DO_ROUND 3
vmovdqa X2, X0
vmovdqa X3, X1
sub $1, SRND
jne loop2
addm (4*0)(CTX),a
addm (4*1)(CTX),b
addm (4*2)(CTX),c
addm (4*3)(CTX),d
addm (4*4)(CTX),e
addm (4*5)(CTX),f
addm (4*6)(CTX),g
addm (4*7)(CTX),h
mov _INP(%rsp), INP
add $64, INP
cmp _INP_END(%rsp), INP
jne loop0
done_hash:
mov %r12, %rsp
popq %r12
popq %r15
popq %r14
popq %r13
popq %rbp
popq %rbx
ret
ENDPROC(sha256_transform_avx)
.data
.align 64
K256:
.long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
.long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
.long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
.long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
.long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
.long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
.long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
.long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
.long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
.long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
.long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
.long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070
.long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
.long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
.long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
.long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
PSHUFFLE_BYTE_FLIP_MASK:
.octa 0x0c0d0e0f08090a0b0405060700010203
# shuffle xBxA -> 00BA
_SHUF_00BA:
.octa 0xFFFFFFFFFFFFFFFF0b0a090803020100
# shuffle xDxC -> DC00
_SHUF_DC00:
.octa 0x0b0a090803020100FFFFFFFFFFFFFFFF
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,772 @@
########################################################################
# Implement fast SHA-256 with SSSE3 instructions. (x86_64)
#
# Copyright (C) 2013 Intel Corporation.
#
# Authors:
# James Guilford <james.guilford@intel.com>
# Kirk Yap <kirk.s.yap@intel.com>
# Tim Chen <tim.c.chen@linux.intel.com>
#
# This software is available to you under a choice of one of two
# licenses. You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# - Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# - Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 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.
#
########################################################################
#
# This code is described in an Intel White-Paper:
# "Fast SHA-256 Implementations on Intel Architecture Processors"
#
# To find it, surf to http://www.intel.com/p/en_US/embedded
# and search for that title.
#
########################################################################
#
# Using this part of Minio codebase under the license
# Apache License Version 2.0 with modifications
##
#ifdef HAS_SSE41
#ifndef ENTRY
#define ENTRY(name) \
.globl name ; \
.align 4,0x90 ; \
name:
#endif
#ifndef END
#define END(name) \
.size name, .-name
#endif
#ifndef ENDPROC
#define ENDPROC(name) \
.type name, @function ; \
END(name)
#endif
#define NUM_INVALID 100
#define TYPE_R32 0
#define TYPE_R64 1
#define TYPE_XMM 2
#define TYPE_INVALID 100
.macro R32_NUM opd r32
\opd = NUM_INVALID
.ifc \r32,%eax
\opd = 0
.endif
.ifc \r32,%ecx
\opd = 1
.endif
.ifc \r32,%edx
\opd = 2
.endif
.ifc \r32,%ebx
\opd = 3
.endif
.ifc \r32,%esp
\opd = 4
.endif
.ifc \r32,%ebp
\opd = 5
.endif
.ifc \r32,%esi
\opd = 6
.endif
.ifc \r32,%edi
\opd = 7
.endif
#ifdef X86_64
.ifc \r32,%r8d
\opd = 8
.endif
.ifc \r32,%r9d
\opd = 9
.endif
.ifc \r32,%r10d
\opd = 10
.endif
.ifc \r32,%r11d
\opd = 11
.endif
.ifc \r32,%r12d
\opd = 12
.endif
.ifc \r32,%r13d
\opd = 13
.endif
.ifc \r32,%r14d
\opd = 14
.endif
.ifc \r32,%r15d
\opd = 15
.endif
#endif
.endm
.macro R64_NUM opd r64
\opd = NUM_INVALID
#ifdef X86_64
.ifc \r64,%rax
\opd = 0
.endif
.ifc \r64,%rcx
\opd = 1
.endif
.ifc \r64,%rdx
\opd = 2
.endif
.ifc \r64,%rbx
\opd = 3
.endif
.ifc \r64,%rsp
\opd = 4
.endif
.ifc \r64,%rbp
\opd = 5
.endif
.ifc \r64,%rsi
\opd = 6
.endif
.ifc \r64,%rdi
\opd = 7
.endif
.ifc \r64,%r8
\opd = 8
.endif
.ifc \r64,%r9
\opd = 9
.endif
.ifc \r64,%r10
\opd = 10
.endif
.ifc \r64,%r11
\opd = 11
.endif
.ifc \r64,%r12
\opd = 12
.endif
.ifc \r64,%r13
\opd = 13
.endif
.ifc \r64,%r14
\opd = 14
.endif
.ifc \r64,%r15
\opd = 15
.endif
#endif
.endm
.macro XMM_NUM opd xmm
\opd = NUM_INVALID
.ifc \xmm,%xmm0
\opd = 0
.endif
.ifc \xmm,%xmm1
\opd = 1
.endif
.ifc \xmm,%xmm2
\opd = 2
.endif
.ifc \xmm,%xmm3
\opd = 3
.endif
.ifc \xmm,%xmm4
\opd = 4
.endif
.ifc \xmm,%xmm5
\opd = 5
.endif
.ifc \xmm,%xmm6
\opd = 6
.endif
.ifc \xmm,%xmm7
\opd = 7
.endif
.ifc \xmm,%xmm8
\opd = 8
.endif
.ifc \xmm,%xmm9
\opd = 9
.endif
.ifc \xmm,%xmm10
\opd = 10
.endif
.ifc \xmm,%xmm11
\opd = 11
.endif
.ifc \xmm,%xmm12
\opd = 12
.endif
.ifc \xmm,%xmm13
\opd = 13
.endif
.ifc \xmm,%xmm14
\opd = 14
.endif
.ifc \xmm,%xmm15
\opd = 15
.endif
.endm
.macro TYPE type reg
R32_NUM reg_type_r32 \reg
R64_NUM reg_type_r64 \reg
XMM_NUM reg_type_xmm \reg
.if reg_type_r64 <> NUM_INVALID
\type = TYPE_R64
.elseif reg_type_r32 <> NUM_INVALID
\type = TYPE_R32
.elseif reg_type_xmm <> NUM_INVALID
\type = TYPE_XMM
.else
\type = TYPE_INVALID
.endif
.endm
.macro PFX_OPD_SIZE
.byte 0x66
.endm
.macro PFX_REX opd1 opd2 W=0
.if ((\opd1 | \opd2) & 8) || \W
.byte 0x40 | ((\opd1 & 8) >> 3) | ((\opd2 & 8) >> 1) | (\W << 3)
.endif
.endm
.macro MODRM mod opd1 opd2
.byte \mod | (\opd1 & 7) | ((\opd2 & 7) << 3)
.endm
.macro PSHUFB_XMM xmm1 xmm2
XMM_NUM pshufb_opd1 \xmm1
XMM_NUM pshufb_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX pshufb_opd1 pshufb_opd2
.byte 0x0f, 0x38, 0x00
MODRM 0xc0 pshufb_opd1 pshufb_opd2
.endm
.macro PCLMULQDQ imm8 xmm1 xmm2
XMM_NUM clmul_opd1 \xmm1
XMM_NUM clmul_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX clmul_opd1 clmul_opd2
.byte 0x0f, 0x3a, 0x44
MODRM 0xc0 clmul_opd1 clmul_opd2
.byte \imm8
.endm
.macro PEXTRD imm8 xmm gpr
R32_NUM extrd_opd1 \gpr
XMM_NUM extrd_opd2 \xmm
PFX_OPD_SIZE
PFX_REX extrd_opd1 extrd_opd2
.byte 0x0f, 0x3a, 0x16
MODRM 0xc0 extrd_opd1 extrd_opd2
.byte \imm8
.endm
.macro MOVQ_R64_XMM opd1 opd2
TYPE movq_r64_xmm_opd1_type \opd1
.if movq_r64_xmm_opd1_type == TYPE_XMM
XMM_NUM movq_r64_xmm_opd1 \opd1
R64_NUM movq_r64_xmm_opd2 \opd2
.else
R64_NUM movq_r64_xmm_opd1 \opd1
XMM_NUM movq_r64_xmm_opd2 \opd2
.endif
PFX_OPD_SIZE
PFX_REX movq_r64_xmm_opd1 movq_r64_xmm_opd2 1
.if movq_r64_xmm_opd1_type == TYPE_XMM
.byte 0x0f, 0x7e
.else
.byte 0x0f, 0x6e
.endif
MODRM 0xc0 movq_r64_xmm_opd1 movq_r64_xmm_opd2
.endm
## assume buffers not aligned
#define MOVDQ movdqu
################################ Define Macros
# addm [mem], reg
# Add reg to mem using reg-mem add and store
.macro addm p1 p2
add \p1, \p2
mov \p2, \p1
.endm
################################
# COPY_XMM_AND_BSWAP xmm, [mem], byte_flip_mask
# Load xmm with mem and byte swap each dword
.macro COPY_XMM_AND_BSWAP p1 p2 p3
MOVDQ \p2, \p1
pshufb \p3, \p1
.endm
################################
X0 = %xmm4
X1 = %xmm5
X2 = %xmm6
X3 = %xmm7
XTMP0 = %xmm0
XTMP1 = %xmm1
XTMP2 = %xmm2
XTMP3 = %xmm3
XTMP4 = %xmm8
XFER = %xmm9
SHUF_00BA = %xmm10 # shuffle xBxA -> 00BA
SHUF_DC00 = %xmm11 # shuffle xDxC -> DC00
BYTE_FLIP_MASK = %xmm12
NUM_BLKS = %rdx # 3rd arg
CTX = %rsi # 2nd arg
INP = %rdi # 1st arg
SRND = %rdi # clobbers INP
c = %ecx
d = %r8d
e = %edx
TBL = %rbp
a = %eax
b = %ebx
f = %r9d
g = %r10d
h = %r11d
y0 = %r13d
y1 = %r14d
y2 = %r15d
_INP_END_SIZE = 8
_INP_SIZE = 8
_XFER_SIZE = 16
_XMM_SAVE_SIZE = 0
_INP_END = 0
_INP = _INP_END + _INP_END_SIZE
_XFER = _INP + _INP_SIZE
_XMM_SAVE = _XFER + _XFER_SIZE
STACK_SIZE = _XMM_SAVE + _XMM_SAVE_SIZE
# rotate_Xs
# Rotate values of symbols X0...X3
.macro rotate_Xs
X_ = X0
X0 = X1
X1 = X2
X2 = X3
X3 = X_
.endm
# ROTATE_ARGS
# Rotate values of symbols a...h
.macro ROTATE_ARGS
TMP_ = h
h = g
g = f
f = e
e = d
d = c
c = b
b = a
a = TMP_
.endm
.macro FOUR_ROUNDS_AND_SCHED
## compute s0 four at a time and s1 two at a time
## compute W[-16] + W[-7] 4 at a time
movdqa X3, XTMP0
mov e, y0 # y0 = e
ror $(25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
palignr $4, X2, XTMP0 # XTMP0 = W[-7]
ror $(22-13), y1 # y1 = a >> (22-13)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
ror $(11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
movdqa X1, XTMP1
xor a, y1 # y1 = a ^ (a >> (22-13)
xor g, y2 # y2 = f^g
paddd X0, XTMP0 # XTMP0 = W[-7] + W[-16]
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
ror $(13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
## compute s0
palignr $4, X0, XTMP1 # XTMP1 = W[-15]
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
ror $6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
xor g, y2 # y2 = CH = ((f^g)&e)^g
movdqa XTMP1, XTMP2 # XTMP2 = W[-15]
ror $2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
add y0, y2 # y2 = S1 + CH
add _XFER(%rsp) , y2 # y2 = k + w + S1 + CH
movdqa XTMP1, XTMP3 # XTMP3 = W[-15]
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
pslld $(32-7), XTMP1 #
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
psrld $7, XTMP2 #
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
por XTMP2, XTMP1 # XTMP1 = W[-15] ror 7
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
#
ROTATE_ARGS #
movdqa XTMP3, XTMP2 # XTMP2 = W[-15]
mov e, y0 # y0 = e
mov a, y1 # y1 = a
movdqa XTMP3, XTMP4 # XTMP4 = W[-15]
ror $(25-11), y0 # y0 = e >> (25-11)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
ror $(22-13), y1 # y1 = a >> (22-13)
pslld $(32-18), XTMP3 #
xor a, y1 # y1 = a ^ (a >> (22-13)
ror $(11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
xor g, y2 # y2 = f^g
psrld $18, XTMP2 #
ror $(13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
ror $6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
pxor XTMP3, XTMP1
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
xor g, y2 # y2 = CH = ((f^g)&e)^g
psrld $3, XTMP4 # XTMP4 = W[-15] >> 3
add y0, y2 # y2 = S1 + CH
add (1*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
ror $2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
pxor XTMP2, XTMP1 # XTMP1 = W[-15] ror 7 ^ W[-15] ror 18
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
pxor XTMP4, XTMP1 # XTMP1 = s0
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
## compute low s1
pshufd $0b11111010, X3, XTMP2 # XTMP2 = W[-2] {BBAA}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
paddd XTMP1, XTMP0 # XTMP0 = W[-16] + W[-7] + s0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
movdqa XTMP2, XTMP3 # XTMP3 = W[-2] {BBAA}
mov e, y0 # y0 = e
mov a, y1 # y1 = a
ror $(25-11), y0 # y0 = e >> (25-11)
movdqa XTMP2, XTMP4 # XTMP4 = W[-2] {BBAA}
xor e, y0 # y0 = e ^ (e >> (25-11))
ror $(22-13), y1 # y1 = a >> (22-13)
mov f, y2 # y2 = f
xor a, y1 # y1 = a ^ (a >> (22-13)
ror $(11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
psrlq $17, XTMP2 # XTMP2 = W[-2] ror 17 {xBxA}
xor g, y2 # y2 = f^g
psrlq $19, XTMP3 # XTMP3 = W[-2] ror 19 {xBxA}
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
and e, y2 # y2 = (f^g)&e
psrld $10, XTMP4 # XTMP4 = W[-2] >> 10 {BBAA}
ror $(13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
xor g, y2 # y2 = CH = ((f^g)&e)^g
ror $6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
pxor XTMP3, XTMP2
add y0, y2 # y2 = S1 + CH
ror $2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
add (2*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
pxor XTMP2, XTMP4 # XTMP4 = s1 {xBxA}
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
pshufb SHUF_00BA, XTMP4 # XTMP4 = s1 {00BA}
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
paddd XTMP4, XTMP0 # XTMP0 = {..., ..., W[1], W[0]}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
## compute high s1
pshufd $0b01010000, XTMP0, XTMP2 # XTMP2 = W[-2] {BBAA}
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
#
ROTATE_ARGS #
movdqa XTMP2, XTMP3 # XTMP3 = W[-2] {DDCC}
mov e, y0 # y0 = e
ror $(25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
movdqa XTMP2, X0 # X0 = W[-2] {DDCC}
ror $(22-13), y1 # y1 = a >> (22-13)
xor e, y0 # y0 = e ^ (e >> (25-11))
mov f, y2 # y2 = f
ror $(11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
psrlq $17, XTMP2 # XTMP2 = W[-2] ror 17 {xDxC}
xor a, y1 # y1 = a ^ (a >> (22-13)
xor g, y2 # y2 = f^g
psrlq $19, XTMP3 # XTMP3 = W[-2] ror 19 {xDxC}
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25
and e, y2 # y2 = (f^g)&e
ror $(13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
psrld $10, X0 # X0 = W[-2] >> 10 {DDCC}
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22
ror $6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>2
xor g, y2 # y2 = CH = ((f^g)&e)^g
pxor XTMP3, XTMP2 #
ror $2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>2
add y0, y2 # y2 = S1 + CH
add (3*4 + _XFER)(%rsp), y2 # y2 = k + w + S1 + CH
pxor XTMP2, X0 # X0 = s1 {xDxC}
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
pshufb SHUF_DC00, X0 # X0 = s1 {DC00}
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
paddd XTMP0, X0 # X0 = {W[3], W[2], W[1], W[0]}
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
rotate_Xs
.endm
## input is [rsp + _XFER + %1 * 4]
.macro DO_ROUND round
mov e, y0 # y0 = e
ror $(25-11), y0 # y0 = e >> (25-11)
mov a, y1 # y1 = a
xor e, y0 # y0 = e ^ (e >> (25-11))
ror $(22-13), y1 # y1 = a >> (22-13)
mov f, y2 # y2 = f
xor a, y1 # y1 = a ^ (a >> (22-13)
ror $(11-6), y0 # y0 = (e >> (11-6)) ^ (e >> (25-6))
xor g, y2 # y2 = f^g
xor e, y0 # y0 = e ^ (e >> (11-6)) ^ (e >> (25-6))
ror $(13-2), y1 # y1 = (a >> (13-2)) ^ (a >> (22-2))
and e, y2 # y2 = (f^g)&e
xor a, y1 # y1 = a ^ (a >> (13-2)) ^ (a >> (22-2))
ror $6, y0 # y0 = S1 = (e>>6) & (e>>11) ^ (e>>25)
xor g, y2 # y2 = CH = ((f^g)&e)^g
add y0, y2 # y2 = S1 + CH
ror $2, y1 # y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22)
offset = \round * 4 + _XFER
add offset(%rsp), y2 # y2 = k + w + S1 + CH
mov a, y0 # y0 = a
add y2, h # h = h + S1 + CH + k + w
mov a, y2 # y2 = a
or c, y0 # y0 = a|c
add h, d # d = d + h + S1 + CH + k + w
and c, y2 # y2 = a&c
and b, y0 # y0 = (a|c)&b
add y1, h # h = h + S1 + CH + k + w + S0
or y2, y0 # y0 = MAJ = (a|c)&b)|(a&c)
add y0, h # h = h + S1 + CH + k + w + S0 + MAJ
ROTATE_ARGS
.endm
########################################################################
## void sha256_transform_ssse3(void *input_data, UINT32 digest[8], UINT64 num_blks)
## arg 1 : pointer to input data
## arg 2 : pointer to digest
## arg 3 : Num blocks
########################################################################
.text
ENTRY(sha256_transform_ssse3)
.align 32
pushq %rbx
pushq %rbp
pushq %r13
pushq %r14
pushq %r15
pushq %r12
mov %rsp, %r12
subq $STACK_SIZE, %rsp
and $~15, %rsp
shl $6, NUM_BLKS # convert to bytes
jz done_hash
add INP, NUM_BLKS
mov NUM_BLKS, _INP_END(%rsp) # pointer to end of data
## load initial digest
mov 4*0(CTX), a
mov 4*1(CTX), b
mov 4*2(CTX), c
mov 4*3(CTX), d
mov 4*4(CTX), e
mov 4*5(CTX), f
mov 4*6(CTX), g
mov 4*7(CTX), h
movdqa PSHUFFLE_BYTE_FLIP_MASK(%rip), BYTE_FLIP_MASK
movdqa _SHUF_00BA(%rip), SHUF_00BA
movdqa _SHUF_DC00(%rip), SHUF_DC00
loop0:
lea K256(%rip), TBL
## byte swap first 16 dwords
COPY_XMM_AND_BSWAP X0, 0*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X1, 1*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X2, 2*16(INP), BYTE_FLIP_MASK
COPY_XMM_AND_BSWAP X3, 3*16(INP), BYTE_FLIP_MASK
mov INP, _INP(%rsp)
## schedule 48 input dwords, by doing 3 rounds of 16 each
mov $3, SRND
.align 16
loop1:
movdqa (TBL), XFER
paddd X0, XFER
movdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
movdqa 1*16(TBL), XFER
paddd X0, XFER
movdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
movdqa 2*16(TBL), XFER
paddd X0, XFER
movdqa XFER, _XFER(%rsp)
FOUR_ROUNDS_AND_SCHED
movdqa 3*16(TBL), XFER
paddd X0, XFER
movdqa XFER, _XFER(%rsp)
add $4*16, TBL
FOUR_ROUNDS_AND_SCHED
sub $1, SRND
jne loop1
mov $2, SRND
loop2:
paddd (TBL), X0
movdqa X0, _XFER(%rsp)
DO_ROUND 0
DO_ROUND 1
DO_ROUND 2
DO_ROUND 3
paddd 1*16(TBL), X1
movdqa X1, _XFER(%rsp)
add $2*16, TBL
DO_ROUND 0
DO_ROUND 1
DO_ROUND 2
DO_ROUND 3
movdqa X2, X0
movdqa X3, X1
sub $1, SRND
jne loop2
addm (4*0)(CTX),a
addm (4*1)(CTX),b
addm (4*2)(CTX),c
addm (4*3)(CTX),d
addm (4*4)(CTX),e
addm (4*5)(CTX),f
addm (4*6)(CTX),g
addm (4*7)(CTX),h
mov _INP(%rsp), INP
add $64, INP
cmp _INP_END(%rsp), INP
jne loop0
done_hash:
mov %r12, %rsp
popq %r12
popq %r15
popq %r14
popq %r13
popq %rbp
popq %rbx
ret
ENDPROC(sha256_transform_ssse3)
.data
.align 64
K256:
.long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
.long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
.long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
.long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
.long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
.long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
.long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
.long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
.long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
.long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
.long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
.long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070
.long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
.long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
.long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
.long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
PSHUFFLE_BYTE_FLIP_MASK:
.octa 0x0c0d0e0f08090a0b0405060700010203
# shuffle xBxA -> 00BA
_SHUF_00BA:
.octa 0xFFFFFFFFFFFFFFFF0b0a090803020100
# shuffle xDxC -> DC00
_SHUF_DC00:
.octa 0x0b0a090803020100FFFFFFFFFFFFFFFF
#endif
+53
View File
@@ -0,0 +1,53 @@
/*
* Minio Cloud Storage, (C) 2014 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sha256
import (
"hash"
"io"
"crypto/sha256"
)
// Sum256 - single caller sha256 helper
func Sum256(data []byte) []byte {
d := sha256.New()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha256 helper
func Sum(reader io.Reader) ([]byte, error) {
d := sha256.New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
d.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return d.Sum(nil), nil
}
// New returns a new hash.Hash computing SHA256.
func New() hash.Hash {
return sha256.New()
}
+179
View File
@@ -0,0 +1,179 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
// Using this part of Minio codebase under the license
// Apache License Version 2.0 with modifications
// Package sha256 provides SHA256SSE3, SHA256AVX, SHA256AVX2
package sha256
import (
"hash"
"io"
"github.com/minio/minio-xl/pkg/cpu"
)
// The size of a SHA256 checksum in bytes.
const Size = 32
// The blocksize of SHA256 in bytes.
const BlockSize = 64
const (
chunk = 64
init0 = 0x6A09E667
init1 = 0xBB67AE85
init2 = 0x3C6EF372
init3 = 0xA54FF53A
init4 = 0x510E527F
init5 = 0x9B05688C
init6 = 0x1F83D9AB
init7 = 0x5BE0CD19
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [8]uint32
x [chunk]byte
nx int
len uint64
}
// Reset digest back to default
func (d *digest) Reset() {
d.h[0] = init0
d.h[1] = init1
d.h[2] = init2
d.h[3] = init3
d.h[4] = init4
d.h[5] = init5
d.h[6] = init6
d.h[7] = init7
d.nx = 0
d.len = 0
}
func block(dig *digest, p []byte) {
switch true {
case cpu.HasAVX2() == true:
blockAVX2(dig, p)
case cpu.HasAVX() == true:
blockAVX(dig, p)
case cpu.HasSSE41() == true:
blockSSE(dig, p)
default:
blockGeneric(dig, p)
}
}
// New returns a new hash.Hash computing the SHA256 checksum.
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
// Return size of checksum
func (d *digest) Size() int { return Size }
// Return blocksize of checksum
func (d *digest) BlockSize() int { return BlockSize }
// Write to digest
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == chunk {
block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
// Return sha256 sum in bytes
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
// Intermediate checksum function
func (d *digest) checkSum() [Size]byte {
len := d.len
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
d.Write(tmp[0 : 56-len%64])
} else {
d.Write(tmp[0 : 64+56-len%64])
}
// Length in bits.
len <<= 3
for i := uint(0); i < 8; i++ {
tmp[i] = byte(len >> (56 - 8*i))
}
d.Write(tmp[0:8])
if d.nx != 0 {
panic("d.nx != 0")
}
h := d.h[:]
var digest [Size]byte
for i, s := range h {
digest[i*4] = byte(s >> 24)
digest[i*4+1] = byte(s >> 16)
digest[i*4+2] = byte(s >> 8)
digest[i*4+3] = byte(s)
}
return digest
}
/// Convenience functions
// Sum256 - single caller sha256 helper
func Sum256(data []byte) []byte {
var d digest
d.Reset()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha256 helper
func Sum(reader io.Reader) ([]byte, error) {
h := New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
h.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return h.Sum(nil), nil
}
+125
View File
@@ -0,0 +1,125 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
// Using this part of Minio codebase under the license
// Apache License Version 2.0 with modifications
// SHA256 hash algorithm. See FIPS 180-2.
package sha256
import (
"fmt"
"io"
"testing"
)
type sha256Test struct {
out string
in string
}
var golden = []sha256Test{
{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
{"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "a"},
{"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", "ab"},
{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"},
{"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", "abcd"},
{"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", "abcde"},
{"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", "abcdef"},
{"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", "abcdefg"},
{"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", "abcdefgh"},
{"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", "abcdefghi"},
{"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", "abcdefghij"},
{"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", "Discard medicine more than two years old."},
{"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", "He who has a shady past knows that nice guys finish last."},
{"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", "I wouldn't marry him with a ten foot pole."},
{"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
{"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", "The days of the digital watch are numbered. -Tom Stoppard"},
{"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", "Nepal premier won't resign."},
{"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", "For every action there is an equal and opposite government program."},
{"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", "His money is twice tainted: 'taint yours and 'taint mine."},
{"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
{"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
{"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", "size: a.out: bad magic"},
{"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", "The major problem is with sendmail. -Mark Horton"},
{"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
{"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", "If the enemy is within range, then so are you."},
{"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", "It's well we cannot hear the screams/That we create in others' dreams."},
{"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", "You remind me of a TV show, but that's all right: I watch it anyway."},
{"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", "C is as portable as Stonehedge!!"},
{"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
{"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
{"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", "How can you write a big system without C++? -Paul Glick"},
}
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
s := fmt.Sprintf("%x", Sum256([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out)
}
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out)
}
c.Reset()
}
}
}
func TestSize(t *testing.T) {
c := New()
if got := c.Size(); got != Size {
t.Errorf("Size = %d; want %d", got, Size)
}
}
func TestBlockSize(t *testing.T) {
c := New()
if got := c.BlockSize(); got != BlockSize {
t.Errorf("BlockSize = %d want %d", got, BlockSize)
}
}
var bench = New()
var buf = make([]byte, 1024*1024)
func benchmarkSize(b *testing.B, size int) {
b.SetBytes(int64(size))
sum := make([]byte, bench.Size())
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8)
}
func BenchmarkHash1K(b *testing.B) {
benchmarkSize(b, 1024)
}
func BenchmarkHash8K(b *testing.B) {
benchmarkSize(b, 8192)
}
func BenchmarkHash1M(b *testing.B) {
benchmarkSize(b, 1024*1024)
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Minio Cloud Storage, (C) 2014 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sha256
import (
"hash"
"io"
"crypto/sha256"
)
// Sum256 - single caller sha256 helper
func Sum256(data []byte) []byte {
d := sha256.New()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha256 helper
func Sum(reader io.Reader) ([]byte, error) {
d := sha256.New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
d.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return d.Sum(nil), nil
}
// New returns a new hash.Hash computing SHA256.
func New() hash.Hash {
return sha256.New()
}
+162
View File
@@ -0,0 +1,162 @@
// +build amd64
//
// Minio Cloud Storage, (C) 2015 Minio, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Software block transform are provided by The Go Authors:
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
package sha256
// #cgo CFLAGS: -DHAS_SSE41 -DHAS_AVX -DHAS_AVX2
// #include <stdint.h>
// void sha256_transform_ssse3 (const char *input_data, uint32_t *digest, unsigned long num_blks);
// void sha256_transform_avx (const char *input_data, uint32_t *digest, unsigned long num_blks);
// void sha256_transform_rorx (const char *input_data, uint32_t *digest, unsigned long num_blks);
import "C"
import "unsafe"
func blockSSE(dig *digest, p []byte) {
C.sha256_transform_ssse3((*C.char)(unsafe.Pointer(&p[0])), (*C.uint32_t)(unsafe.Pointer(&dig.h[0])), (C.ulong)(len(p)/64))
}
func blockAVX(dig *digest, p []byte) {
C.sha256_transform_avx((*C.char)(unsafe.Pointer(&p[0])), (*C.uint32_t)(unsafe.Pointer(&dig.h[0])), (C.ulong)(len(p)/64))
}
func blockAVX2(dig *digest, p []byte) {
C.sha256_transform_rorx((*C.char)(unsafe.Pointer(&p[0])), (*C.uint32_t)(unsafe.Pointer(&dig.h[0])), (C.ulong)(len(p)/64))
}
func blockGeneric(dig *digest, p []byte) {
var w [64]uint32
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= chunk {
// Can interlace the computation of w with the
// rounds below if needed for speed.
for i := 0; i < 16; i++ {
j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
}
for i := 16; i < 64; i++ {
v1 := w[i-2]
t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10)
v2 := w[i-15]
t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3)
w[i] = t1 + w[i-7] + t2 + w[i-16]
}
a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7
for i := 0; i < 64; i++ {
t1 := h + ((e>>6 | e<<(32-6)) ^ (e>>11 | e<<(32-11)) ^ (e>>25 | e<<(32-25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i]
t2 := ((a>>2 | a<<(32-2)) ^ (a>>13 | a<<(32-13)) ^ (a>>22 | a<<(32-22))) + ((a & b) ^ (a & c) ^ (b & c))
h = g
g = f
f = e
e = d + t1
d = c
c = b
b = a
a = t1 + t2
}
h0 += a
h1 += b
h2 += c
h3 += d
h4 += e
h5 += f
h6 += g
h7 += h
p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
}
var _K = []uint32{
0x428a2f98,
0x71374491,
0xb5c0fbcf,
0xe9b5dba5,
0x3956c25b,
0x59f111f1,
0x923f82a4,
0xab1c5ed5,
0xd807aa98,
0x12835b01,
0x243185be,
0x550c7dc3,
0x72be5d74,
0x80deb1fe,
0x9bdc06a7,
0xc19bf174,
0xe49b69c1,
0xefbe4786,
0x0fc19dc6,
0x240ca1cc,
0x2de92c6f,
0x4a7484aa,
0x5cb0a9dc,
0x76f988da,
0x983e5152,
0xa831c66d,
0xb00327c8,
0xbf597fc7,
0xc6e00bf3,
0xd5a79147,
0x06ca6351,
0x14292967,
0x27b70a85,
0x2e1b2138,
0x4d2c6dfc,
0x53380d13,
0x650a7354,
0x766a0abb,
0x81c2c92e,
0x92722c85,
0xa2bfe8a1,
0xa81a664b,
0xc24b8b70,
0xc76c51a3,
0xd192e819,
0xd6990624,
0xf40e3585,
0x106aa070,
0x19a4c116,
0x1e376c08,
0x2748774c,
0x34b0bcb5,
0x391c0cb3,
0x4ed8aa4a,
0x5b9cca4f,
0x682e6ff3,
0x748f82ee,
0x78a5636f,
0x84c87814,
0x8cc70208,
0x90befffa,
0xa4506ceb,
0xbef9a3f7,
0xc67178f2,
}
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,686 @@
########################################################################
# Implement fast SHA-512 with AVX instructions. (x86_64)
#
# Copyright (C) 2013 Intel Corporation.
#
# Authors:
# James Guilford <james.guilford@intel.com>
# Kirk Yap <kirk.s.yap@intel.com>
# David Cote <david.m.cote@intel.com>
# Tim Chen <tim.c.chen@linux.intel.com>
#
# This software is available to you under a choice of one of two
# licenses. You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# - Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# - Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 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.
#
########################################################################
#
# This code is described in an Intel White-Paper:
# "Fast SHA-512 Implementations on Intel Architecture Processors"
#
# To find it, surf to http://www.intel.com/p/en_US/embedded
# and search for that title.
#
########################################################################
# Using this part of Minio codebase under the license
# Apache License Version 2.0 with modifications
##
#ifdef HAS_AVX
#ifndef ENTRY
#define ENTRY(name) \
.globl name ; \
.align 4,0x90 ; \
name:
#endif
#ifndef END
#define END(name) \
.size name, .-name
#endif
#ifndef ENDPROC
#define ENDPROC(name) \
.type name, @function ; \
END(name)
#endif
#define NUM_INVALID 100
#define TYPE_R32 0
#define TYPE_R64 1
#define TYPE_XMM 2
#define TYPE_INVALID 100
.macro R32_NUM opd r32
\opd = NUM_INVALID
.ifc \r32,%eax
\opd = 0
.endif
.ifc \r32,%ecx
\opd = 1
.endif
.ifc \r32,%edx
\opd = 2
.endif
.ifc \r32,%ebx
\opd = 3
.endif
.ifc \r32,%esp
\opd = 4
.endif
.ifc \r32,%ebp
\opd = 5
.endif
.ifc \r32,%esi
\opd = 6
.endif
.ifc \r32,%edi
\opd = 7
.endif
#ifdef X86_64
.ifc \r32,%r8d
\opd = 8
.endif
.ifc \r32,%r9d
\opd = 9
.endif
.ifc \r32,%r10d
\opd = 10
.endif
.ifc \r32,%r11d
\opd = 11
.endif
.ifc \r32,%r12d
\opd = 12
.endif
.ifc \r32,%r13d
\opd = 13
.endif
.ifc \r32,%r14d
\opd = 14
.endif
.ifc \r32,%r15d
\opd = 15
.endif
#endif
.endm
.macro R64_NUM opd r64
\opd = NUM_INVALID
#ifdef X86_64
.ifc \r64,%rax
\opd = 0
.endif
.ifc \r64,%rcx
\opd = 1
.endif
.ifc \r64,%rdx
\opd = 2
.endif
.ifc \r64,%rbx
\opd = 3
.endif
.ifc \r64,%rsp
\opd = 4
.endif
.ifc \r64,%rbp
\opd = 5
.endif
.ifc \r64,%rsi
\opd = 6
.endif
.ifc \r64,%rdi
\opd = 7
.endif
.ifc \r64,%r8
\opd = 8
.endif
.ifc \r64,%r9
\opd = 9
.endif
.ifc \r64,%r10
\opd = 10
.endif
.ifc \r64,%r11
\opd = 11
.endif
.ifc \r64,%r12
\opd = 12
.endif
.ifc \r64,%r13
\opd = 13
.endif
.ifc \r64,%r14
\opd = 14
.endif
.ifc \r64,%r15
\opd = 15
.endif
#endif
.endm
.macro XMM_NUM opd xmm
\opd = NUM_INVALID
.ifc \xmm,%xmm0
\opd = 0
.endif
.ifc \xmm,%xmm1
\opd = 1
.endif
.ifc \xmm,%xmm2
\opd = 2
.endif
.ifc \xmm,%xmm3
\opd = 3
.endif
.ifc \xmm,%xmm4
\opd = 4
.endif
.ifc \xmm,%xmm5
\opd = 5
.endif
.ifc \xmm,%xmm6
\opd = 6
.endif
.ifc \xmm,%xmm7
\opd = 7
.endif
.ifc \xmm,%xmm8
\opd = 8
.endif
.ifc \xmm,%xmm9
\opd = 9
.endif
.ifc \xmm,%xmm10
\opd = 10
.endif
.ifc \xmm,%xmm11
\opd = 11
.endif
.ifc \xmm,%xmm12
\opd = 12
.endif
.ifc \xmm,%xmm13
\opd = 13
.endif
.ifc \xmm,%xmm14
\opd = 14
.endif
.ifc \xmm,%xmm15
\opd = 15
.endif
.endm
.macro TYPE type reg
R32_NUM reg_type_r32 \reg
R64_NUM reg_type_r64 \reg
XMM_NUM reg_type_xmm \reg
.if reg_type_r64 <> NUM_INVALID
\type = TYPE_R64
.elseif reg_type_r32 <> NUM_INVALID
\type = TYPE_R32
.elseif reg_type_xmm <> NUM_INVALID
\type = TYPE_XMM
.else
\type = TYPE_INVALID
.endif
.endm
.macro PFX_OPD_SIZE
.byte 0x66
.endm
.macro PFX_REX opd1 opd2 W=0
.if ((\opd1 | \opd2) & 8) || \W
.byte 0x40 | ((\opd1 & 8) >> 3) | ((\opd2 & 8) >> 1) | (\W << 3)
.endif
.endm
.macro MODRM mod opd1 opd2
.byte \mod | (\opd1 & 7) | ((\opd2 & 7) << 3)
.endm
.macro PSHUFB_XMM xmm1 xmm2
XMM_NUM pshufb_opd1 \xmm1
XMM_NUM pshufb_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX pshufb_opd1 pshufb_opd2
.byte 0x0f, 0x38, 0x00
MODRM 0xc0 pshufb_opd1 pshufb_opd2
.endm
.macro PCLMULQDQ imm8 xmm1 xmm2
XMM_NUM clmul_opd1 \xmm1
XMM_NUM clmul_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX clmul_opd1 clmul_opd2
.byte 0x0f, 0x3a, 0x44
MODRM 0xc0 clmul_opd1 clmul_opd2
.byte \imm8
.endm
.macro PEXTRD imm8 xmm gpr
R32_NUM extrd_opd1 \gpr
XMM_NUM extrd_opd2 \xmm
PFX_OPD_SIZE
PFX_REX extrd_opd1 extrd_opd2
.byte 0x0f, 0x3a, 0x16
MODRM 0xc0 extrd_opd1 extrd_opd2
.byte \imm8
.endm
.macro MOVQ_R64_XMM opd1 opd2
TYPE movq_r64_xmm_opd1_type \opd1
.if movq_r64_xmm_opd1_type == TYPE_XMM
XMM_NUM movq_r64_xmm_opd1 \opd1
R64_NUM movq_r64_xmm_opd2 \opd2
.else
R64_NUM movq_r64_xmm_opd1 \opd1
XMM_NUM movq_r64_xmm_opd2 \opd2
.endif
PFX_OPD_SIZE
PFX_REX movq_r64_xmm_opd1 movq_r64_xmm_opd2 1
.if movq_r64_xmm_opd1_type == TYPE_XMM
.byte 0x0f, 0x7e
.else
.byte 0x0f, 0x6e
.endif
MODRM 0xc0 movq_r64_xmm_opd1 movq_r64_xmm_opd2
.endm
.text
# Virtual Registers
# ARG1
msg = %rdi
# ARG2
digest = %rsi
# ARG3
msglen = %rdx
T1 = %rcx
T2 = %r8
a_64 = %r9
b_64 = %r10
c_64 = %r11
d_64 = %r12
e_64 = %r13
f_64 = %r14
g_64 = %r15
h_64 = %rbx
tmp0 = %rax
# Local variables (stack frame)
# Message Schedule
W_SIZE = 80*8
# W[t] + K[t] | W[t+1] + K[t+1]
WK_SIZE = 2*8
RSPSAVE_SIZE = 1*8
GPRSAVE_SIZE = 5*8
frame_W = 0
frame_WK = frame_W + W_SIZE
frame_RSPSAVE = frame_WK + WK_SIZE
frame_GPRSAVE = frame_RSPSAVE + RSPSAVE_SIZE
frame_size = frame_GPRSAVE + GPRSAVE_SIZE
# Useful QWORD "arrays" for simpler memory references
# MSG, DIGEST, K_t, W_t are arrays
# WK_2(t) points to 1 of 2 qwords at frame.WK depdending on t being odd/even
# Input message (arg1)
#define MSG(i) 8*i(msg)
# Output Digest (arg2)
#define DIGEST(i) 8*i(digest)
# SHA Constants (static mem)
#define K_t(i) 8*i+K512(%rip)
# Message Schedule (stack frame)
#define W_t(i) 8*i+frame_W(%rsp)
# W[t]+K[t] (stack frame)
#define WK_2(i) 8*((i%2))+frame_WK(%rsp)
.macro RotateState
# Rotate symbols a..h right
TMP = h_64
h_64 = g_64
g_64 = f_64
f_64 = e_64
e_64 = d_64
d_64 = c_64
c_64 = b_64
b_64 = a_64
a_64 = TMP
.endm
.macro RORQ p1 p2
# shld is faster than ror on Sandybridge
shld $(64-\p2), \p1, \p1
.endm
.macro SHA512_Round rnd
# Compute Round %%t
mov f_64, T1 # T1 = f
mov e_64, tmp0 # tmp = e
xor g_64, T1 # T1 = f ^ g
RORQ tmp0, 23 # 41 # tmp = e ror 23
and e_64, T1 # T1 = (f ^ g) & e
xor e_64, tmp0 # tmp = (e ror 23) ^ e
xor g_64, T1 # T1 = ((f ^ g) & e) ^ g = CH(e,f,g)
idx = \rnd
add WK_2(idx), T1 # W[t] + K[t] from message scheduler
RORQ tmp0, 4 # 18 # tmp = ((e ror 23) ^ e) ror 4
xor e_64, tmp0 # tmp = (((e ror 23) ^ e) ror 4) ^ e
mov a_64, T2 # T2 = a
add h_64, T1 # T1 = CH(e,f,g) + W[t] + K[t] + h
RORQ tmp0, 14 # 14 # tmp = ((((e ror23)^e)ror4)^e)ror14 = S1(e)
add tmp0, T1 # T1 = CH(e,f,g) + W[t] + K[t] + S1(e)
mov a_64, tmp0 # tmp = a
xor c_64, T2 # T2 = a ^ c
and c_64, tmp0 # tmp = a & c
and b_64, T2 # T2 = (a ^ c) & b
xor tmp0, T2 # T2 = ((a ^ c) & b) ^ (a & c) = Maj(a,b,c)
mov a_64, tmp0 # tmp = a
RORQ tmp0, 5 # 39 # tmp = a ror 5
xor a_64, tmp0 # tmp = (a ror 5) ^ a
add T1, d_64 # e(next_state) = d + T1
RORQ tmp0, 6 # 34 # tmp = ((a ror 5) ^ a) ror 6
xor a_64, tmp0 # tmp = (((a ror 5) ^ a) ror 6) ^ a
lea (T1, T2), h_64 # a(next_state) = T1 + Maj(a,b,c)
RORQ tmp0, 28 # 28 # tmp = ((((a ror5)^a)ror6)^a)ror28 = S0(a)
add tmp0, h_64 # a(next_state) = T1 + Maj(a,b,c) S0(a)
RotateState
.endm
.macro SHA512_2Sched_2Round_avx rnd
# Compute rounds t-2 and t-1
# Compute message schedule QWORDS t and t+1
# Two rounds are computed based on the values for K[t-2]+W[t-2] and
# K[t-1]+W[t-1] which were previously stored at WK_2 by the message
# scheduler.
# The two new schedule QWORDS are stored at [W_t(t)] and [W_t(t+1)].
# They are then added to their respective SHA512 constants at
# [K_t(t)] and [K_t(t+1)] and stored at dqword [WK_2(t)]
# For brievity, the comments following vectored instructions only refer to
# the first of a pair of QWORDS.
# Eg. XMM4=W[t-2] really means XMM4={W[t-2]|W[t-1]}
# The computation of the message schedule and the rounds are tightly
# stitched to take advantage of instruction-level parallelism.
idx = \rnd - 2
vmovdqa W_t(idx), %xmm4 # XMM4 = W[t-2]
idx = \rnd - 15
vmovdqu W_t(idx), %xmm5 # XMM5 = W[t-15]
mov f_64, T1
vpsrlq $61, %xmm4, %xmm0 # XMM0 = W[t-2]>>61
mov e_64, tmp0
vpsrlq $1, %xmm5, %xmm6 # XMM6 = W[t-15]>>1
xor g_64, T1
RORQ tmp0, 23 # 41
vpsrlq $19, %xmm4, %xmm1 # XMM1 = W[t-2]>>19
and e_64, T1
xor e_64, tmp0
vpxor %xmm1, %xmm0, %xmm0 # XMM0 = W[t-2]>>61 ^ W[t-2]>>19
xor g_64, T1
idx = \rnd
add WK_2(idx), T1#
vpsrlq $8, %xmm5, %xmm7 # XMM7 = W[t-15]>>8
RORQ tmp0, 4 # 18
vpsrlq $6, %xmm4, %xmm2 # XMM2 = W[t-2]>>6
xor e_64, tmp0
mov a_64, T2
add h_64, T1
vpxor %xmm7, %xmm6, %xmm6 # XMM6 = W[t-15]>>1 ^ W[t-15]>>8
RORQ tmp0, 14 # 14
add tmp0, T1
vpsrlq $7, %xmm5, %xmm8 # XMM8 = W[t-15]>>7
mov a_64, tmp0
xor c_64, T2
vpsllq $(64-61), %xmm4, %xmm3 # XMM3 = W[t-2]<<3
and c_64, tmp0
and b_64, T2
vpxor %xmm3, %xmm2, %xmm2 # XMM2 = W[t-2]>>6 ^ W[t-2]<<3
xor tmp0, T2
mov a_64, tmp0
vpsllq $(64-1), %xmm5, %xmm9 # XMM9 = W[t-15]<<63
RORQ tmp0, 5 # 39
vpxor %xmm9, %xmm8, %xmm8 # XMM8 = W[t-15]>>7 ^ W[t-15]<<63
xor a_64, tmp0
add T1, d_64
RORQ tmp0, 6 # 34
xor a_64, tmp0
vpxor %xmm8, %xmm6, %xmm6 # XMM6 = W[t-15]>>1 ^ W[t-15]>>8 ^
# W[t-15]>>7 ^ W[t-15]<<63
lea (T1, T2), h_64
RORQ tmp0, 28 # 28
vpsllq $(64-19), %xmm4, %xmm4 # XMM4 = W[t-2]<<25
add tmp0, h_64
RotateState
vpxor %xmm4, %xmm0, %xmm0 # XMM0 = W[t-2]>>61 ^ W[t-2]>>19 ^
# W[t-2]<<25
mov f_64, T1
vpxor %xmm2, %xmm0, %xmm0 # XMM0 = s1(W[t-2])
mov e_64, tmp0
xor g_64, T1
idx = \rnd - 16
vpaddq W_t(idx), %xmm0, %xmm0 # XMM0 = s1(W[t-2]) + W[t-16]
idx = \rnd - 7
vmovdqu W_t(idx), %xmm1 # XMM1 = W[t-7]
RORQ tmp0, 23 # 41
and e_64, T1
xor e_64, tmp0
xor g_64, T1
vpsllq $(64-8), %xmm5, %xmm5 # XMM5 = W[t-15]<<56
idx = \rnd + 1
add WK_2(idx), T1
vpxor %xmm5, %xmm6, %xmm6 # XMM6 = s0(W[t-15])
RORQ tmp0, 4 # 18
vpaddq %xmm6, %xmm0, %xmm0 # XMM0 = s1(W[t-2]) + W[t-16] + s0(W[t-15])
xor e_64, tmp0
vpaddq %xmm1, %xmm0, %xmm0 # XMM0 = W[t] = s1(W[t-2]) + W[t-7] +
# s0(W[t-15]) + W[t-16]
mov a_64, T2
add h_64, T1
RORQ tmp0, 14 # 14
add tmp0, T1
idx = \rnd
vmovdqa %xmm0, W_t(idx) # Store W[t]
vpaddq K_t(idx), %xmm0, %xmm0 # Compute W[t]+K[t]
vmovdqa %xmm0, WK_2(idx) # Store W[t]+K[t] for next rounds
mov a_64, tmp0
xor c_64, T2
and c_64, tmp0
and b_64, T2
xor tmp0, T2
mov a_64, tmp0
RORQ tmp0, 5 # 39
xor a_64, tmp0
add T1, d_64
RORQ tmp0, 6 # 34
xor a_64, tmp0
lea (T1, T2), h_64
RORQ tmp0, 28 # 28
add tmp0, h_64
RotateState
.endm
########################################################################
# void sha512_transform_avx(const void* M, void* D, u64 L)
# Purpose: Updates the SHA512 digest stored at D with the message stored in M.
# The size of the message pointed to by M must be an integer multiple of SHA512
# message blocks.
# L is the message length in SHA512 blocks
########################################################################
ENTRY(sha512_transform_avx)
cmp $0, msglen
je nowork
# Allocate Stack Space
mov %rsp, %rax
sub $frame_size, %rsp
and $~(0x20 - 1), %rsp
mov %rax, frame_RSPSAVE(%rsp)
# Save GPRs
mov %rbx, frame_GPRSAVE(%rsp)
mov %r12, frame_GPRSAVE +8*1(%rsp)
mov %r13, frame_GPRSAVE +8*2(%rsp)
mov %r14, frame_GPRSAVE +8*3(%rsp)
mov %r15, frame_GPRSAVE +8*4(%rsp)
updateblock:
# Load state variables
mov DIGEST(0), a_64
mov DIGEST(1), b_64
mov DIGEST(2), c_64
mov DIGEST(3), d_64
mov DIGEST(4), e_64
mov DIGEST(5), f_64
mov DIGEST(6), g_64
mov DIGEST(7), h_64
t = 0
.rept 80/2 + 1
# (80 rounds) / (2 rounds/iteration) + (1 iteration)
# +1 iteration because the scheduler leads hashing by 1 iteration
.if t < 2
# BSWAP 2 QWORDS
vmovdqa XMM_QWORD_BSWAP(%rip), %xmm1
vmovdqu MSG(t), %xmm0
vpshufb %xmm1, %xmm0, %xmm0 # BSWAP
vmovdqa %xmm0, W_t(t) # Store Scheduled Pair
vpaddq K_t(t), %xmm0, %xmm0 # Compute W[t]+K[t]
vmovdqa %xmm0, WK_2(t) # Store into WK for rounds
.elseif t < 16
# BSWAP 2 QWORDS# Compute 2 Rounds
vmovdqu MSG(t), %xmm0
vpshufb %xmm1, %xmm0, %xmm0 # BSWAP
SHA512_Round t-2 # Round t-2
vmovdqa %xmm0, W_t(t) # Store Scheduled Pair
vpaddq K_t(t), %xmm0, %xmm0 # Compute W[t]+K[t]
SHA512_Round t-1 # Round t-1
vmovdqa %xmm0, WK_2(t)# Store W[t]+K[t] into WK
.elseif t < 79
# Schedule 2 QWORDS# Compute 2 Rounds
SHA512_2Sched_2Round_avx t
.else
# Compute 2 Rounds
SHA512_Round t-2
SHA512_Round t-1
.endif
t = t+2
.endr
# Update digest
add a_64, DIGEST(0)
add b_64, DIGEST(1)
add c_64, DIGEST(2)
add d_64, DIGEST(3)
add e_64, DIGEST(4)
add f_64, DIGEST(5)
add g_64, DIGEST(6)
add h_64, DIGEST(7)
# Advance to next message block
add $16*8, msg
dec msglen
jnz updateblock
# Restore GPRs
mov frame_GPRSAVE(%rsp), %rbx
mov frame_GPRSAVE +8*1(%rsp), %r12
mov frame_GPRSAVE +8*2(%rsp), %r13
mov frame_GPRSAVE +8*3(%rsp), %r14
mov frame_GPRSAVE +8*4(%rsp), %r15
# Restore Stack Pointer
mov frame_RSPSAVE(%rsp), %rsp
nowork:
ret
ENDPROC(sha512_transform_avx)
########################################################################
### Binary Data
.data
.align 16
# Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb.
XMM_QWORD_BSWAP:
.octa 0x08090a0b0c0d0e0f0001020304050607
# K[t] used in SHA512 hashing
K512:
.quad 0x428a2f98d728ae22,0x7137449123ef65cd
.quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
.quad 0x3956c25bf348b538,0x59f111f1b605d019
.quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118
.quad 0xd807aa98a3030242,0x12835b0145706fbe
.quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
.quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1
.quad 0x9bdc06a725c71235,0xc19bf174cf692694
.quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3
.quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
.quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483
.quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5
.quad 0x983e5152ee66dfab,0xa831c66d2db43210
.quad 0xb00327c898fb213f,0xbf597fc7beef0ee4
.quad 0xc6e00bf33da88fc2,0xd5a79147930aa725
.quad 0x06ca6351e003826f,0x142929670a0e6e70
.quad 0x27b70a8546d22ffc,0x2e1b21385c26c926
.quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df
.quad 0x650a73548baf63de,0x766a0abb3c77b2a8
.quad 0x81c2c92e47edaee6,0x92722c851482353b
.quad 0xa2bfe8a14cf10364,0xa81a664bbc423001
.quad 0xc24b8b70d0f89791,0xc76c51a30654be30
.quad 0xd192e819d6ef5218,0xd69906245565a910
.quad 0xf40e35855771202a,0x106aa07032bbd1b8
.quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53
.quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
.quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
.quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
.quad 0x748f82ee5defb2fc,0x78a5636f43172f60
.quad 0x84c87814a1f0ab72,0x8cc702081a6439ec
.quad 0x90befffa23631e28,0xa4506cebde82bde9
.quad 0xbef9a3f7b2c67915,0xc67178f2e372532b
.quad 0xca273eceea26619c,0xd186b8c721c0c207
.quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
.quad 0x06f067aa72176fba,0x0a637dc5a2c898a6
.quad 0x113f9804bef90dae,0x1b710b35131c471b
.quad 0x28db77f523047d84,0x32caab7b40c72493
.quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
.quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a
.quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,686 @@
########################################################################
# Implement fast SHA-512 with SSSE3 instructions. (x86_64)
#
# Copyright (C) 2013 Intel Corporation.
#
# Authors:
# James Guilford <james.guilford@intel.com>
# Kirk Yap <kirk.s.yap@intel.com>
# David Cote <david.m.cote@intel.com>
# Tim Chen <tim.c.chen@linux.intel.com>
#
# This software is available to you under a choice of one of two
# licenses. You may choose to be licensed under the terms of the GNU
# General Public License (GPL) Version 2, available from the file
# COPYING in the main directory of this source tree, or the
# OpenIB.org BSD license below:
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# - Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# - Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 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.
#
########################################################################
#
# This code is described in an Intel White-Paper:
# "Fast SHA-512 Implementations on Intel Architecture Processors"
#
# To find it, surf to http://www.intel.com/p/en_US/embedded
# and search for that title.
#
########################################################################
# Using this part of Minio codebase under the license
# Apache License Version 2.0 with modifications
##
#ifdef HAS_SSE41
#ifndef ENTRY
#define ENTRY(name) \
.globl name ; \
.align 4,0x90 ; \
name:
#endif
#ifndef END
#define END(name) \
.size name, .-name
#endif
#ifndef ENDPROC
#define ENDPROC(name) \
.type name, @function ; \
END(name)
#endif
#define NUM_INVALID 100
#define TYPE_R32 0
#define TYPE_R64 1
#define TYPE_XMM 2
#define TYPE_INVALID 100
.macro R32_NUM opd r32
\opd = NUM_INVALID
.ifc \r32,%eax
\opd = 0
.endif
.ifc \r32,%ecx
\opd = 1
.endif
.ifc \r32,%edx
\opd = 2
.endif
.ifc \r32,%ebx
\opd = 3
.endif
.ifc \r32,%esp
\opd = 4
.endif
.ifc \r32,%ebp
\opd = 5
.endif
.ifc \r32,%esi
\opd = 6
.endif
.ifc \r32,%edi
\opd = 7
.endif
#ifdef X86_64
.ifc \r32,%r8d
\opd = 8
.endif
.ifc \r32,%r9d
\opd = 9
.endif
.ifc \r32,%r10d
\opd = 10
.endif
.ifc \r32,%r11d
\opd = 11
.endif
.ifc \r32,%r12d
\opd = 12
.endif
.ifc \r32,%r13d
\opd = 13
.endif
.ifc \r32,%r14d
\opd = 14
.endif
.ifc \r32,%r15d
\opd = 15
.endif
#endif
.endm
.macro R64_NUM opd r64
\opd = NUM_INVALID
#ifdef X86_64
.ifc \r64,%rax
\opd = 0
.endif
.ifc \r64,%rcx
\opd = 1
.endif
.ifc \r64,%rdx
\opd = 2
.endif
.ifc \r64,%rbx
\opd = 3
.endif
.ifc \r64,%rsp
\opd = 4
.endif
.ifc \r64,%rbp
\opd = 5
.endif
.ifc \r64,%rsi
\opd = 6
.endif
.ifc \r64,%rdi
\opd = 7
.endif
.ifc \r64,%r8
\opd = 8
.endif
.ifc \r64,%r9
\opd = 9
.endif
.ifc \r64,%r10
\opd = 10
.endif
.ifc \r64,%r11
\opd = 11
.endif
.ifc \r64,%r12
\opd = 12
.endif
.ifc \r64,%r13
\opd = 13
.endif
.ifc \r64,%r14
\opd = 14
.endif
.ifc \r64,%r15
\opd = 15
.endif
#endif
.endm
.macro XMM_NUM opd xmm
\opd = NUM_INVALID
.ifc \xmm,%xmm0
\opd = 0
.endif
.ifc \xmm,%xmm1
\opd = 1
.endif
.ifc \xmm,%xmm2
\opd = 2
.endif
.ifc \xmm,%xmm3
\opd = 3
.endif
.ifc \xmm,%xmm4
\opd = 4
.endif
.ifc \xmm,%xmm5
\opd = 5
.endif
.ifc \xmm,%xmm6
\opd = 6
.endif
.ifc \xmm,%xmm7
\opd = 7
.endif
.ifc \xmm,%xmm8
\opd = 8
.endif
.ifc \xmm,%xmm9
\opd = 9
.endif
.ifc \xmm,%xmm10
\opd = 10
.endif
.ifc \xmm,%xmm11
\opd = 11
.endif
.ifc \xmm,%xmm12
\opd = 12
.endif
.ifc \xmm,%xmm13
\opd = 13
.endif
.ifc \xmm,%xmm14
\opd = 14
.endif
.ifc \xmm,%xmm15
\opd = 15
.endif
.endm
.macro TYPE type reg
R32_NUM reg_type_r32 \reg
R64_NUM reg_type_r64 \reg
XMM_NUM reg_type_xmm \reg
.if reg_type_r64 <> NUM_INVALID
\type = TYPE_R64
.elseif reg_type_r32 <> NUM_INVALID
\type = TYPE_R32
.elseif reg_type_xmm <> NUM_INVALID
\type = TYPE_XMM
.else
\type = TYPE_INVALID
.endif
.endm
.macro PFX_OPD_SIZE
.byte 0x66
.endm
.macro PFX_REX opd1 opd2 W=0
.if ((\opd1 | \opd2) & 8) || \W
.byte 0x40 | ((\opd1 & 8) >> 3) | ((\opd2 & 8) >> 1) | (\W << 3)
.endif
.endm
.macro MODRM mod opd1 opd2
.byte \mod | (\opd1 & 7) | ((\opd2 & 7) << 3)
.endm
.macro PSHUFB_XMM xmm1 xmm2
XMM_NUM pshufb_opd1 \xmm1
XMM_NUM pshufb_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX pshufb_opd1 pshufb_opd2
.byte 0x0f, 0x38, 0x00
MODRM 0xc0 pshufb_opd1 pshufb_opd2
.endm
.macro PCLMULQDQ imm8 xmm1 xmm2
XMM_NUM clmul_opd1 \xmm1
XMM_NUM clmul_opd2 \xmm2
PFX_OPD_SIZE
PFX_REX clmul_opd1 clmul_opd2
.byte 0x0f, 0x3a, 0x44
MODRM 0xc0 clmul_opd1 clmul_opd2
.byte \imm8
.endm
.macro PEXTRD imm8 xmm gpr
R32_NUM extrd_opd1 \gpr
XMM_NUM extrd_opd2 \xmm
PFX_OPD_SIZE
PFX_REX extrd_opd1 extrd_opd2
.byte 0x0f, 0x3a, 0x16
MODRM 0xc0 extrd_opd1 extrd_opd2
.byte \imm8
.endm
.macro MOVQ_R64_XMM opd1 opd2
TYPE movq_r64_xmm_opd1_type \opd1
.if movq_r64_xmm_opd1_type == TYPE_XMM
XMM_NUM movq_r64_xmm_opd1 \opd1
R64_NUM movq_r64_xmm_opd2 \opd2
.else
R64_NUM movq_r64_xmm_opd1 \opd1
XMM_NUM movq_r64_xmm_opd2 \opd2
.endif
PFX_OPD_SIZE
PFX_REX movq_r64_xmm_opd1 movq_r64_xmm_opd2 1
.if movq_r64_xmm_opd1_type == TYPE_XMM
.byte 0x0f, 0x7e
.else
.byte 0x0f, 0x6e
.endif
MODRM 0xc0 movq_r64_xmm_opd1 movq_r64_xmm_opd2
.endm
.text
# Virtual Registers
# ARG1
msg = %rdi
# ARG2
digest = %rsi
# ARG3
msglen = %rdx
T1 = %rcx
T2 = %r8
a_64 = %r9
b_64 = %r10
c_64 = %r11
d_64 = %r12
e_64 = %r13
f_64 = %r14
g_64 = %r15
h_64 = %rbx
tmp0 = %rax
# Local variables (stack frame)
W_SIZE = 80*8
WK_SIZE = 2*8
RSPSAVE_SIZE = 1*8
GPRSAVE_SIZE = 5*8
frame_W = 0
frame_WK = frame_W + W_SIZE
frame_RSPSAVE = frame_WK + WK_SIZE
frame_GPRSAVE = frame_RSPSAVE + RSPSAVE_SIZE
frame_size = frame_GPRSAVE + GPRSAVE_SIZE
# Useful QWORD "arrays" for simpler memory references
# MSG, DIGEST, K_t, W_t are arrays
# WK_2(t) points to 1 of 2 qwords at frame.WK depdending on t being odd/even
# Input message (arg1)
#define MSG(i) 8*i(msg)
# Output Digest (arg2)
#define DIGEST(i) 8*i(digest)
# SHA Constants (static mem)
#define K_t(i) 8*i+K512(%rip)
# Message Schedule (stack frame)
#define W_t(i) 8*i+frame_W(%rsp)
# W[t]+K[t] (stack frame)
#define WK_2(i) 8*((i%2))+frame_WK(%rsp)
.macro RotateState
# Rotate symbols a..h right
TMP = h_64
h_64 = g_64
g_64 = f_64
f_64 = e_64
e_64 = d_64
d_64 = c_64
c_64 = b_64
b_64 = a_64
a_64 = TMP
.endm
.macro SHA512_Round rnd
# Compute Round %%t
mov f_64, T1 # T1 = f
mov e_64, tmp0 # tmp = e
xor g_64, T1 # T1 = f ^ g
ror $23, tmp0 # 41 # tmp = e ror 23
and e_64, T1 # T1 = (f ^ g) & e
xor e_64, tmp0 # tmp = (e ror 23) ^ e
xor g_64, T1 # T1 = ((f ^ g) & e) ^ g = CH(e,f,g)
idx = \rnd
add WK_2(idx), T1 # W[t] + K[t] from message scheduler
ror $4, tmp0 # 18 # tmp = ((e ror 23) ^ e) ror 4
xor e_64, tmp0 # tmp = (((e ror 23) ^ e) ror 4) ^ e
mov a_64, T2 # T2 = a
add h_64, T1 # T1 = CH(e,f,g) + W[t] + K[t] + h
ror $14, tmp0 # 14 # tmp = ((((e ror23)^e)ror4)^e)ror14 = S1(e)
add tmp0, T1 # T1 = CH(e,f,g) + W[t] + K[t] + S1(e)
mov a_64, tmp0 # tmp = a
xor c_64, T2 # T2 = a ^ c
and c_64, tmp0 # tmp = a & c
and b_64, T2 # T2 = (a ^ c) & b
xor tmp0, T2 # T2 = ((a ^ c) & b) ^ (a & c) = Maj(a,b,c)
mov a_64, tmp0 # tmp = a
ror $5, tmp0 # 39 # tmp = a ror 5
xor a_64, tmp0 # tmp = (a ror 5) ^ a
add T1, d_64 # e(next_state) = d + T1
ror $6, tmp0 # 34 # tmp = ((a ror 5) ^ a) ror 6
xor a_64, tmp0 # tmp = (((a ror 5) ^ a) ror 6) ^ a
lea (T1, T2), h_64 # a(next_state) = T1 + Maj(a,b,c)
ror $28, tmp0 # 28 # tmp = ((((a ror5)^a)ror6)^a)ror28 = S0(a)
add tmp0, h_64 # a(next_state) = T1 + Maj(a,b,c) S0(a)
RotateState
.endm
.macro SHA512_2Sched_2Round_sse rnd
# Compute rounds t-2 and t-1
# Compute message schedule QWORDS t and t+1
# Two rounds are computed based on the values for K[t-2]+W[t-2] and
# K[t-1]+W[t-1] which were previously stored at WK_2 by the message
# scheduler.
# The two new schedule QWORDS are stored at [W_t(%%t)] and [W_t(%%t+1)].
# They are then added to their respective SHA512 constants at
# [K_t(%%t)] and [K_t(%%t+1)] and stored at dqword [WK_2(%%t)]
# For brievity, the comments following vectored instructions only refer to
# the first of a pair of QWORDS.
# Eg. XMM2=W[t-2] really means XMM2={W[t-2]|W[t-1]}
# The computation of the message schedule and the rounds are tightly
# stitched to take advantage of instruction-level parallelism.
# For clarity, integer instructions (for the rounds calculation) are indented
# by one tab. Vectored instructions (for the message scheduler) are indented
# by two tabs.
mov f_64, T1
idx = \rnd -2
movdqa W_t(idx), %xmm2 # XMM2 = W[t-2]
xor g_64, T1
and e_64, T1
movdqa %xmm2, %xmm0 # XMM0 = W[t-2]
xor g_64, T1
idx = \rnd
add WK_2(idx), T1
idx = \rnd - 15
movdqu W_t(idx), %xmm5 # XMM5 = W[t-15]
mov e_64, tmp0
ror $23, tmp0 # 41
movdqa %xmm5, %xmm3 # XMM3 = W[t-15]
xor e_64, tmp0
ror $4, tmp0 # 18
psrlq $61-19, %xmm0 # XMM0 = W[t-2] >> 42
xor e_64, tmp0
ror $14, tmp0 # 14
psrlq $(8-7), %xmm3 # XMM3 = W[t-15] >> 1
add tmp0, T1
add h_64, T1
pxor %xmm2, %xmm0 # XMM0 = (W[t-2] >> 42) ^ W[t-2]
mov a_64, T2
xor c_64, T2
pxor %xmm5, %xmm3 # XMM3 = (W[t-15] >> 1) ^ W[t-15]
and b_64, T2
mov a_64, tmp0
psrlq $(19-6), %xmm0 # XMM0 = ((W[t-2]>>42)^W[t-2])>>13
and c_64, tmp0
xor tmp0, T2
psrlq $(7-1), %xmm3 # XMM3 = ((W[t-15]>>1)^W[t-15])>>6
mov a_64, tmp0
ror $5, tmp0 # 39
pxor %xmm2, %xmm0 # XMM0 = (((W[t-2]>>42)^W[t-2])>>13)^W[t-2]
xor a_64, tmp0
ror $6, tmp0 # 34
pxor %xmm5, %xmm3 # XMM3 = (((W[t-15]>>1)^W[t-15])>>6)^W[t-15]
xor a_64, tmp0
ror $28, tmp0 # 28
psrlq $6, %xmm0 # XMM0 = ((((W[t-2]>>42)^W[t-2])>>13)^W[t-2])>>6
add tmp0, T2
add T1, d_64
psrlq $1, %xmm3 # XMM3 = (((W[t-15]>>1)^W[t-15])>>6)^W[t-15]>>1
lea (T1, T2), h_64
RotateState
movdqa %xmm2, %xmm1 # XMM1 = W[t-2]
mov f_64, T1
xor g_64, T1
movdqa %xmm5, %xmm4 # XMM4 = W[t-15]
and e_64, T1
xor g_64, T1
psllq $(64-19)-(64-61) , %xmm1 # XMM1 = W[t-2] << 42
idx = \rnd + 1
add WK_2(idx), T1
mov e_64, tmp0
psllq $(64-1)-(64-8), %xmm4 # XMM4 = W[t-15] << 7
ror $23, tmp0 # 41
xor e_64, tmp0
pxor %xmm2, %xmm1 # XMM1 = (W[t-2] << 42)^W[t-2]
ror $4, tmp0 # 18
xor e_64, tmp0
pxor %xmm5, %xmm4 # XMM4 = (W[t-15]<<7)^W[t-15]
ror $14, tmp0 # 14
add tmp0, T1
psllq $(64-61), %xmm1 # XMM1 = ((W[t-2] << 42)^W[t-2])<<3
add h_64, T1
mov a_64, T2
psllq $(64-8), %xmm4 # XMM4 = ((W[t-15]<<7)^W[t-15])<<56
xor c_64, T2
and b_64, T2
pxor %xmm1, %xmm0 # XMM0 = s1(W[t-2])
mov a_64, tmp0
and c_64, tmp0
idx = \rnd - 7
movdqu W_t(idx), %xmm1 # XMM1 = W[t-7]
xor tmp0, T2
pxor %xmm4, %xmm3 # XMM3 = s0(W[t-15])
mov a_64, tmp0
paddq %xmm3, %xmm0 # XMM0 = s1(W[t-2]) + s0(W[t-15])
ror $5, tmp0 # 39
idx =\rnd-16
paddq W_t(idx), %xmm0 # XMM0 = s1(W[t-2]) + s0(W[t-15]) + W[t-16]
xor a_64, tmp0
paddq %xmm1, %xmm0 # XMM0 = s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]
ror $6, tmp0 # 34
movdqa %xmm0, W_t(\rnd) # Store scheduled qwords
xor a_64, tmp0
paddq K_t(\rnd), %xmm0 # Compute W[t]+K[t]
ror $28, tmp0 # 28
idx = \rnd
movdqa %xmm0, WK_2(idx) # Store W[t]+K[t] for next rounds
add tmp0, T2
add T1, d_64
lea (T1, T2), h_64
RotateState
.endm
########################################################################
# void sha512_transform_ssse3(const void* M, void* D, u64 L)#
# Purpose: Updates the SHA512 digest stored at D with the message stored in M.
# The size of the message pointed to by M must be an integer multiple of SHA512
# message blocks.
# L is the message length in SHA512 blocks.
########################################################################
ENTRY(sha512_transform_ssse3)
cmp $0, msglen
je nowork
# Allocate Stack Space
mov %rsp, %rax
sub $frame_size, %rsp
and $~(0x20 - 1), %rsp
mov %rax, frame_RSPSAVE(%rsp)
# Save GPRs
mov %rbx, frame_GPRSAVE(%rsp)
mov %r12, frame_GPRSAVE +8*1(%rsp)
mov %r13, frame_GPRSAVE +8*2(%rsp)
mov %r14, frame_GPRSAVE +8*3(%rsp)
mov %r15, frame_GPRSAVE +8*4(%rsp)
updateblock:
# Load state variables
mov DIGEST(0), a_64
mov DIGEST(1), b_64
mov DIGEST(2), c_64
mov DIGEST(3), d_64
mov DIGEST(4), e_64
mov DIGEST(5), f_64
mov DIGEST(6), g_64
mov DIGEST(7), h_64
t = 0
.rept 80/2 + 1
# (80 rounds) / (2 rounds/iteration) + (1 iteration)
# +1 iteration because the scheduler leads hashing by 1 iteration
.if t < 2
# BSWAP 2 QWORDS
movdqa XMM_QWORD_BSWAP(%rip), %xmm1
movdqu MSG(t), %xmm0
pshufb %xmm1, %xmm0 # BSWAP
movdqa %xmm0, W_t(t) # Store Scheduled Pair
paddq K_t(t), %xmm0 # Compute W[t]+K[t]
movdqa %xmm0, WK_2(t) # Store into WK for rounds
.elseif t < 16
# BSWAP 2 QWORDS# Compute 2 Rounds
movdqu MSG(t), %xmm0
pshufb %xmm1, %xmm0 # BSWAP
SHA512_Round t-2 # Round t-2
movdqa %xmm0, W_t(t) # Store Scheduled Pair
paddq K_t(t), %xmm0 # Compute W[t]+K[t]
SHA512_Round t-1 # Round t-1
movdqa %xmm0, WK_2(t) # Store W[t]+K[t] into WK
.elseif t < 79
# Schedule 2 QWORDS# Compute 2 Rounds
SHA512_2Sched_2Round_sse t
.else
# Compute 2 Rounds
SHA512_Round t-2
SHA512_Round t-1
.endif
t = t+2
.endr
# Update digest
add a_64, DIGEST(0)
add b_64, DIGEST(1)
add c_64, DIGEST(2)
add d_64, DIGEST(3)
add e_64, DIGEST(4)
add f_64, DIGEST(5)
add g_64, DIGEST(6)
add h_64, DIGEST(7)
# Advance to next message block
add $16*8, msg
dec msglen
jnz updateblock
# Restore GPRs
mov frame_GPRSAVE(%rsp), %rbx
mov frame_GPRSAVE +8*1(%rsp), %r12
mov frame_GPRSAVE +8*2(%rsp), %r13
mov frame_GPRSAVE +8*3(%rsp), %r14
mov frame_GPRSAVE +8*4(%rsp), %r15
# Restore Stack Pointer
mov frame_RSPSAVE(%rsp), %rsp
nowork:
ret
ENDPROC(sha512_transform_ssse3)
########################################################################
### Binary Data
.data
.align 16
# Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb.
XMM_QWORD_BSWAP:
.octa 0x08090a0b0c0d0e0f0001020304050607
# K[t] used in SHA512 hashing
K512:
.quad 0x428a2f98d728ae22,0x7137449123ef65cd
.quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
.quad 0x3956c25bf348b538,0x59f111f1b605d019
.quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118
.quad 0xd807aa98a3030242,0x12835b0145706fbe
.quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
.quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1
.quad 0x9bdc06a725c71235,0xc19bf174cf692694
.quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3
.quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
.quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483
.quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5
.quad 0x983e5152ee66dfab,0xa831c66d2db43210
.quad 0xb00327c898fb213f,0xbf597fc7beef0ee4
.quad 0xc6e00bf33da88fc2,0xd5a79147930aa725
.quad 0x06ca6351e003826f,0x142929670a0e6e70
.quad 0x27b70a8546d22ffc,0x2e1b21385c26c926
.quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df
.quad 0x650a73548baf63de,0x766a0abb3c77b2a8
.quad 0x81c2c92e47edaee6,0x92722c851482353b
.quad 0xa2bfe8a14cf10364,0xa81a664bbc423001
.quad 0xc24b8b70d0f89791,0xc76c51a30654be30
.quad 0xd192e819d6ef5218,0xd69906245565a910
.quad 0xf40e35855771202a,0x106aa07032bbd1b8
.quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53
.quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
.quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
.quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
.quad 0x748f82ee5defb2fc,0x78a5636f43172f60
.quad 0x84c87814a1f0ab72,0x8cc702081a6439ec
.quad 0x90befffa23631e28,0xa4506cebde82bde9
.quad 0xbef9a3f7b2c67915,0xc67178f2e372532b
.quad 0xca273eceea26619c,0xd186b8c721c0c207
.quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
.quad 0x06f067aa72176fba,0x0a637dc5a2c898a6
.quad 0x113f9804bef90dae,0x1b710b35131c471b
.quad 0x28db77f523047d84,0x32caab7b40c72493
.quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
.quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a
.quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817
#endif
+58
View File
@@ -0,0 +1,58 @@
/*
* Minio Cloud Storage, (C) 2014 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sha512
import (
"hash"
"io"
"crypto/sha512"
)
// The size of a SHA512 checksum in bytes.
const (
Size = sha512.Size
)
// Sum512 - single caller sha512 helper
func Sum512(data []byte) []byte {
d := sha512.New()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha512 helper
func Sum(reader io.Reader) ([]byte, error) {
d := sha512.New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
d.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return d.Sum(nil), nil
}
// New returns a new hash.Hash computing SHA512.
func New() hash.Hash {
return sha512.New()
}
+184
View File
@@ -0,0 +1,184 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
// Using this part of Minio codebase under the license
// Apache License Version 2.0 with modifications
// Package sha512 implements the SHA512 hash algorithms as defined
// in FIPS 180-2.
package sha512
import (
"hash"
"io"
"github.com/minio/minio-xl/pkg/cpu"
)
// The size of a SHA512 checksum in bytes.
const Size = 64
// The blocksize of SHA512 in bytes.
const BlockSize = 128
const (
chunk = 128
init0 = 0x6a09e667f3bcc908
init1 = 0xbb67ae8584caa73b
init2 = 0x3c6ef372fe94f82b
init3 = 0xa54ff53a5f1d36f1
init4 = 0x510e527fade682d1
init5 = 0x9b05688c2b3e6c1f
init6 = 0x1f83d9abfb41bd6b
init7 = 0x5be0cd19137e2179
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [8]uint64
x [chunk]byte
nx int
len uint64
}
func block(dig *digest, p []byte) {
switch true {
case cpu.HasAVX2() == true:
blockAVX2(dig, p)
case cpu.HasAVX() == true:
blockAVX(dig, p)
case cpu.HasSSE41() == true:
blockSSE(dig, p)
default:
blockGeneric(dig, p)
}
}
// Reset digest to its default value
func (d *digest) Reset() {
d.h[0] = init0
d.h[1] = init1
d.h[2] = init2
d.h[3] = init3
d.h[4] = init4
d.h[5] = init5
d.h[6] = init6
d.h[7] = init7
d.nx = 0
d.len = 0
}
// New returns a new hash.Hash computing the SHA512 checksum.
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
// Return output array byte size
func (d *digest) Size() int { return Size }
// Return blockSize
func (d *digest) BlockSize() int { return BlockSize }
// Write blocks
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == chunk {
block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
// Calculate sha512
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
// internal checksum calculation, returns [Size]byte
func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 112 bytes mod 128.
len := d.len
var tmp [128]byte
tmp[0] = 0x80
if len%128 < 112 {
d.Write(tmp[0 : 112-len%128])
} else {
d.Write(tmp[0 : 128+112-len%128])
}
// Length in bits.
len <<= 3
for i := uint(0); i < 16; i++ {
tmp[i] = byte(len >> (120 - 8*i))
}
d.Write(tmp[0:16])
if d.nx != 0 {
panic("d.nx != 0")
}
h := d.h[:]
var digest [Size]byte
for i, s := range h {
digest[i*8] = byte(s >> 56)
digest[i*8+1] = byte(s >> 48)
digest[i*8+2] = byte(s >> 40)
digest[i*8+3] = byte(s >> 32)
digest[i*8+4] = byte(s >> 24)
digest[i*8+5] = byte(s >> 16)
digest[i*8+6] = byte(s >> 8)
digest[i*8+7] = byte(s)
}
return digest
}
/// Convenience functions
// Sum512 - single caller sha512 helper
func Sum512(data []byte) []byte {
var d digest
d.Reset()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha512 helper
func Sum(reader io.Reader) ([]byte, error) {
h := New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
h.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return h.Sum(nil), nil
}
+125
View File
@@ -0,0 +1,125 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
// Using this part of Minio codebase under the license
// Apache License Version 2.0 with modifications
// SHA512 hash algorithm. See FIPS 180-2.
package sha512
import (
"fmt"
"io"
"testing"
)
type sha512Test struct {
out string
in string
}
var golden = []sha512Test{
{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""},
{"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", "a"},
{"2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d", "ab"},
{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"},
{"d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f", "abcd"},
{"878ae65a92e86cac011a570d4c30a7eaec442b85ce8eca0c2952b5e3cc0628c2e79d889ad4d5c7c626986d452dd86374b6ffaa7cd8b67665bef2289a5c70b0a1", "abcde"},
{"e32ef19623e8ed9d267f657a81944b3d07adbb768518068e88435745564e8d4150a0a703be2a7d88b61e3d390c2bb97e2d4c311fdc69d6b1267f05f59aa920e7", "abcdef"},
{"d716a4188569b68ab1b6dfac178e570114cdf0ea3a1cc0e31486c3e41241bc6a76424e8c37ab26f096fc85ef9886c8cb634187f4fddff645fb099f1ff54c6b8c", "abcdefg"},
{"a3a8c81bc97c2560010d7389bc88aac974a104e0e2381220c6e084c4dccd1d2d17d4f86db31c2a851dc80e6681d74733c55dcd03dd96f6062cdda12a291ae6ce", "abcdefgh"},
{"f22d51d25292ca1d0f68f69aedc7897019308cc9db46efb75a03dd494fc7f126c010e8ade6a00a0c1a5f1b75d81e0ed5a93ce98dc9b833db7839247b1d9c24fe", "abcdefghi"},
{"ef6b97321f34b1fea2169a7db9e1960b471aa13302a988087357c520be957ca119c3ba68e6b4982c019ec89de3865ccf6a3cda1fe11e59f98d99f1502c8b9745", "abcdefghij"},
{"2210d99af9c8bdecda1b4beff822136753d8342505ddce37f1314e2cdbb488c6016bdaa9bd2ffa513dd5de2e4b50f031393d8ab61f773b0e0130d7381e0f8a1d", "Discard medicine more than two years old."},
{"a687a8985b4d8d0a24f115fe272255c6afaf3909225838546159c1ed685c211a203796ae8ecc4c81a5b6315919b3a64f10713da07e341fcdbb08541bf03066ce", "He who has a shady past knows that nice guys finish last."},
{"8ddb0392e818b7d585ab22769a50df660d9f6d559cca3afc5691b8ca91b8451374e42bcdabd64589ed7c91d85f626596228a5c8572677eb98bc6b624befb7af8", "I wouldn't marry him with a ten foot pole."},
{"26ed8f6ca7f8d44b6a8a54ae39640fa8ad5c673f70ee9ce074ba4ef0d483eea00bab2f61d8695d6b34df9c6c48ae36246362200ed820448bdc03a720366a87c6", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
{"e5a14bf044be69615aade89afcf1ab0389d5fc302a884d403579d1386a2400c089b0dbb387ed0f463f9ee342f8244d5a38cfbc0e819da9529fbff78368c9a982", "The days of the digital watch are numbered. -Tom Stoppard"},
{"420a1faa48919e14651bed45725abe0f7a58e0f099424c4e5a49194946e38b46c1f8034b18ef169b2e31050d1648e0b982386595f7df47da4b6fd18e55333015", "Nepal premier won't resign."},
{"d926a863beadb20134db07683535c72007b0e695045876254f341ddcccde132a908c5af57baa6a6a9c63e6649bba0c213dc05fadcf9abccea09f23dcfb637fbe", "For every action there is an equal and opposite government program."},
{"9a98dd9bb67d0da7bf83da5313dff4fd60a4bac0094f1b05633690ffa7f6d61de9a1d4f8617937d560833a9aaa9ccafe3fd24db418d0e728833545cadd3ad92d", "His money is twice tainted: 'taint yours and 'taint mine."},
{"d7fde2d2351efade52f4211d3746a0780a26eec3df9b2ed575368a8a1c09ec452402293a8ea4eceb5a4f60064ea29b13cdd86918cd7a4faf366160b009804107", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
{"b0f35ffa2697359c33a56f5c0cf715c7aeed96da9905ca2698acadb08fbc9e669bf566b6bd5d61a3e86dc22999bcc9f2224e33d1d4f32a228cf9d0349e2db518", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
{"3d2e5f91778c9e66f7e061293aaa8a8fc742dd3b2e4f483772464b1144189b49273e610e5cccd7a81a19ca1fa70f16b10f1a100a4d8c1372336be8484c64b311", "size: a.out: bad magic"},
{"b2f68ff58ac015efb1c94c908b0d8c2bf06f491e4de8e6302c49016f7f8a33eac3e959856c7fddbc464de618701338a4b46f76dbfaf9a1e5262b5f40639771c7", "The major problem is with sendmail. -Mark Horton"},
{"d8c92db5fdf52cf8215e4df3b4909d29203ff4d00e9ad0b64a6a4e04dec5e74f62e7c35c7fb881bd5de95442123df8f57a489b0ae616bd326f84d10021121c57", "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
{"19a9f8dc0a233e464e8566ad3ca9b91e459a7b8c4780985b015776e1bf239a19bc233d0556343e2b0a9bc220900b4ebf4f8bdf89ff8efeaf79602d6849e6f72e", "If the enemy is within range, then so are you."},
{"00b4c41f307bde87301cdc5b5ab1ae9a592e8ecbb2021dd7bc4b34e2ace60741cc362560bec566ba35178595a91932b8d5357e2c9cec92d393b0fa7831852476", "It's well we cannot hear the screams/That we create in others' dreams."},
{"91eccc3d5375fd026e4d6787874b1dce201cecd8a27dbded5065728cb2d09c58a3d467bb1faf353bf7ba567e005245d5321b55bc344f7c07b91cb6f26c959be7", "You remind me of a TV show, but that's all right: I watch it anyway."},
{"fabbbe22180f1f137cfdc9556d2570e775d1ae02a597ded43a72a40f9b485d500043b7be128fb9fcd982b83159a0d99aa855a9e7cc4240c00dc01a9bdf8218d7", "C is as portable as Stonehedge!!"},
{"2ecdec235c1fa4fc2a154d8fba1dddb8a72a1ad73838b51d792331d143f8b96a9f6fcb0f34d7caa351fe6d88771c4f105040e0392f06e0621689d33b2f3ba92e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
{"7ad681f6f96f82f7abfa7ecc0334e8fa16d3dc1cdc45b60b7af43fe4075d2357c0c1d60e98350f1afb1f2fe7a4d7cd2ad55b88e458e06b73c40b437331f5dab4", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
{"833f9248ab4a3b9e5131f745fda1ffd2dd435b30e965957e78291c7ab73605fd1912b0794e5c233ab0a12d205a39778d19b83515d6a47003f19cdee51d98c7e0", "How can you write a big system without C++? -Paul Glick"},
}
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
s := fmt.Sprintf("%x", Sum512([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum512 function: sha512(%s) = %s want %s", g.in, s, g.out)
}
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out)
}
c.Reset()
}
}
}
func TestSize(t *testing.T) {
c := New()
if got := c.Size(); got != Size {
t.Errorf("Size = %d; want %d", got, Size)
}
}
func TestBlockSize(t *testing.T) {
c := New()
if got := c.BlockSize(); got != BlockSize {
t.Errorf("BlockSize = %d; want %d", got, BlockSize)
}
}
var bench = New()
var buf = make([]byte, 1024*1024)
func benchmarkSize(b *testing.B, size int) {
b.SetBytes(int64(size))
sum := make([]byte, bench.Size())
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8)
}
func BenchmarkHash1K(b *testing.B) {
benchmarkSize(b, 1024)
}
func BenchmarkHash8K(b *testing.B) {
benchmarkSize(b, 8192)
}
func BenchmarkHash1M(b *testing.B) {
benchmarkSize(b, 1024*1024)
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Minio Cloud Storage, (C) 2014 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sha512
import (
"hash"
"io"
"crypto/sha512"
)
// The size of a SHA512 checksum in bytes.
const (
Size = sha512.Size
)
// Sum512 - single caller sha512 helper
func Sum512(data []byte) []byte {
d := sha512.New()
d.Write(data)
return d.Sum(nil)
}
// Sum - io.Reader based streaming sha512 helper
func Sum(reader io.Reader) ([]byte, error) {
d := sha512.New()
var err error
for err == nil {
length := 0
byteBuffer := make([]byte, 1024*1024)
length, err = reader.Read(byteBuffer)
byteBuffer = byteBuffer[0:length]
d.Write(byteBuffer)
}
if err != io.EOF {
return nil, err
}
return d.Sum(nil), nil
}
// New returns a new hash.Hash computing SHA512.
func New() hash.Hash {
return sha512.New()
}
+180
View File
@@ -0,0 +1,180 @@
// +build amd64
//
// Minio Cloud Storage, (C) 2015 Minio, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Software block transform are provided by The Go Authors:
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of
// Golang project:
// https://github.com/golang/go/blob/master/LICENSE
package sha512
// #cgo CFLAGS: -DHAS_SSE41 -DHAS_AVX -DHAS_AVX2
// #include <stdint.h>
// void sha512_transform_ssse3 (const void* M, void* D, uint64_t L);
// void sha512_transform_avx (const void* M, void* D, uint64_t L);
// void sha512_transform_rorx (const void* M, void* D, uint64_t L);
import "C"
import (
"unsafe"
)
func blockSSE(dig *digest, p []byte) {
C.sha512_transform_ssse3(unsafe.Pointer(&p[0]), unsafe.Pointer(&dig.h[0]), (C.uint64_t)(len(p)/chunk))
}
func blockAVX(dig *digest, p []byte) {
C.sha512_transform_avx(unsafe.Pointer(&p[0]), unsafe.Pointer(&dig.h[0]), (C.uint64_t)(len(p)/chunk))
}
func blockAVX2(dig *digest, p []byte) {
C.sha512_transform_rorx(unsafe.Pointer(&p[0]), unsafe.Pointer(&dig.h[0]), (C.uint64_t)(len(p)/chunk))
}
func blockGeneric(dig *digest, p []byte) {
var w [80]uint64
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= chunk {
for i := 0; i < 16; i++ {
j := i * 8
w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 |
uint64(p[j+4])<<24 | uint64(p[j+5])<<16 | uint64(p[j+6])<<8 | uint64(p[j+7])
}
for i := 16; i < 80; i++ {
v1 := w[i-2]
t1 := (v1>>19 | v1<<(64-19)) ^ (v1>>61 | v1<<(64-61)) ^ (v1 >> 6)
v2 := w[i-15]
t2 := (v2>>1 | v2<<(64-1)) ^ (v2>>8 | v2<<(64-8)) ^ (v2 >> 7)
w[i] = t1 + w[i-7] + t2 + w[i-16]
}
a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7
for i := 0; i < 80; i++ {
t1 := h + ((e>>14 | e<<(64-14)) ^ (e>>18 | e<<(64-18)) ^ (e>>41 | e<<(64-41))) + ((e & f) ^ (^e & g)) + _K[i] + w[i]
t2 := ((a>>28 | a<<(64-28)) ^ (a>>34 | a<<(64-34)) ^ (a>>39 | a<<(64-39))) + ((a & b) ^ (a & c) ^ (b & c))
h = g
g = f
f = e
e = d + t1
d = c
c = b
b = a
a = t1 + t2
}
h0 += a
h1 += b
h2 += c
h3 += d
h4 += e
h5 += f
h6 += g
h7 += h
p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
}
var _K = []uint64{
0x428a2f98d728ae22,
0x7137449123ef65cd,
0xb5c0fbcfec4d3b2f,
0xe9b5dba58189dbbc,
0x3956c25bf348b538,
0x59f111f1b605d019,
0x923f82a4af194f9b,
0xab1c5ed5da6d8118,
0xd807aa98a3030242,
0x12835b0145706fbe,
0x243185be4ee4b28c,
0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f,
0x80deb1fe3b1696b1,
0x9bdc06a725c71235,
0xc19bf174cf692694,
0xe49b69c19ef14ad2,
0xefbe4786384f25e3,
0x0fc19dc68b8cd5b5,
0x240ca1cc77ac9c65,
0x2de92c6f592b0275,
0x4a7484aa6ea6e483,
0x5cb0a9dcbd41fbd4,
0x76f988da831153b5,
0x983e5152ee66dfab,
0xa831c66d2db43210,
0xb00327c898fb213f,
0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2,
0xd5a79147930aa725,
0x06ca6351e003826f,
0x142929670a0e6e70,
0x27b70a8546d22ffc,
0x2e1b21385c26c926,
0x4d2c6dfc5ac42aed,
0x53380d139d95b3df,
0x650a73548baf63de,
0x766a0abb3c77b2a8,
0x81c2c92e47edaee6,
0x92722c851482353b,
0xa2bfe8a14cf10364,
0xa81a664bbc423001,
0xc24b8b70d0f89791,
0xc76c51a30654be30,
0xd192e819d6ef5218,
0xd69906245565a910,
0xf40e35855771202a,
0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8,
0x1e376c085141ab53,
0x2748774cdf8eeb99,
0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63,
0x4ed8aa4ae3418acb,
0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc,
0x78a5636f43172f60,
0x84c87814a1f0ab72,
0x8cc702081a6439ec,
0x90befffa23631e28,
0xa4506cebde82bde9,
0xbef9a3f7b2c67915,
0xc67178f2e372532b,
0xca273eceea26619c,
0xd186b8c721c0c207,
0xeada7dd6cde0eb1e,
0xf57d4f7fee6ed178,
0x06f067aa72176fba,
0x0a637dc5a2c898a6,
0x113f9804bef90dae,
0x1b710b35131c471b,
0x28db77f523047d84,
0x32caab7b40c72493,
0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c,
0x4cc5d4becb3e42b6,
0x597f299cfc657e2a,
0x5fcb6fab3ad6faec,
0x6c44198c4a475817,
}
+30
View File
@@ -0,0 +1,30 @@
BSD License
For grace software
Copyright (c) 2015, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+214
View File
@@ -0,0 +1,214 @@
// +build linux darwin freebsd openbsd netbsd dragonfly
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package minhttp provides easy to use graceful restart for a set of HTTP services
//
// This package is a fork from https://github.com/facebookgo/grace
//
// Re-licensing with Apache License 2.0, with code modifications
package minhttp
import (
"crypto/tls"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/facebookgo/httpdown"
"github.com/minio/minio-xl/pkg/probe"
)
// An app contains one or more servers and their associated configuration.
type app struct {
servers []*http.Server
listeners []net.Listener
sds []httpdown.Server
net *minNet
errors chan *probe.Error
}
// listen initailize listeners
func (a *app) listen() *probe.Error {
for _, s := range a.servers {
l, err := a.net.Listen("tcp", s.Addr)
if err != nil {
return err.Trace()
}
if s.TLSConfig != nil {
l = tls.NewListener(l, s.TLSConfig)
}
a.listeners = append(a.listeners, l)
}
return nil
}
// serve start serving all listeners
func (a *app) serve() {
h := &httpdown.HTTP{
StopTimeout: 10 * time.Second,
KillTimeout: 1 * time.Second,
}
for i, s := range a.servers {
a.sds = append(a.sds, h.Serve(s, a.listeners[i]))
}
}
// wait for http server to signal all requests that have been served
func (a *app) wait() {
var wg sync.WaitGroup
wg.Add(len(a.sds) * 2) // Wait & Stop
go a.trapSignal(&wg)
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Wait(); err != nil {
a.errors <- probe.NewError(err)
}
}(s)
}
wg.Wait()
}
// trapSignal wait on listed signals for pre-defined behaviors
func (a *app) trapSignal(wg *sync.WaitGroup) {
ch := make(chan os.Signal, 10)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGHUP)
for {
sig := <-ch
switch sig {
case syscall.SIGTERM:
// this ensures a subsequent TERM will trigger standard go behaviour of terminating
signal.Stop(ch)
// roll through all initialized http servers and stop them
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Stop(); err != nil {
a.errors <- probe.NewError(err)
}
}(s)
}
return
case syscall.SIGHUP:
// we only return here if there's an error, otherwise the new process
// will send us a TERM when it's ready to trigger the actual shutdown.
if _, err := a.net.StartProcess(); err != nil {
a.errors <- err.Trace()
}
}
}
}
// ListenAndServe will serve the given http.Servers and will monitor for signals
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2/SIGHUP).
func ListenAndServe(servers ...*http.Server) *probe.Error {
// get parent process id
ppid := os.Getppid()
a := &app{
servers: servers,
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
net: &minNet{},
errors: make(chan *probe.Error, 1+(len(servers)*2)),
}
// Acquire Listeners
if err := a.listen(); err != nil {
return err.Trace()
}
// Start serving.
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
if os.Getenv("LISTEN_FDS") != "" && ppid != 1 {
if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
return probe.NewError(err)
}
}
waitdone := make(chan struct{})
go func() {
defer close(waitdone)
a.wait()
// communicate by sending not by closing a channel
waitdone <- struct{}{}
}()
select {
case err := <-a.errors:
if err == nil {
panic("unexpected nil error")
}
return err.Trace()
case <-waitdone:
return nil
}
}
// ListenAndServeLimited is similar to ListenAndServe but ratelimited with connLimit value
func ListenAndServeLimited(connLimit int, servers ...*http.Server) *probe.Error {
// get parent process id
ppid := os.Getppid()
a := &app{
servers: servers,
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
net: &minNet{connLimit: connLimit},
errors: make(chan *probe.Error, 1+(len(servers)*2)),
}
// Acquire Listeners
if err := a.listen(); err != nil {
return err.Trace()
}
// Start serving.
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
if os.Getenv("LISTEN_FDS") != "" && ppid != 1 {
if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
return probe.NewError(err)
}
}
waitdone := make(chan struct{})
go func() {
defer close(waitdone)
a.wait()
// communicate by sending not by closing a channel
waitdone <- struct{}{}
}()
select {
case err := <-a.errors:
if err == nil {
panic("unexpected nil error")
}
return err.Trace()
case <-waitdone:
return nil
}
}
+214
View File
@@ -0,0 +1,214 @@
// +build windows
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package minhttp provides easy to use graceful restart for a set of HTTP services
//
// This package is a fork from https://github.com/facebookgo/grace
//
// Re-licensing with Apache License 2.0, with code modifications
package minhttp
import (
"crypto/tls"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/facebookgo/httpdown"
"github.com/minio/minio-xl/pkg/probe"
)
// An app contains one or more servers and their associated configuration.
type app struct {
servers []*http.Server
listeners []net.Listener
sds []httpdown.Server
net *minNet
errors chan *probe.Error
}
// listen initailize listeners
func (a *app) listen() *probe.Error {
for _, s := range a.servers {
l, err := a.net.Listen("tcp", s.Addr)
if err != nil {
return err.Trace()
}
if s.TLSConfig != nil {
l = tls.NewListener(l, s.TLSConfig)
}
a.listeners = append(a.listeners, l)
}
return nil
}
// serve start serving all listeners
func (a *app) serve() {
h := &httpdown.HTTP{
StopTimeout: 10 * time.Second,
KillTimeout: 1 * time.Second,
}
for i, s := range a.servers {
a.sds = append(a.sds, h.Serve(s, a.listeners[i]))
}
}
// wait for http server to signal all requests that have been served
func (a *app) wait() {
var wg sync.WaitGroup
wg.Add(len(a.sds) * 2) // Wait & Stop
go a.trapSignal(&wg)
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Wait(); err != nil {
a.errors <- probe.NewError(err)
}
}(s)
}
wg.Wait()
}
// trapSignal wait on listed signals for pre-defined behaviors
func (a *app) trapSignal(wg *sync.WaitGroup) {
ch := make(chan os.Signal, 10)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGHUP)
for {
sig := <-ch
switch sig {
case syscall.SIGTERM:
// this ensures a subsequent TERM will trigger standard go behaviour of terminating
signal.Stop(ch)
// roll through all initialized http servers and stop them
for _, s := range a.sds {
go func(s httpdown.Server) {
defer wg.Done()
if err := s.Stop(); err != nil {
a.errors <- probe.NewError(err)
}
}(s)
}
return
case syscall.SIGHUP:
// we only return here if there's an error, otherwise the new process
// will send us a TERM when it's ready to trigger the actual shutdown.
if _, err := a.net.StartProcess(); err != nil {
a.errors <- err.Trace()
}
}
}
}
// ListenAndServe will serve the given http.Servers and will monitor for signals
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2/SIGHUP).
func ListenAndServe(servers ...*http.Server) *probe.Error {
// get parent process id
ppid := os.Getppid()
a := &app{
servers: servers,
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
net: &minNet{},
errors: make(chan *probe.Error, 1+(len(servers)*2)),
}
// Acquire Listeners
if err := a.listen(); err != nil {
return err.Trace()
}
// Start serving.
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
if os.Getenv("LISTEN_FDS") != "" && ppid != 1 {
if err := terminateProcess(ppid, 1); err != nil {
return probe.NewError(err)
}
}
waitdone := make(chan struct{})
go func() {
defer close(waitdone)
a.wait()
// communicate by sending not by closing a channel
waitdone <- struct{}{}
}()
select {
case err := <-a.errors:
if err == nil {
panic("unexpected nil error")
}
return err.Trace()
case <-waitdone:
return nil
}
}
// ListenAndServeLimited is similar to ListenAndServe but ratelimited with connLimit value
func ListenAndServeLimited(connLimit int, servers ...*http.Server) *probe.Error {
// get parent process id
ppid := os.Getppid()
a := &app{
servers: servers,
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
net: &minNet{connLimit: connLimit},
errors: make(chan *probe.Error, 1+(len(servers)*2)),
}
// Acquire Listeners
if err := a.listen(); err != nil {
return err.Trace()
}
// Start serving.
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
if os.Getenv("LISTEN_FDS") != "" && ppid != 1 {
if err := terminateProcess(ppid, 1); err != nil {
return probe.NewError(err)
}
}
waitdone := make(chan struct{})
go func() {
defer close(waitdone)
a.wait()
// communicate by sending not by closing a channel
waitdone <- struct{}{}
}()
select {
case err := <-a.errors:
if err == nil {
panic("unexpected nil error")
}
return err.Trace()
case <-waitdone:
return nil
}
}
+34
View File
@@ -0,0 +1,34 @@
// !build windows
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minhttp
import (
"os"
"syscall"
)
func terminateProcess(pid, exitcode int) error {
h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid))
if e != nil {
return os.NewSyscallError("OpenProcess", e)
}
defer syscall.CloseHandle(h)
e = syscall.TerminateProcess(h, uint32(exitcode))
return os.NewSyscallError("TerminateProcess", e)
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minhttp
import (
"net"
"os"
"sync"
)
// rateLimitedListener returns a Listener that accepts at most n simultaneous
// connections from the provided Listener.
func rateLimitedListener(l net.Listener, nconn int) net.Listener {
if nconn > 0 {
return &rateLimitListener{l, make(chan struct{}, nconn)}
}
return l
}
type rateLimitListener struct {
net.Listener
sem chan struct{}
}
func (l *rateLimitListener) accept() { l.sem <- struct{}{} }
func (l *rateLimitListener) release() { <-l.sem }
// File - necessary to expose underlying socket fd
func (l *rateLimitListener) File() (f *os.File, err error) {
return l.Listener.(fileListener).File()
}
// Accept - accept method for accepting new connections
func (l *rateLimitListener) Accept() (net.Conn, error) {
l.accept()
c, err := l.Listener.Accept()
if err != nil {
l.release()
return nil, err
}
return &rateLimitListenerConn{Conn: c, release: l.release}, nil
}
type rateLimitListenerConn struct {
net.Conn
releaseOnce sync.Once
release func()
}
func (l *rateLimitListenerConn) Close() error {
err := l.Conn.Close()
l.releaseOnce.Do(l.release)
return err
}
+274
View File
@@ -0,0 +1,274 @@
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minhttp
import (
"fmt"
"net"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"github.com/minio/minio-xl/pkg/probe"
)
// This package is a fork https://github.com/facebookgo/grace
//
// Re-licensing with Apache License 2.0, with code modifications
// This package provides a family of Listen functions that either open a
// fresh connection or provide an inherited connection from when the process
// was started. This behaves like their counterparts in the net pacakge, but
// transparently provide support for graceful restarts without dropping
// connections. This is provided in a systemd socket activation compatible form
// to allow using socket activation.
//
const (
// Used to indicate a graceful restart in the new process.
envCountKey = "LISTEN_FDS" // similar to systemd SDS_LISTEN_FDS
envCountKeyPrefix = envCountKey + "="
)
// In order to keep the working directory the same as when we started we record
// it at startup.
var originalWD, _ = os.Getwd()
// minNet provides the family of Listen functions and maintains the associated
// state. Typically you will have only once instance of minNet per application.
type minNet struct {
inheritedListeners []net.Listener
activeListeners []net.Listener
connLimit int
mutex sync.Mutex
inheritOnce sync.Once
}
// minAddr simple wrapper over net.Addr interface to implement IsEqual()
type minAddr struct {
net.Addr
}
// fileListener simple interface to extract file pointers from different types of net.Listener's
type fileListener interface {
File() (*os.File, error)
}
// getInheritedListeners - look for LISTEN_FDS in environment variables and populate listeners accordingly
func (n *minNet) getInheritedListeners() *probe.Error {
var retErr *probe.Error
n.inheritOnce.Do(func() {
n.mutex.Lock()
defer n.mutex.Unlock()
countStr := os.Getenv(envCountKey)
if countStr == "" {
return
}
count, err := strconv.Atoi(countStr)
if err != nil {
retErr = probe.NewError(fmt.Errorf("found invalid count value: %s=%s", envCountKey, countStr))
return
}
fdStart := 3
for i := fdStart; i < fdStart+count; i++ {
file := os.NewFile(uintptr(i), "listener")
l, err := net.FileListener(file)
if err != nil {
file.Close()
retErr = probe.NewError(err)
return
}
if err := file.Close(); err != nil {
retErr = probe.NewError(err)
return
}
n.inheritedListeners = append(n.inheritedListeners, l)
}
})
if retErr != nil {
return retErr.Trace()
}
return nil
}
// Listen announces on the local network address laddr. The network net must be
// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
// returns an inherited net.Listener for the matching network and address, or
// creates a new one using net.Listen()
func (n *minNet) Listen(nett, laddr string) (net.Listener, *probe.Error) {
switch nett {
default:
return nil, probe.NewError(net.UnknownNetworkError(nett))
case "tcp", "tcp4", "tcp6":
addr, err := net.ResolveTCPAddr(nett, laddr)
if err != nil {
return nil, probe.NewError(err)
}
return n.ListenTCP(nett, addr)
case "unix", "unixpacket":
addr, err := net.ResolveUnixAddr(nett, laddr)
if err != nil {
return nil, probe.NewError(err)
}
return n.ListenUnix(nett, addr)
}
}
// ListenTCP announces on the local network address laddr. The network net must
// be: "tcp", "tcp4" or "tcp6". It returns an inherited net.Listener for the
// matching network and address, or creates a new one using net.ListenTCP.
func (n *minNet) ListenTCP(nett string, laddr *net.TCPAddr) (net.Listener, *probe.Error) {
if err := n.getInheritedListeners(); err != nil {
return nil, err.Trace()
}
n.mutex.Lock()
defer n.mutex.Unlock()
// look for an inherited listener
for i, l := range n.inheritedListeners {
if l == nil { // we nil used inherited listeners
continue
}
equal := minAddr{l.Addr()}.IsEqual(laddr)
if equal {
n.inheritedListeners[i] = nil
n.activeListeners = append(n.activeListeners, l)
return l.(*net.TCPListener), nil
}
}
// make a fresh listener
l, err := net.ListenTCP(nett, laddr)
if err != nil {
return nil, probe.NewError(err)
}
n.activeListeners = append(n.activeListeners, rateLimitedListener(l, n.connLimit))
return l, nil
}
// ListenUnix announces on the local network address laddr. The network net
// must be a: "unix" or "unixpacket". It returns an inherited net.Listener for
// the matching network and address, or creates a new one using net.ListenUnix.
func (n *minNet) ListenUnix(nett string, laddr *net.UnixAddr) (net.Listener, *probe.Error) {
if err := n.getInheritedListeners(); err != nil {
return nil, err.Trace()
}
n.mutex.Lock()
defer n.mutex.Unlock()
// look for an inherited listener
for i, l := range n.inheritedListeners {
if l == nil { // we nil used inherited listeners
continue
}
equal := minAddr{l.Addr()}.IsEqual(laddr)
if equal {
n.inheritedListeners[i] = nil
n.activeListeners = append(n.activeListeners, l)
return l.(*net.UnixListener), nil
}
}
// make a fresh listener
l, err := net.ListenUnix(nett, laddr)
if err != nil {
return nil, probe.NewError(err)
}
n.activeListeners = append(n.activeListeners, rateLimitedListener(l, n.connLimit))
return l, nil
}
// activeListeners returns a snapshot copy of the active listeners.
func (n *minNet) getActiveListeners() []net.Listener {
n.mutex.Lock()
defer n.mutex.Unlock()
ls := make([]net.Listener, len(n.activeListeners))
copy(ls, n.activeListeners)
return ls
}
// IsEqual is synonymous with IP.IsEqual() method, here IsEqual matches net.Addr instead of net.IP
func (n1 minAddr) IsEqual(n2 net.Addr) bool {
if n1.Network() != n2.Network() {
return false
}
a1h, a1p, _ := net.SplitHostPort(n1.String())
a2h, a2p, _ := net.SplitHostPort(n2.String())
// Special cases since Addr() from net.Listener will
// add frivolous [::] ipv6 for no ":[PORT]" style addresses
if a1h == "::" && a2h == "" && a1p == a2p {
return true
}
if a2h == "::" && a1h == "" && a1p == a2p {
return true
}
if net.ParseIP(a1h).Equal(net.ParseIP(a2h)) && a1p == a2p {
return true
}
return false
}
// StartProcess starts a new process passing it the active listeners. It
// doesn't fork, but starts a new process using the same environment and
// arguments as when it was originally started. This allows for a newly
// deployed binary to be started. It returns the pid of the newly started
// process when successful.
func (n *minNet) StartProcess() (int, *probe.Error) {
listeners := n.getActiveListeners()
// Extract the fds from the listeners.
files := make([]*os.File, len(listeners))
for i, l := range listeners {
var err error
files[i], err = l.(fileListener).File()
if err != nil {
return 0, probe.NewError(err)
}
defer files[i].Close()
}
// Use the original binary location. This works with symlinks such that if
// the file it points to has been changed we will use the updated symlink.
argv0, err := exec.LookPath(os.Args[0])
if err != nil {
return 0, probe.NewError(err)
}
// Pass on the environment and replace the old count key with the new one.
var env []string
for _, v := range os.Environ() {
if !strings.HasPrefix(v, envCountKeyPrefix) {
env = append(env, v)
}
}
env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: originalWD,
Env: env,
Files: allFiles,
})
if err != nil {
return 0, probe.NewError(err)
}
return process.Pid, nil
}
+182
View File
@@ -0,0 +1,182 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses)/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package probe implements a simple mechanism to trace and return errors in large programs.
package probe
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"github.com/dustin/go-humanize"
)
// GetSysInfo returns useful system statistics.
func GetSysInfo() map[string]string {
host, err := os.Hostname()
if err != nil {
host = ""
}
memstats := &runtime.MemStats{}
runtime.ReadMemStats(memstats)
return map[string]string{
"host.name": host,
"host.os": runtime.GOOS,
"host.arch": runtime.GOARCH,
"host.lang": runtime.Version(),
"host.cpus": strconv.Itoa(runtime.NumCPU()),
"mem.used": humanize.Bytes(memstats.Alloc),
"mem.total": humanize.Bytes(memstats.Sys),
"mem.heap.used": humanize.Bytes(memstats.HeapAlloc),
"mem.heap.total": humanize.Bytes(memstats.HeapSys),
}
}
// TracePoint container for individual trace entries in overall call trace
type TracePoint struct {
Line int `json:"line,omitempty"`
Filename string `json:"file,omitempty"`
Function string `json:"func,omitempty"`
Env map[string][]string `json:"env,omitempty"`
}
// Error implements tracing error functionality.
type Error struct {
lock sync.RWMutex
Cause error `json:"cause,omitempty"`
CallTrace []TracePoint `json:"trace,omitempty"`
SysInfo map[string]string `json:"sysinfo,omitempty"`
}
// NewError function instantiates an error probe for tracing.
// Default ``error`` (golang's error interface) is injected in
// only once. Rest of the time, you trace the return path with
// ``probe.Trace`` and finally handling them at top level
//
// Following dummy code talks about how one can pass up the
// errors and put them in CallTrace.
//
// func sendError() *probe.Error {
// return probe.NewError(errors.New("Help Needed"))
// }
// func recvError() *probe.Error {
// return sendError().Trace()
// }
// if err := recvError(); err != nil {
// log.Fatalln(err.Trace())
// }
//
func NewError(e error) *Error {
if e == nil {
return nil
}
Err := Error{lock: sync.RWMutex{}, Cause: e, CallTrace: []TracePoint{}, SysInfo: GetSysInfo()}
return Err.trace()
}
// Trace records the point at which it is invoked.
// Stack traces are important for debugging purposes.
func (e *Error) Trace(fields ...string) *Error {
if e == nil {
return nil
}
e.lock.Lock()
defer e.lock.Unlock()
return e.trace(fields...)
}
// Internal trace - records the point at which it is invoked.
func (e *Error) trace(fields ...string) *Error {
if e == nil {
return nil
}
pc, file, line, _ := runtime.Caller(2)
function := runtime.FuncForPC(pc).Name()
_, function = filepath.Split(function)
file = "..." + strings.TrimPrefix(file, os.Getenv("GOPATH")) // trim gopathSource from file
tp := TracePoint{}
if len(fields) > 0 {
tp = TracePoint{Line: line, Filename: file, Function: function, Env: map[string][]string{"Tags": fields}}
} else {
tp = TracePoint{Line: line, Filename: file, Function: function}
}
e.CallTrace = append(e.CallTrace, tp)
return e
}
// Untrace erases last known trace entry.
func (e *Error) Untrace() *Error {
if e == nil {
return nil
}
e.lock.Lock()
defer e.lock.Unlock()
l := len(e.CallTrace)
if l == 0 {
return nil
}
e.CallTrace = e.CallTrace[:l-1]
return e
}
// ToGoError returns original error message.
func (e *Error) ToGoError() error {
if e == nil || e.Cause == nil {
return nil
}
return e.Cause
}
// String returns error message.
func (e *Error) String() string {
if e == nil || e.Cause == nil {
return "<nil>"
}
e.lock.RLock()
defer e.lock.RUnlock()
if e.Cause != nil {
str := e.Cause.Error()
callLen := len(e.CallTrace)
for i := callLen - 1; i >= 0; i-- {
if len(e.CallTrace[i].Env) > 0 {
str += fmt.Sprintf("\n (%d) %s:%d %s(..) Tags: [%s]",
i, e.CallTrace[i].Filename, e.CallTrace[i].Line, e.CallTrace[i].Function, strings.Join(e.CallTrace[i].Env["Tags"], ", "))
} else {
str += fmt.Sprintf("\n (%d) %s:%d %s(..)",
i, e.CallTrace[i].Filename, e.CallTrace[i].Line, e.CallTrace[i].Function)
}
}
str += "\n" + " Host:" + e.SysInfo["host.name"] + " | "
str += "OS:" + e.SysInfo["host.os"] + " | "
str += "Arch:" + e.SysInfo["host.arch"] + " | "
str += "Lang:" + e.SysInfo["host.lang"] + " | "
str += "Mem:" + e.SysInfo["mem.used"] + "/" + e.SysInfo["mem.total"] + " | "
str += "Heap:" + e.SysInfo["mem.heap.used"] + "/" + e.SysInfo["mem.heap.total"]
return str
}
return "<nil>"
}
+61
View File
@@ -0,0 +1,61 @@
/*
* Minio Cloud Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package probe_test
import (
"os"
"testing"
"github.com/minio/minio-xl/pkg/probe"
. "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
type MySuite struct{}
var _ = Suite(&MySuite{})
func testDummy0() *probe.Error {
_, e := os.Stat("this-file-cannot-exit")
return probe.NewError(e)
}
func testDummy1() *probe.Error {
return testDummy0().Trace("DummyTag1")
}
func testDummy2() *probe.Error {
return testDummy1().Trace("DummyTag2")
}
func (s *MySuite) TestProbe(c *C) {
es := testDummy2().Trace("TopOfStack")
// Uncomment the following Println to visually test probe call trace.
// fmt.Println("Expecting a simulated error here.", es)
c.Assert(es, Not(Equals), nil)
newES := es.Trace()
c.Assert(newES, Not(Equals), nil)
}
func (s *MySuite) TestWrappedError(c *C) {
_, e := os.Stat("this-file-cannot-exit")
es := probe.NewError(e) // *probe.Error
e = probe.WrapError(es) // *probe.WrappedError
_, ok := probe.UnwrapError(e)
c.Assert(ok, Equals, true)
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses)/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package probe implements a simple mechanism to trace and return errors in large programs.
package probe
// wrappedError implements a container for *probe.Error.
type wrappedError struct {
err *Error
}
// WrapError function wraps a *probe.Error into a 'error' compatible duck type.
func WrapError(err *Error) error {
return &wrappedError{err: err}
}
// UnwrapError tries to convert generic 'error' into typed *probe.Error and returns true, false otherwise.
func UnwrapError(err error) (*Error, bool) {
switch e := err.(type) {
case *wrappedError:
return e.err, true
default:
return nil, false
}
}
// Error interface method.
func (w *wrappedError) Error() string {
return w.err.String()
}
+96
View File
@@ -0,0 +1,96 @@
/*
* Quick - Quick key value store for config files and persistent state files
*
* Minio Client (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package quick
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/olekukonko/ts"
)
const errorFmt = "%5d: %s <-- "
// FormatJSONSyntaxError generates a pretty printed json syntax error since
// golang doesn't provide an easy way to report the location of the error
func FormatJSONSyntaxError(data io.Reader, sErr *json.SyntaxError) error {
if sErr == nil {
return nil
}
var readLine bytes.Buffer
bio := bufio.NewReader(data)
errLine := int64(1)
readBytes := int64(0)
// termWidth is set to a default one to use when we are
// not able to calculate terminal width via OS syscalls
termWidth := 25
// errorShift is the length of the minimum needed place for
// error msg accessoires, like <--, etc.. We calculate it
// dynamically to avoid an eventual bug after modifying errorFmt
errorShift := len(fmt.Sprintf(errorFmt, 1, ""))
if termSize, err := ts.GetSize(); err == nil {
termWidth = termSize.Col()
}
for {
b, err := bio.ReadByte()
if err != nil {
if err != io.EOF {
return err
}
break
}
readBytes++
if readBytes > sErr.Offset {
break
}
switch b {
case '\n':
readLine.Reset()
errLine++
case '\t':
readLine.WriteByte(' ')
case '\r':
break
default:
readLine.WriteByte(b)
}
}
lineLen := readLine.Len()
idx := lineLen - termWidth + errorShift
if idx < 0 || idx > lineLen-1 {
idx = 0
}
errorStr := fmt.Sprintf("JSON syntax error at line %d, col %d : %s.\n",
errLine, readLine.Len(), sErr)
errorStr += fmt.Sprintf(errorFmt, errLine, readLine.String()[idx:])
return errors.New(errorStr)
}
+305
View File
@@ -0,0 +1,305 @@
/*
* Quick - Quick key value store for config files and persistent state files
*
* Minio Client (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package quick
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"reflect"
"runtime"
"strings"
"sync"
"github.com/fatih/structs"
"github.com/minio/minio-xl/pkg/probe"
)
// Config - generic config interface functions
type Config interface {
String() string
Version() string
Save(string) *probe.Error
Load(string) *probe.Error
Data() interface{}
Diff(Config) ([]structs.Field, *probe.Error)
DeepDiff(Config) ([]structs.Field, *probe.Error)
}
// config - implements quick.Config interface
type config struct {
data interface{}
lock *sync.RWMutex
}
// CheckData - checks the validity of config data. Data sould be of type struct and contain a string type field called "Version"
func CheckData(data interface{}) *probe.Error {
if !structs.IsStruct(data) {
return probe.NewError(fmt.Errorf("Invalid argument type. Expecing \"struct\" type."))
}
st := structs.New(data)
f, ok := st.FieldOk("Version")
if !ok {
return probe.NewError(fmt.Errorf("Invalid type of struct argument. No [%s.Version] field found.", st.Name()))
}
if f.Kind() != reflect.String {
return probe.NewError(fmt.Errorf("Invalid type of struct argument. Expecting \"string\" type [%s.Version] field.", st.Name()))
}
return nil
}
// New - instantiate a new config
func New(data interface{}) (Config, *probe.Error) {
err := CheckData(data)
if err != nil {
return nil, err.Trace()
}
d := new(config)
d.data = data
d.lock = new(sync.RWMutex)
return d, nil
}
// CheckVersion - loads json and compares the version number provided returns back true or false - any failure
// is returned as error.
func CheckVersion(filename string, version string) (bool, *probe.Error) {
_, err := os.Stat(filename)
if err != nil {
return false, probe.NewError(err)
}
fileData, err := ioutil.ReadFile(filename)
if err != nil {
return false, probe.NewError(err)
}
if runtime.GOOS == "windows" {
fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
}
data := struct {
Version string
}{
Version: "",
}
err = json.Unmarshal(fileData, &data)
if err != nil {
switch err := err.(type) {
case *json.SyntaxError:
return false, probe.NewError(FormatJSONSyntaxError(bytes.NewReader(fileData), err))
default:
return false, probe.NewError(err)
}
}
config, perr := New(data)
if perr != nil {
return false, perr.Trace()
}
if config.Version() != version {
return false, nil
}
return true, nil
}
// Load - loads json config from filename for the a given struct data
func Load(filename string, data interface{}) (Config, *probe.Error) {
_, err := os.Stat(filename)
if err != nil {
return nil, probe.NewError(err)
}
fileData, err := ioutil.ReadFile(filename)
if err != nil {
return nil, probe.NewError(err)
}
if runtime.GOOS == "windows" {
fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
}
err = json.Unmarshal(fileData, &data)
if err != nil {
switch err := err.(type) {
case *json.SyntaxError:
return nil, probe.NewError(FormatJSONSyntaxError(bytes.NewReader(fileData), err))
default:
return nil, probe.NewError(err)
}
}
config, perr := New(data)
if perr != nil {
return nil, perr.Trace()
}
return config, nil
}
// Version returns the current config file format version
func (d config) Version() string {
st := structs.New(d.data)
f, ok := st.FieldOk("Version")
if !ok {
return ""
}
val := f.Value()
ver, ok := val.(string)
if ok {
return ver
}
return ""
}
// String converts JSON config to printable string
func (d config) String() string {
configBytes, _ := json.MarshalIndent(d.data, "", "\t")
return string(configBytes)
}
// Save writes config data in JSON format to a file.
func (d config) Save(filename string) *probe.Error {
d.lock.Lock()
defer d.lock.Unlock()
jsonData, err := json.MarshalIndent(d.data, "", "\t")
if err != nil {
return probe.NewError(err)
}
if runtime.GOOS == "windows" {
jsonData = []byte(strings.Replace(string(jsonData), "\n", "\r\n", -1))
}
err = ioutil.WriteFile(filename, jsonData, 0600)
if err != nil {
return probe.NewError(err)
}
return nil
}
// Load - loads JSON config from file and merge with currently set values
func (d *config) Load(filename string) *probe.Error {
d.lock.Lock()
defer d.lock.Unlock()
_, err := os.Stat(filename)
if err != nil {
return probe.NewError(err)
}
fileData, err := ioutil.ReadFile(filename)
if err != nil {
return probe.NewError(err)
}
if runtime.GOOS == "windows" {
fileData = []byte(strings.Replace(string(fileData), "\r\n", "\n", -1))
}
st := structs.New(d.data)
f, ok := st.FieldOk("Version")
if !ok {
return probe.NewError(fmt.Errorf("Argument struct [%s] does not contain field \"Version\".", st.Name()))
}
err = json.Unmarshal(fileData, d.data)
if err != nil {
switch err := err.(type) {
case *json.SyntaxError:
return probe.NewError(FormatJSONSyntaxError(bytes.NewReader(fileData), err))
default:
return probe.NewError(err)
}
}
if err := CheckData(d.data); err != nil {
return err.Trace()
}
if (*d).Version() != f.Value() {
return probe.NewError(fmt.Errorf("Version mismatch"))
}
return nil
}
// Data - grab internal data map for reading
func (d config) Data() interface{} {
return d.data
}
//Diff - list fields that are in A but not in B
func (d config) Diff(c Config) ([]structs.Field, *probe.Error) {
var fields []structs.Field
err := CheckData(c.Data())
if err != nil {
return []structs.Field{}, err.Trace()
}
currFields := structs.Fields(d.Data())
newFields := structs.Fields(c.Data())
found := false
for _, currField := range currFields {
found = false
for _, newField := range newFields {
if reflect.DeepEqual(currField.Name(), newField.Name()) {
found = true
}
}
if !found {
fields = append(fields, *currField)
}
}
return fields, nil
}
//DeepDiff - list fields in A that are missing or not equal to fields in B
func (d config) DeepDiff(c Config) ([]structs.Field, *probe.Error) {
var fields []structs.Field
err := CheckData(c.Data())
if err != nil {
return []structs.Field{}, err.Trace()
}
currFields := structs.Fields(d.Data())
newFields := structs.Fields(c.Data())
found := false
for _, currField := range currFields {
found = false
for _, newField := range newFields {
if reflect.DeepEqual(currField.Value(), newField.Value()) {
found = true
}
}
if !found {
fields = append(fields, *currField)
}
}
return fields, nil
}
+169
View File
@@ -0,0 +1,169 @@
/*
* Quick - Quick key value store for config files and persistent state files
*
* Minio Client (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package quick_test
import (
"os"
"testing"
"github.com/minio/minio-xl/pkg/quick"
. "gopkg.in/check.v1"
)
func Test(t *testing.T) { TestingT(t) }
type MySuite struct{}
var _ = Suite(&MySuite{})
func (s *MySuite) TestCheckData(c *C) {
err := quick.CheckData(nil)
c.Assert(err, Not(IsNil))
type myStructBad struct {
User string
Password string
Folders []string
}
saveMeBad := myStructBad{"guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = quick.CheckData(&saveMeBad)
c.Assert(err, Not(IsNil))
type myStructGood struct {
Version string
User string
Password string
Folders []string
}
saveMeGood := myStructGood{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = quick.CheckData(&saveMeGood)
c.Assert(err, IsNil)
}
func (s *MySuite) TestVersion(c *C) {
defer os.RemoveAll("test.json")
type myStruct struct {
Version string
User string
Password string
Folders []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := quick.New(&saveMe)
c.Assert(err, IsNil)
c.Assert(config, Not(IsNil))
config.Save("test.json")
valid, err := quick.CheckVersion("test.json", "1")
c.Assert(err, IsNil)
c.Assert(valid, Equals, true)
valid, err = quick.CheckVersion("test.json", "2")
c.Assert(err, IsNil)
c.Assert(valid, Equals, false)
}
func (s *MySuite) TestSaveLoad(c *C) {
defer os.RemoveAll("test.json")
type myStruct struct {
Version string
User string
Password string
Folders []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := quick.New(&saveMe)
c.Assert(err, IsNil)
c.Assert(config, Not(IsNil))
config.Save("test.json")
loadMe := myStruct{Version: "1"}
newConfig, err := quick.New(&loadMe)
c.Assert(err, IsNil)
c.Assert(newConfig, Not(IsNil))
newConfig.Load("test.json")
c.Assert(config.Data(), DeepEquals, newConfig.Data())
c.Assert(config.Data(), DeepEquals, &loadMe)
mismatch := myStruct{"1.1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
c.Assert(newConfig.Data(), Not(DeepEquals), &mismatch)
}
func (s *MySuite) TestDiff(c *C) {
type myStruct struct {
Version string
User string
Password string
Folders []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := quick.New(&saveMe)
c.Assert(err, IsNil)
c.Assert(config, Not(IsNil))
type myNewStruct struct {
Version string
// User string
Password string
Folders []string
}
mismatch := myNewStruct{"1", "nopassword", []string{"Work", "documents", "Music"}}
newConfig, err := quick.New(&mismatch)
c.Assert(err, IsNil)
c.Assert(newConfig, Not(IsNil))
fields, ok := config.Diff(newConfig)
c.Assert(ok, IsNil)
c.Assert(len(fields), Equals, 1)
// Uncomment for debugging
// for i, field := range fields {
// fmt.Printf("Diff[%d]: %s=%v\n", i, field.Name(), field.Value())
// }
}
func (s *MySuite) TestDeepDiff(c *C) {
type myStruct struct {
Version string
User string
Password string
Folders []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := quick.New(&saveMe)
c.Assert(err, IsNil)
c.Assert(config, Not(IsNil))
mismatch := myStruct{"1", "Guest", "nopassword", []string{"Work", "documents", "Music"}}
newConfig, err := quick.New(&mismatch)
c.Assert(err, IsNil)
c.Assert(newConfig, Not(IsNil))
fields, err := config.DeepDiff(newConfig)
c.Assert(err, IsNil)
c.Assert(len(fields), Equals, 2)
// Uncomment for debugging
// for i, field := range fields {
// fmt.Printf("DeepDiff[%d]: %s=%v\n", i, field.Name(), field.Value())
// }
}
-32
View File
@@ -1,32 +0,0 @@
// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
// This module is a Terminal API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems
package ts
import (
"fmt"
"testing"
)
func ExampleGetSize() {
size, _ := GetSize()
fmt.Println(size.Col()) // Get Width
fmt.Println(size.Row()) // Get Height
fmt.Println(size.PosX()) // Get X position
fmt.Println(size.PosY()) // Get Y position
}
func TestSize(t *testing.T) {
size, err := GetSize()
if err != nil {
t.Fatal(err)
}
if size.Col() == 0 || size.Row() == 0 {
t.Fatalf("Screen Size Failed")
}
}
-88
View File
@@ -1,88 +0,0 @@
package cors
import (
"net/http"
"testing"
)
type FakeResponse struct {
header http.Header
}
func (r FakeResponse) Header() http.Header {
return r.header
}
func (r FakeResponse) WriteHeader(n int) {
}
func (r FakeResponse) Write(b []byte) (n int, err error) {
return len(b), nil
}
func BenchmarkWithout(b *testing.B) {
res := FakeResponse{http.Header{}}
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
testHandler.ServeHTTP(res, req)
}
}
func BenchmarkDefault(b *testing.B) {
res := FakeResponse{http.Header{}}
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "somedomain.com")
handler := Default().Handler(testHandler)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
handler.ServeHTTP(res, req)
}
}
func BenchmarkAllowedOrigin(b *testing.B) {
res := FakeResponse{http.Header{}}
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "somedomain.com")
c := New(Options{
AllowedOrigins: []string{"somedomain.com"},
})
handler := c.Handler(testHandler)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
handler.ServeHTTP(res, req)
}
}
func BenchmarkPreflight(b *testing.B) {
res := FakeResponse{http.Header{}}
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Access-Control-Request-Method", "GET")
handler := Default().Handler(testHandler)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
handler.ServeHTTP(res, req)
}
}
func BenchmarkPreflightHeader(b *testing.B) {
res := FakeResponse{http.Header{}}
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Access-Control-Request-Method", "GET")
req.Header.Add("Access-Control-Request-Headers", "Accept")
handler := Default().Handler(testHandler)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
handler.ServeHTTP(res, req)
}
}
-371
View File
@@ -1,371 +0,0 @@
package cors
import (
"net/http"
"net/http/httptest"
"regexp"
"strings"
"testing"
)
var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("bar"))
})
func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) {
for name, value := range reqHeaders {
if actual := strings.Join(resHeaders[name], ", "); actual != value {
t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, actual)
}
}
}
func TestNoConfig(t *testing.T) {
s := New(Options{
// Intentionally left blank.
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestMatchAllOrigin(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"*"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestAllowedOrigin(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestWildcardOrigin(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://*.bar.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foo.bar.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "http://foo.bar.com",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestDisallowedOrigin(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://barbaz.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestDisallowedWildcardOrigin(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://*.bar.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foo.baz.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestAllowedOriginFunc(t *testing.T) {
r, _ := regexp.Compile("^http://foo")
s := New(Options{
AllowOriginFunc: func(o string) bool {
return r.MatchString(o)
},
})
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
res := httptest.NewRecorder()
req.Header.Set("Origin", "http://foobar.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Access-Control-Allow-Origin": "http://foobar.com",
})
res = httptest.NewRecorder()
req.Header.Set("Origin", "http://barfoo.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Access-Control-Allow-Origin": "",
})
}
func TestAllowedMethod(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowedMethods: []string{"PUT", "DELETE"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "PUT")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "PUT",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestDisallowedMethod(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowedMethods: []string{"PUT", "DELETE"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "PATCH")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestAllowedHeader(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowedHeaders: []string{"X-Header-1", "x-header-2"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "GET")
req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestAllowedWildcardHeader(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowedHeaders: []string{"*"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "GET")
req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestDisallowedHeader(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowedHeaders: []string{"X-Header-1", "x-header-2"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "GET")
req.Header.Add("Access-Control-Request-Headers", "X-Header-3, X-Header-1")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestOriginHeader(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "GET")
req.Header.Add("Access-Control-Request-Headers", "origin")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Headers": "Origin",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
func TestExposedHeader(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
ExposedHeaders: []string{"X-Header-1", "x-header-2"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "X-Header-1, X-Header-2",
})
}
func TestAllowedCredentials(t *testing.T) {
s := New(Options{
AllowedOrigins: []string{"http://foobar.com"},
AllowCredentials: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Origin", "http://foobar.com")
req.Header.Add("Access-Control-Request-Method", "GET")
s.Handler(testHandler).ServeHTTP(res, req)
assertHeaders(t, res.Header(), map[string]string{
"Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
"Access-Control-Allow-Origin": "http://foobar.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Headers": "",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Max-Age": "",
"Access-Control-Expose-Headers": "",
})
}
-70
View File
@@ -1,70 +0,0 @@
package cors
import (
"strings"
"testing"
)
func TestWildcard(t *testing.T) {
w := wildcard{"foo", "bar"}
if !w.match("foobar") {
t.Error("foo*bar should match foobar")
}
if !w.match("foobazbar") {
t.Error("foo*bar should match foobazbar")
}
if w.match("foobaz") {
t.Error("foo*bar should not match foobaz")
}
w = wildcard{"foo", "oof"}
if w.match("foof") {
t.Error("foo*oof should not match foof")
}
}
func TestConvert(t *testing.T) {
s := convert([]string{"A", "b", "C"}, strings.ToLower)
e := []string{"a", "b", "c"}
if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] {
t.Errorf("%v != %v", s, e)
}
}
func TestParseHeaderList(t *testing.T) {
h := parseHeaderList("header, second-header, THIRD-HEADER, Numb3r3d-H34d3r")
e := []string{"Header", "Second-Header", "Third-Header", "Numb3r3d-H34d3r"}
if h[0] != e[0] || h[1] != e[1] || h[2] != e[2] {
t.Errorf("%v != %v", h, e)
}
}
func TestParseHeaderListEmpty(t *testing.T) {
if len(parseHeaderList("")) != 0 {
t.Error("should be empty sclice")
}
if len(parseHeaderList(" , ")) != 0 {
t.Error("should be empty sclice")
}
}
func BenchmarkParseHeaderList(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
parseHeaderList("header, second-header, THIRD-HEADER")
}
}
func BenchmarkParseHeaderListSingle(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
parseHeaderList("header")
}
}
func BenchmarkParseHeaderListNormalized(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
parseHeaderList("Header1, Header2, Third-Header")
}
}
-25
View File
@@ -1,25 +0,0 @@
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)
}
}
-236
View File
@@ -1,236 +0,0 @@
// 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 %s, want %s", 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 %s, want %s", 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 %s, want %s", 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 %s, want %s", 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 %s, want %s", 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 %s, want %s", 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 %s, want %s", actualText, expectedText)
}
if err != nil {
t.Fatal("Could not get ConsoleScreenBufferInfo")
}
if actualAttributes != expectedAttributes {
t.Errorf("Text: %s, 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)
}
}
-24
View File
@@ -1,24 +0,0 @@
// 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
View File
@@ -1,19 +0,0 @@
// 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))
}

Some files were not shown because too many files have changed in this diff Show More