diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d8832be8fa4..2b42526d473 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -14,10 +14,6 @@ "ImportPath": "github.com/Unknwon/com", "Rev": "d9bcf409c8a368d06c9b347705c381e7c12d54df" }, - { - "ImportPath": "github.com/Unknwon/macaron", - "Rev": "93de4f3fad97bf246b838f828e2348f46f21f20a" - }, { "ImportPath": "github.com/aws/aws-sdk-go/aws", "Comment": "v1.0.0", @@ -68,6 +64,11 @@ "Comment": "v1.0.0", "Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f" }, + { + "ImportPath": "github.com/bradfitz/gomemcache/memcache", + "Comment": "release.r60-40-g72a6864", + "Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8" + }, { "ImportPath": "github.com/davecgh/go-spew/spew", "Rev": "2df174808ee097f90d259e432cc04442cf60be21" @@ -82,6 +83,22 @@ "Comment": "v1-19-g83e6542", "Rev": "83e65426fd1c06626e88aa8a085e5bfed0208e29" }, + { + "ImportPath": "github.com/go-macaron/binding", + "Rev": "2502aaf4bce3a4e6451b4610847bfb8dffdb6266" + }, + { + "ImportPath": "github.com/go-macaron/gzip", + "Rev": "4938e9be6b279d8426cb1c89a6bcf7af70b0c21d" + }, + { + "ImportPath": "github.com/go-macaron/inject", + "Rev": "c5ab7bf3a307593cd44cb272d1a5beea473dd072" + }, + { + "ImportPath": "github.com/go-macaron/session", + "Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b" + }, { "ImportPath": "github.com/go-sql-driver/mysql", "Comment": "v1.2-26-g9543750", @@ -109,19 +126,27 @@ "ImportPath": "github.com/jtolds/gls", "Rev": "f1ac7f4f24f50328e6bc838ca4437d1612a0243c" }, + { + "ImportPath": "github.com/klauspost/compress/flate", + "Rev": "7b02889a2005228347aef0e76beeaee564d82f8c" + }, + { + "ImportPath": "github.com/klauspost/compress/gzip", + "Rev": "7b02889a2005228347aef0e76beeaee564d82f8c" + }, + { + "ImportPath": "github.com/klauspost/cpuid", + "Rev": "349c675778172472f5e8f3a3e0fe187e302e5a10" + }, + { + "ImportPath": "github.com/klauspost/crc32", + "Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d" + }, { "ImportPath": "github.com/lib/pq", "Comment": "go1.0-cutoff-13-g19eeca3", "Rev": "19eeca3e30d2577b1761db471ec130810e67f532" }, - { - "ImportPath": "github.com/macaron-contrib/binding", - "Rev": "0fbe4b9707e6eb556ef843e5471592f55ce0a5e7" - }, - { - "ImportPath": "github.com/macaron-contrib/session", - "Rev": "31e841d95c7302b9ac456c830ea2d6dfcef4f84a" - }, { "ImportPath": "github.com/mattn/go-sqlite3", "Rev": "e28cd440fabdd39b9520344bc26829f61db40ece" @@ -162,15 +187,14 @@ "Comment": "v0-16-g1772191", "Rev": "177219109c97e7920c933e21c9b25f874357b237" }, + { + "ImportPath": "gopkg.in/macaron.v1", + "Rev": "1c6dd87797ae9319b4658cbd48d1d0420b279fd5" + }, { "ImportPath": "gopkg.in/redis.v2", "Comment": "v2.3.2", "Rev": "e6179049628164864e6e84e973cfb56335748dea" - }, - { - "ImportPath": "github.com/bradfitz/gomemcache/memcache", - "Comment": "release.r60-40-g72a6864", - "Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8" - } + } ] } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md b/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md deleted file mode 100644 index 8b201624f61..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md +++ /dev/null @@ -1,94 +0,0 @@ -Macaron [![Build Status](https://drone.io/github.com/Unknwon/macaron/status.png)](https://drone.io/github.com/Unknwon/macaron/latest) [![](http://gocover.io/_badge/github.com/Unknwon/macaron)](http://gocover.io/github.com/Unknwon/macaron) -======================= - -![Macaron Logo](https://raw.githubusercontent.com/Unknwon/macaron/master/macaronlogo.png) - -Package macaron is a high productive and modular design web framework in Go. - -##### Current version: 0.5.4 - -## Getting Started - -To install Macaron: - - go get github.com/Unknwon/macaron - -The very basic usage of Macaron: - -```go -package main - -import "github.com/Unknwon/macaron" - -func main() { - m := macaron.Classic() - m.Get("/", func() string { - return "Hello world!" - }) - m.Run() -} -``` - -## Features - -- Powerful routing with suburl. -- Flexible routes combinations. -- Unlimited nested group routers. -- Directly integrate with existing services. -- Dynamically change template files at runtime. -- Allow to use in-memory template and static files. -- Easy to plugin/unplugin features with modular design. -- Handy dependency injection powered by [inject](https://github.com/codegangsta/inject). -- Better router layer and less reflection make faster speed. - -## Middlewares - -Middlewares allow you easily plugin/unplugin features for your Macaron applications. - -There are already many [middlewares](https://github.com/macaron-contrib) to simplify your work: - -- gzip - Gzip compression to all requests -- render - Go template engine -- static - Serves static files -- [binding](https://github.com/macaron-contrib/binding) - Request data binding and validation -- [i18n](https://github.com/macaron-contrib/i18n) - Internationalization and Localization -- [cache](https://github.com/macaron-contrib/cache) - Cache manager -- [session](https://github.com/macaron-contrib/session) - Session manager -- [csrf](https://github.com/macaron-contrib/csrf) - Generates and validates csrf tokens -- [captcha](https://github.com/macaron-contrib/captcha) - Captcha service -- [pongo2](https://github.com/macaron-contrib/pongo2) - Pongo2 template engine support -- [sockets](https://github.com/macaron-contrib/sockets) - WebSockets channels binding -- [bindata](https://github.com/macaron-contrib/bindata) - Embed binary data as static and template files -- [toolbox](https://github.com/macaron-contrib/toolbox) - Health check, pprof, profile and statistic services -- [oauth2](https://github.com/macaron-contrib/oauth2) - OAuth 2.0 backend -- [switcher](https://github.com/macaron-contrib/switcher) - Multiple-site support -- [method](https://github.com/macaron-contrib/method) - HTTP method override -- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions -- [renders](https://github.com/macaron-contrib/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option) - -## Use Cases - -- [Gogs](https://github.com/gogits/gogs): Go Git Service -- [Gogs Web](https://github.com/gogits/gogsweb): Gogs official website -- [Go Walker](https://gowalker.org): Go online API documentation -- [Switch](https://github.com/gpmgo/switch): Gopm registry -- [YouGam](http://yougam.com): Online Forum -- [Car Girl](http://qcnl.gzsy.com/): Online campaign -- [Critical Stack Intel](https://intel.criticalstack.com/): A 100% free intel marketplace from Critical Stack, Inc. - -## Getting Help - -- [API Reference](https://gowalker.org/github.com/Unknwon/macaron) -- [Documentation](http://macaron.gogs.io) -- [FAQs](http://macaron.gogs.io/docs/faqs) -- [![Join the chat at https://gitter.im/Unknwon/macaron](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Unknwon/macaron?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -## Credits - -- Basic design of [Martini](https://github.com/go-martini/martini). -- Router layer of [beego](https://github.com/astaxie/beego). -- Logo is modified by [@insionng](https://github.com/insionng) based on [Tribal Dragon](http://xtremeyamazaki.deviantart.com/art/Tribal-Dragon-27005087). - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go deleted file mode 100644 index c4b4752e12f..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bytes" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "testing" - "time" - - "github.com/Unknwon/com" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Context(t *testing.T) { - Convey("Do advanced encapsulation operations", t, func() { - m := Classic() - m.Use(Renderers(RenderOptions{ - Directory: "fixtures/basic", - }, "fixtures/basic2")) - - Convey("Get request body", func() { - m.Get("/body1", func(ctx *Context) { - data, err := ioutil.ReadAll(ctx.Req.Body().ReadCloser()) - So(err, ShouldBeNil) - So(string(data), ShouldEqual, "This is my request body") - }) - m.Get("/body2", func(ctx *Context) { - data, err := ctx.Req.Body().Bytes() - So(err, ShouldBeNil) - So(string(data), ShouldEqual, "This is my request body") - }) - m.Get("/body3", func(ctx *Context) { - data, err := ctx.Req.Body().String() - So(err, ShouldBeNil) - So(data, ShouldEqual, "This is my request body") - }) - - for i := 1; i <= 3; i++ { - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/body"+com.ToStr(i), nil) - req.Body = ioutil.NopCloser(bytes.NewBufferString("This is my request body")) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - } - }) - - Convey("Get remote IP address", func() { - m.Get("/remoteaddr", func(ctx *Context) string { - return ctx.RemoteAddr() - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/remoteaddr", nil) - req.RemoteAddr = "127.0.0.1:3333" - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "127.0.0.1") - }) - - Convey("Render HTML", func() { - - Convey("Normal HTML", func() { - m.Get("/html", func(ctx *Context) { - ctx.HTML(304, "hello", "Unknwon") // 304 for logger test. - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/html", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "

Hello Unknwon

") - }) - - Convey("HTML template set", func() { - m.Get("/html2", func(ctx *Context) { - ctx.Data["Name"] = "Unknwon" - ctx.HTMLSet(200, "basic2", "hello2") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/html2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "

Hello Unknwon

") - }) - - Convey("With layout", func() { - m.Get("/layout", func(ctx *Context) { - ctx.HTML(200, "hello", "Unknwon", HTMLOptions{"layout"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/layout", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "head

Hello Unknwon

foot") - }) - }) - - Convey("Parse from and query", func() { - m.Get("/query", func(ctx *Context) string { - var buf bytes.Buffer - buf.WriteString(ctx.QueryTrim("name") + " ") - buf.WriteString(ctx.QueryEscape("name") + " ") - buf.WriteString(com.ToStr(ctx.QueryInt("int")) + " ") - buf.WriteString(com.ToStr(ctx.QueryInt64("int64")) + " ") - buf.WriteString(com.ToStr(ctx.QueryFloat64("float64")) + " ") - return buf.String() - }) - m.Get("/query2", func(ctx *Context) string { - var buf bytes.Buffer - buf.WriteString(strings.Join(ctx.QueryStrings("list"), ",") + " ") - buf.WriteString(strings.Join(ctx.QueryStrings("404"), ",") + " ") - return buf.String() - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/query?name=Unknwon&int=12&int64=123&float64=1.25", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Unknwon Unknwon 12 123 1.25 ") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/query2?list=item1&list=item2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "item1,item2 ") - }) - - Convey("URL parameter", func() { - m.Get("/:name/:int/:int64/:float64", func(ctx *Context) string { - var buf bytes.Buffer - ctx.SetParams("name", ctx.Params("name")) - buf.WriteString(ctx.Params("")) - buf.WriteString(ctx.Params(":name") + " ") - buf.WriteString(ctx.ParamsEscape(":name") + " ") - buf.WriteString(com.ToStr(ctx.ParamsInt(":int")) + " ") - buf.WriteString(com.ToStr(ctx.ParamsInt64(":int64")) + " ") - buf.WriteString(com.ToStr(ctx.ParamsFloat64(":float64")) + " ") - return buf.String() - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/user/1/13/1.24", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "user user 1 13 1.24 ") - }) - - Convey("Get file", func() { - m.Get("/getfile", func(ctx *Context) { - ctx.GetFile("hi") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/getfile", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - - Convey("Set and get cookie", func() { - m.Get("/set", func(ctx *Context) { - ctx.SetCookie("user", "Unknwon", 1, "/", "localhost", true, true) - ctx.SetCookie("user", "Unknwon", int32(1), "/", "localhost", 1) - ctx.SetCookie("user", "Unknwon", int64(1)) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/set", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Header().Get("Set-Cookie"), ShouldEqual, "user=Unknwon; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure") - - m.Get("/get", func(ctx *Context) string { - ctx.GetCookie("404") - So(ctx.GetCookieInt("uid"), ShouldEqual, 1) - So(ctx.GetCookieInt64("uid"), ShouldEqual, 1) - So(ctx.GetCookieFloat64("balance"), ShouldEqual, 1.25) - return ctx.GetCookie("user") - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "user=Unknwon; uid=1; balance=1.25") - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Unknwon") - }) - - Convey("Set and get secure cookie", func() { - m.SetDefaultCookieSecret("macaron") - m.Get("/set", func(ctx *Context) { - ctx.SetSecureCookie("user", "Unknwon", 1) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/set", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - m.Get("/get", func(ctx *Context) string { - name, ok := ctx.GetSecureCookie("user") - So(ok, ShouldBeTrue) - return name - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Unknwon") - }) - - Convey("Serve files", func() { - m.Get("/file", func(ctx *Context) { - ctx.ServeFile("fixtures/custom_funcs/index.tmpl") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/file", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}") - - m.Get("/file2", func(ctx *Context) { - ctx.ServeFile("fixtures/custom_funcs/index.tmpl", "ok.tmpl") - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/file2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}") - }) - - Convey("Serve file content", func() { - m.Get("/file", func(ctx *Context) { - ctx.ServeFileContent("fixtures/custom_funcs/index.tmpl") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/file", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}") - - m.Get("/file2", func(ctx *Context) { - ctx.ServeFileContent("fixtures/custom_funcs/index.tmpl", "ok.tmpl") - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/file2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}") - - m.Get("/file3", func(ctx *Context) { - ctx.ServeFileContent("404.tmpl") - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/file3", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "open 404.tmpl: no such file or directory\n") - So(resp.Code, ShouldEqual, 500) - }) - - Convey("Serve content", func() { - m.Get("/content", func(ctx *Context) { - ctx.ServeContent("content1", bytes.NewReader([]byte("Hello world!"))) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/content", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Hello world!") - - m.Get("/content2", func(ctx *Context) { - ctx.ServeContent("content1", bytes.NewReader([]byte("Hello world!")), time.Now()) - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/content2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Hello world!") - }) - }) -} - -func Test_Context_Render(t *testing.T) { - Convey("Invalid render", t, func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - - m := New() - m.Get("/", func(ctx *Context) { - ctx.HTML(200, "hey") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) -} - -func Test_Context_Redirect(t *testing.T) { - Convey("Context with default redirect", t, func() { - url, err := url.Parse("http://localhost/path/one") - So(err, ShouldBeNil) - resp := httptest.NewRecorder() - req := http.Request{ - Method: "GET", - URL: url, - } - ctx := &Context{ - Req: Request{&req}, - Resp: NewResponseWriter(resp), - Data: make(map[string]interface{}), - } - ctx.Redirect("two") - - So(resp.Code, ShouldEqual, http.StatusFound) - So(resp.HeaderMap["Location"][0], ShouldEqual, "/path/two") - }) - - Convey("Context with custom redirect", t, func() { - url, err := url.Parse("http://localhost/path/one") - So(err, ShouldBeNil) - resp := httptest.NewRecorder() - req := http.Request{ - Method: "GET", - URL: url, - } - ctx := &Context{ - Req: Request{&req}, - Resp: NewResponseWriter(resp), - Data: make(map[string]interface{}), - } - ctx.Redirect("two", 307) - - So(resp.Code, ShouldEqual, http.StatusTemporaryRedirect) - So(resp.HeaderMap["Location"][0], ShouldEqual, "/path/two") - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip.go deleted file mode 100644 index 2e935f35076..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bufio" - "compress/gzip" - "fmt" - "net" - "net/http" - "strings" -) - -const ( - HeaderAcceptEncoding = "Accept-Encoding" - HeaderContentEncoding = "Content-Encoding" - HeaderContentLength = "Content-Length" - HeaderContentType = "Content-Type" - HeaderVary = "Vary" -) - -// Gziper returns a Handler that adds gzip compression to all requests. -// Make sure to include the Gzip middleware above other middleware -// that alter the response body (like the render middleware). -func Gziper() Handler { - return func(ctx *Context) { - if !strings.Contains(ctx.Req.Header.Get(HeaderAcceptEncoding), "gzip") { - return - } - - headers := ctx.Resp.Header() - headers.Set(HeaderContentEncoding, "gzip") - headers.Set(HeaderVary, HeaderAcceptEncoding) - - gz := gzip.NewWriter(ctx.Resp) - defer gz.Close() - - gzw := gzipResponseWriter{gz, ctx.Resp} - ctx.Resp = gzw - ctx.MapTo(gzw, (*http.ResponseWriter)(nil)) - - ctx.Next() - - // delete content length after we know we have been written to - gzw.Header().Del("Content-Length") - } -} - -type gzipResponseWriter struct { - w *gzip.Writer - ResponseWriter -} - -func (grw gzipResponseWriter) Write(p []byte) (int, error) { - if len(grw.Header().Get(HeaderContentType)) == 0 { - grw.Header().Set(HeaderContentType, http.DetectContentType(p)) - } - - return grw.w.Write(p) -} - -func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hijacker, ok := grw.ResponseWriter.(http.Hijacker) - if !ok { - return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") - } - return hijacker.Hijack() -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip_test.go deleted file mode 100644 index 565eed66453..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/gzip_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Gzip(t *testing.T) { - Convey("Gzip response content", t, func() { - before := false - - m := New() - m.Use(Gziper()) - m.Use(func(r http.ResponseWriter) { - r.(ResponseWriter).Before(func(rw ResponseWriter) { - before = true - }) - }) - m.Get("/", func() string { return "hello wolrd!" }) - - // Not yet gzip. - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - _, ok := resp.HeaderMap[HeaderContentEncoding] - So(ok, ShouldBeFalse) - - ce := resp.Header().Get(HeaderContentEncoding) - So(strings.EqualFold(ce, "gzip"), ShouldBeFalse) - - // Gzip now. - resp = httptest.NewRecorder() - req.Header.Set(HeaderAcceptEncoding, "gzip") - m.ServeHTTP(resp, req) - - _, ok = resp.HeaderMap[HeaderContentEncoding] - So(ok, ShouldBeTrue) - - ce = resp.Header().Get(HeaderContentEncoding) - So(strings.EqualFold(ce, "gzip"), ShouldBeTrue) - - So(before, ShouldBeTrue) - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/README.md b/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/README.md deleted file mode 100644 index 1721ab4a892..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/README.md +++ /dev/null @@ -1,4 +0,0 @@ -inject -====== - -Dependency injection for go diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.goconvey b/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.goconvey deleted file mode 100644 index 8485e986e45..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.goconvey +++ /dev/null @@ -1 +0,0 @@ -ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject_test.go deleted file mode 100644 index 748572ac2bf..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 inject_test - -import ( - "fmt" - "reflect" - "testing" - - "github.com/Unknwon/macaron/inject" -) - -type SpecialString interface { -} - -type TestStruct struct { - Dep1 string `inject:"t" json:"-"` - Dep2 SpecialString `inject` - Dep3 string -} - -type Greeter struct { - Name string -} - -func (g *Greeter) String() string { - return "Hello, My name is" + g.Name -} - -/* 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)) - } -} - -func Test_InjectorInvoke(t *testing.T) { - injector := inject.New() - expect(t, injector == nil, false) - - dep := "some dependency" - injector.Map(dep) - dep2 := "another dep" - injector.MapTo(dep2, (*SpecialString)(nil)) - dep3 := make(chan *SpecialString) - dep4 := make(chan *SpecialString) - typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem()) - typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem()) - injector.Set(typRecv, reflect.ValueOf(dep3)) - injector.Set(typSend, reflect.ValueOf(dep4)) - - _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) { - expect(t, d1, dep) - expect(t, d2, dep2) - expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem()) - expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem()) - expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir) - expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir) - }) - - expect(t, err, nil) -} - -func Test_InjectorInvokeReturnValues(t *testing.T) { - injector := inject.New() - expect(t, injector == nil, false) - - dep := "some dependency" - injector.Map(dep) - dep2 := "another dep" - injector.MapTo(dep2, (*SpecialString)(nil)) - - result, err := injector.Invoke(func(d1 string, d2 SpecialString) string { - expect(t, d1, dep) - expect(t, d2, dep2) - return "Hello world" - }) - - expect(t, result[0].String(), "Hello world") - expect(t, err, nil) -} - -func Test_InjectorApply(t *testing.T) { - injector := inject.New() - - injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil)) - - s := TestStruct{} - err := injector.Apply(&s) - expect(t, err, nil) - - expect(t, s.Dep1, "a dep") - expect(t, s.Dep2, "another dep") -} - -func Test_InterfaceOf(t *testing.T) { - iType := inject.InterfaceOf((*SpecialString)(nil)) - expect(t, iType.Kind(), reflect.Interface) - - iType = inject.InterfaceOf((**SpecialString)(nil)) - expect(t, iType.Kind(), reflect.Interface) - - // Expecting nil - defer func() { - rec := recover() - refute(t, rec, nil) - }() - iType = inject.InterfaceOf((*testing.T)(nil)) -} - -func Test_InjectorSet(t *testing.T) { - injector := inject.New() - typ := reflect.TypeOf("string") - typSend := reflect.ChanOf(reflect.SendDir, typ) - typRecv := reflect.ChanOf(reflect.RecvDir, typ) - - // instantiating unidirectional channels is not possible using reflect - // http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064 - chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) - chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) - - injector.Set(typSend, chanSend) - injector.Set(typRecv, chanRecv) - - expect(t, injector.GetVal(typSend).IsValid(), true) - expect(t, injector.GetVal(typRecv).IsValid(), true) - expect(t, injector.GetVal(chanSend.Type()).IsValid(), false) -} - -func Test_InjectorGet(t *testing.T) { - injector := inject.New() - - injector.Map("some dependency") - - expect(t, injector.GetVal(reflect.TypeOf("string")).IsValid(), true) - expect(t, injector.GetVal(reflect.TypeOf(11)).IsValid(), false) -} - -func Test_InjectorSetParent(t *testing.T) { - injector := inject.New() - injector.MapTo("another dep", (*SpecialString)(nil)) - - injector2 := inject.New() - injector2.SetParent(injector) - - expect(t, injector2.GetVal(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true) -} - -func TestInjectImplementors(t *testing.T) { - injector := inject.New() - g := &Greeter{"Jeremy"} - injector.Map(g) - - expect(t, injector.GetVal(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go deleted file mode 100644 index c81f70237f9..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bytes" - "log" - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/com" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Logger(t *testing.T) { - Convey("Global logger", t, func() { - buf := bytes.NewBufferString("") - m := New() - m.Map(log.New(buf, "[Macaron] ", 0)) - m.Use(Logger()) - m.Use(func(res http.ResponseWriter) { - res.WriteHeader(http.StatusNotFound) - }) - m.Get("/", func() {}) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusNotFound) - So(len(buf.String()), ShouldBeGreaterThan, 0) - }) - - if ColorLog { - Convey("Color console output", t, func() { - m := Classic() - m.Get("/:code:int", func(ctx *Context) (int, string) { - return ctx.ParamsInt(":code"), "" - }) - - // Just for testing if logger would capture. - codes := []int{200, 201, 202, 301, 302, 304, 401, 403, 404, 500} - for _, code := range codes { - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/"+com.ToStr(code), nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, code) - } - }) - } -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go deleted file mode 100644 index 35cc8f8a218..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "net/http" - "net/http/httptest" - "os" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Version(t *testing.T) { - Convey("Get version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} - -func Test_New(t *testing.T) { - Convey("Initialize a new instance", t, func() { - So(New(), ShouldNotBeNil) - }) - - Convey("Just test that Run doesn't bomb", t, func() { - go New().Run() - time.Sleep(1 * time.Second) - os.Setenv("PORT", "4001") - go New().Run("0.0.0.0") - go New().Run(4002) - go New().Run("0.0.0.0", 4003) - }) -} - -func Test_Macaron_Before(t *testing.T) { - Convey("Register before handlers", t, func() { - m := New() - m.Before(func(rw http.ResponseWriter, req *http.Request) bool { - return false - }) - m.Before(func(rw http.ResponseWriter, req *http.Request) bool { - return true - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) -} - -func Test_Macaron_ServeHTTP(t *testing.T) { - Convey("Serve HTTP requests", t, func() { - result := "" - m := New() - m.Use(func(c *Context) { - result += "foo" - c.Next() - result += "ban" - }) - m.Use(func(c *Context) { - result += "bar" - c.Next() - result += "baz" - }) - m.Get("/", func() {}) - m.Action(func(res http.ResponseWriter, req *http.Request) { - result += "bat" - res.WriteHeader(http.StatusBadRequest) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(result, ShouldEqual, "foobarbatbazban") - So(resp.Code, ShouldEqual, http.StatusBadRequest) - }) -} - -func Test_Macaron_Handlers(t *testing.T) { - Convey("Add custom handlers", t, func() { - result := "" - batman := func(c *Context) { - result += "batman!" - } - - m := New() - m.Use(func(c *Context) { - result += "foo" - c.Next() - result += "ban" - }) - m.Handlers( - batman, - batman, - batman, - ) - - Convey("Add not callable function", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - m.Use("shit") - }) - - m.Get("/", func() {}) - m.Action(func(res http.ResponseWriter, req *http.Request) { - result += "bat" - res.WriteHeader(http.StatusBadRequest) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(result, ShouldEqual, "batman!batman!batman!bat") - So(resp.Code, ShouldEqual, http.StatusBadRequest) - }) -} - -func Test_Macaron_EarlyWrite(t *testing.T) { - Convey("Write early content to response", t, func() { - result := "" - m := New() - m.Use(func(res http.ResponseWriter) { - result += "foobar" - res.Write([]byte("Hello world")) - }) - m.Use(func() { - result += "bat" - }) - m.Get("/", func() {}) - m.Action(func(res http.ResponseWriter) { - result += "baz" - res.WriteHeader(http.StatusBadRequest) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(result, ShouldEqual, "foobar") - So(resp.Code, ShouldEqual, http.StatusOK) - }) -} - -func Test_Macaron_Written(t *testing.T) { - Convey("Written sign", t, func() { - resp := httptest.NewRecorder() - m := New() - m.Handlers(func(res http.ResponseWriter) { - res.WriteHeader(http.StatusOK) - }) - - ctx := m.createContext(resp, &http.Request{Method: "GET"}) - So(ctx.Written(), ShouldBeFalse) - - ctx.run() - So(ctx.Written(), ShouldBeTrue) - }) -} - -func Test_Macaron_Basic_NoRace(t *testing.T) { - Convey("Make sure no race between requests", t, func() { - m := New() - handlers := []Handler{func() {}, func() {}} - // Ensure append will not realloc to trigger the race condition - m.handlers = handlers[:1] - m.Get("/", func() {}) - req, _ := http.NewRequest("GET", "/", nil) - for i := 0; i < 2; i++ { - go func() { - resp := httptest.NewRecorder() - m.ServeHTTP(resp, req) - }() - } - }) -} - -func Test_SetENV(t *testing.T) { - Convey("Get and save environment variable", t, func() { - tests := []struct { - in string - out string - }{ - {"", "development"}, - {"not_development", "not_development"}, - } - - for _, test := range tests { - setENV(test.in) - So(Env, ShouldEqual, test.out) - } - }) -} - -func Test_Config(t *testing.T) { - Convey("Set and get configuration object", t, func() { - So(Config(), ShouldNotBeNil) - cfg, err := SetConfig([]byte("")) - So(err, ShouldBeNil) - So(cfg, ShouldNotBeNil) - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery_test.go deleted file mode 100644 index dc9b341f361..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bytes" - "log" - "net/http" - "net/http/httptest" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Recovery(t *testing.T) { - Convey("Recovery from panic", t, func() { - buf := bytes.NewBufferString("") - setENV(DEV) - - m := New() - m.Map(log.New(buf, "[Macaron] ", 0)) - m.Use(func(res http.ResponseWriter, req *http.Request) { - res.Header().Set("Content-Type", "unpredictable") - }) - m.Use(Recovery()) - m.Use(func(res http.ResponseWriter, req *http.Request) { - panic("here is a panic!") - }) - m.Get("/", func() {}) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusInternalServerError) - So(resp.HeaderMap.Get("Content-Type"), ShouldEqual, "text/html") - So(buf.String(), ShouldNotBeEmpty) - }) - - Convey("Revocery panic to another response writer", t, func() { - resp := httptest.NewRecorder() - resp2 := httptest.NewRecorder() - setENV(DEV) - - m := New() - m.Use(Recovery()) - m.Use(func(c *Context) { - c.MapTo(resp2, (*http.ResponseWriter)(nil)) - panic("here is a panic!") - }) - m.Get("/", func() {}) - - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp2.Code, ShouldEqual, http.StatusInternalServerError) - So(resp2.HeaderMap.Get("Content-Type"), ShouldEqual, "text/html") - So(resp2.Body.Len(), ShouldBeGreaterThan, 0) - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/render_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/render_test.go deleted file mode 100644 index 16318733ec8..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/render_test.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "encoding/xml" - "html/template" - "net/http" - "net/http/httptest" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -type Greeting struct { - One string `json:"one"` - Two string `json:"two"` -} - -type GreetingXML struct { - XMLName xml.Name `xml:"greeting"` - One string `xml:"one,attr"` - Two string `xml:"two,attr"` -} - -func Test_Render_JSON(t *testing.T) { - Convey("Render JSON", t, func() { - m := Classic() - m.Use(Renderer()) - m.Get("/foobar", func(r Render) { - r.JSON(300, Greeting{"hello", "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, `{"one":"hello","two":"world"}`) - }) - - Convey("Render JSON with prefix", t, func() { - m := Classic() - prefix := ")]}',\n" - m.Use(Renderer(RenderOptions{ - PrefixJSON: []byte(prefix), - })) - m.Get("/foobar", func(r Render) { - r.JSON(300, Greeting{"hello", "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, prefix+`{"one":"hello","two":"world"}`) - }) - - Convey("Render Indented JSON", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - IndentJSON: true, - })) - m.Get("/foobar", func(r Render) { - r.JSON(300, Greeting{"hello", "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, `{ - "one": "hello", - "two": "world" -}`) - }) - - Convey("Render JSON and return string", t, func() { - m := Classic() - m.Use(Renderer()) - m.Get("/foobar", func(r Render) { - result, err := r.JSONString(Greeting{"hello", "world"}) - So(err, ShouldBeNil) - So(result, ShouldEqual, `{"one":"hello","two":"world"}`) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - - Convey("Render with charset JSON", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Charset: "foobar", - })) - m.Get("/foobar", func(r Render) { - r.JSON(300, Greeting{"hello", "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=foobar") - So(resp.Body.String(), ShouldEqual, `{"one":"hello","two":"world"}`) - }) -} - -func Test_Render_XML(t *testing.T) { - Convey("Render XML", t, func() { - m := Classic() - m.Use(Renderer()) - m.Get("/foobar", func(r Render) { - r.XML(300, GreetingXML{One: "hello", Two: "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, ``) - }) - - Convey("Render XML with prefix", t, func() { - m := Classic() - prefix := ")]}',\n" - m.Use(Renderer(RenderOptions{ - PrefixXML: []byte(prefix), - })) - m.Get("/foobar", func(r Render) { - r.XML(300, GreetingXML{One: "hello", Two: "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, prefix+``) - }) - - Convey("Render Indented XML", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - IndentXML: true, - })) - m.Get("/foobar", func(r Render) { - r.XML(300, GreetingXML{One: "hello", Two: "world"}) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusMultipleChoices) - So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, ``) - }) -} - -func Test_Render_HTML(t *testing.T) { - Convey("Render HTML", t, func() { - m := Classic() - m.Use(Renderers(RenderOptions{ - Directory: "fixtures/basic", - }, "fixtures/basic2")) - m.Get("/foobar", func(r Render) { - r.HTML(200, "hello", "jeremy") - r.SetTemplatePath("", "fixtures/basic2") - }) - m.Get("/foobar2", func(r Render) { - if r.HasTemplateSet("basic2") { - r.HTMLSet(200, "basic2", "hello", "jeremy") - } - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

Hello jeremy

") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/foobar2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

What's up, jeremy

") - - Convey("Change render templates path", func() { - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

What's up, jeremy

") - }) - }) - - Convey("Render HTML and return string", t, func() { - m := Classic() - m.Use(Renderers(RenderOptions{ - Directory: "fixtures/basic", - }, "basic2:fixtures/basic2")) - m.Get("/foobar", func(r Render) { - result, err := r.HTMLString("hello", "jeremy") - So(err, ShouldBeNil) - So(result, ShouldEqual, "

Hello jeremy

") - }) - m.Get("/foobar2", func(r Render) { - result, err := r.HTMLSetString("basic2", "hello", "jeremy") - So(err, ShouldBeNil) - So(result, ShouldEqual, "

What's up, jeremy

") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/foobar2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - - Convey("Render with nested HTML", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "admin/index", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

Admin jeremy

") - }) - - Convey("Render bad HTML", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "nope", nil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusInternalServerError) - So(resp.Body.String(), ShouldEqual, "html/template: \"nope\" is undefined\n") - }) - - Convey("Invalid template set", t, func() { - Convey("Empty template set argument", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - m := Classic() - m.Use(Renderers(RenderOptions{ - Directory: "fixtures/basic", - }, "")) - }) - - Convey("Bad template set path", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - m := Classic() - m.Use(Renderers(RenderOptions{ - Directory: "fixtures/basic", - }, "404")) - }) - }) -} - -func Test_Render_XHTML(t *testing.T) { - Convey("Render XHTML", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - HTMLContentType: ContentXHTML, - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "hello", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentXHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

Hello jeremy

") - }) -} - -func Test_Render_Extensions(t *testing.T) { - Convey("Render with extensions", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - Extensions: []string{".tmpl", ".html"}, - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "hypertext", nil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "Hypertext!") - }) -} - -func Test_Render_Funcs(t *testing.T) { - Convey("Render with functions", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/custom_funcs", - Funcs: []template.FuncMap{ - { - "myCustomFunc": func() string { - return "My custom function" - }, - }, - }, - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "index", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Body.String(), ShouldEqual, "My custom function") - }) -} - -func Test_Render_Layout(t *testing.T) { - Convey("Render with layout", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - Layout: "layout", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "content", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Body.String(), ShouldEqual, "head

jeremy

foot") - }) - - Convey("Render with current layout", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - Layout: "current_layout", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "content", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Body.String(), ShouldEqual, "content head

jeremy

content foot") - }) - - Convey("Render with override layout", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - Layout: "layout", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "content", "jeremy", HTMLOptions{ - Layout: "another_layout", - }) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "another head

jeremy

another foot") - }) -} - -func Test_Render_Delimiters(t *testing.T) { - Convey("Render with delimiters", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Delims: Delims{"{[{", "}]}"}, - Directory: "fixtures/basic", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "delims", "jeremy") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8") - So(resp.Body.String(), ShouldEqual, "

Hello jeremy

") - }) -} - -func Test_Render_BinaryData(t *testing.T) { - Convey("Render binary data", t, func() { - m := Classic() - m.Use(Renderer()) - m.Get("/foobar", func(r Render) { - r.RawData(200, []byte("hello there")) - }) - m.Get("/foobar2", func(r Render) { - r.RenderData(200, []byte("hello there")) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, ContentBinary) - So(resp.Body.String(), ShouldEqual, "hello there") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/foobar2", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, CONTENT_PLAIN) - So(resp.Body.String(), ShouldEqual, "hello there") - }) - - Convey("Render binary data with mime type", t, func() { - m := Classic() - m.Use(Renderer()) - m.Get("/foobar", func(r Render) { - r.RW().Header().Set(ContentType, "image/jpeg") - r.RawData(200, []byte("..jpeg data..")) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/foobar", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get(ContentType), ShouldEqual, "image/jpeg") - So(resp.Body.String(), ShouldEqual, "..jpeg data..") - }) -} - -func Test_Render_Status(t *testing.T) { - Convey("Render with status 204", t, func() { - resp := httptest.NewRecorder() - r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()} - r.Status(204) - So(resp.Code, ShouldEqual, http.StatusNoContent) - }) - - Convey("Render with status 404", t, func() { - resp := httptest.NewRecorder() - r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()} - r.Error(404) - So(resp.Code, ShouldEqual, http.StatusNotFound) - }) - - Convey("Render with status 500", t, func() { - resp := httptest.NewRecorder() - r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()} - r.Error(500) - So(resp.Code, ShouldEqual, http.StatusInternalServerError) - }) -} - -func Test_Render_NoRace(t *testing.T) { - Convey("Make sure render has no race", t, func() { - m := Classic() - m.Use(Renderer(RenderOptions{ - Directory: "fixtures/basic", - })) - m.Get("/foobar", func(r Render) { - r.HTML(200, "hello", "world") - }) - - done := make(chan bool) - doreq := func() { - resp := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/foobar", nil) - m.ServeHTTP(resp, req) - done <- true - } - // Run two requests to check there is no race condition - go doreq() - go doreq() - <-done - <-done - }) -} - -func Test_GetExt(t *testing.T) { - Convey("Get extension", t, func() { - So(GetExt("test"), ShouldBeBlank) - So(GetExt("test.tmpl"), ShouldEqual, ".tmpl") - So(GetExt("test.go.tmpl"), ShouldEqual, ".go.tmpl") - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/response_writer_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/response_writer_test.go deleted file mode 100644 index 322396b62d0..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/response_writer_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bufio" - "io" - "net" - "net/http" - "net/http/httptest" - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -type closeNotifyingRecorder struct { - *httptest.ResponseRecorder - closed chan bool -} - -func newCloseNotifyingRecorder() *closeNotifyingRecorder { - return &closeNotifyingRecorder{ - httptest.NewRecorder(), - make(chan bool, 1), - } -} - -func (c *closeNotifyingRecorder) close() { - c.closed <- true -} - -func (c *closeNotifyingRecorder) CloseNotify() <-chan bool { - return c.closed -} - -type hijackableResponse struct { - Hijacked bool -} - -func newHijackableResponse() *hijackableResponse { - return &hijackableResponse{} -} - -func (h *hijackableResponse) Header() http.Header { return nil } -func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0, nil } -func (h *hijackableResponse) WriteHeader(code int) {} -func (h *hijackableResponse) Flush() {} -func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) { - h.Hijacked = true - return nil, nil, nil -} - -func Test_ResponseWriter(t *testing.T) { - Convey("Write string to response writer", t, func() { - resp := httptest.NewRecorder() - rw := NewResponseWriter(resp) - rw.Write([]byte("Hello world")) - - So(resp.Code, ShouldEqual, rw.Status()) - So(resp.Body.String(), ShouldEqual, "Hello world") - So(rw.Status(), ShouldEqual, http.StatusOK) - So(rw.Size(), ShouldEqual, 11) - So(rw.Written(), ShouldBeTrue) - }) - - Convey("Write strings to response writer", t, func() { - resp := httptest.NewRecorder() - rw := NewResponseWriter(resp) - rw.Write([]byte("Hello world")) - rw.Write([]byte("foo bar bat baz")) - - So(resp.Code, ShouldEqual, rw.Status()) - So(resp.Body.String(), ShouldEqual, "Hello worldfoo bar bat baz") - So(rw.Status(), ShouldEqual, http.StatusOK) - So(rw.Size(), ShouldEqual, 26) - So(rw.Written(), ShouldBeTrue) - }) - - Convey("Write header to response writer", t, func() { - resp := httptest.NewRecorder() - rw := NewResponseWriter(resp) - rw.WriteHeader(http.StatusNotFound) - - So(resp.Code, ShouldEqual, rw.Status()) - So(resp.Body.String(), ShouldBeBlank) - So(rw.Status(), ShouldEqual, http.StatusNotFound) - So(rw.Size(), ShouldEqual, 0) - }) - - Convey("Write before response write", t, func() { - result := "" - resp := httptest.NewRecorder() - rw := NewResponseWriter(resp) - rw.Before(func(ResponseWriter) { - result += "foo" - }) - rw.Before(func(ResponseWriter) { - result += "bar" - }) - rw.WriteHeader(http.StatusNotFound) - - So(resp.Code, ShouldEqual, rw.Status()) - So(resp.Body.String(), ShouldBeBlank) - So(rw.Status(), ShouldEqual, http.StatusNotFound) - So(rw.Size(), ShouldEqual, 0) - So(result, ShouldEqual, "barfoo") - }) - - Convey("Response writer with Hijack", t, func() { - hijackable := newHijackableResponse() - rw := NewResponseWriter(hijackable) - hijacker, ok := rw.(http.Hijacker) - So(ok, ShouldBeTrue) - _, _, err := hijacker.Hijack() - So(err, ShouldBeNil) - So(hijackable.Hijacked, ShouldBeTrue) - }) - - Convey("Response writer with bad Hijack", t, func() { - hijackable := new(http.ResponseWriter) - rw := NewResponseWriter(*hijackable) - hijacker, ok := rw.(http.Hijacker) - So(ok, ShouldBeTrue) - _, _, err := hijacker.Hijack() - So(err, ShouldNotBeNil) - }) - - Convey("Response writer with close notify", t, func() { - resp := newCloseNotifyingRecorder() - rw := NewResponseWriter(resp) - closed := false - notifier := rw.(http.CloseNotifier).CloseNotify() - resp.close() - select { - case <-notifier: - closed = true - case <-time.After(time.Second): - } - So(closed, ShouldBeTrue) - }) - - Convey("Response writer with flusher", t, func() { - resp := httptest.NewRecorder() - rw := NewResponseWriter(resp) - _, ok := rw.(http.Flusher) - So(ok, ShouldBeTrue) - }) - - Convey("Response writer with flusher handler", t, func() { - m := Classic() - m.Get("/events", func(w http.ResponseWriter, r *http.Request) { - f, ok := w.(http.Flusher) - So(ok, ShouldBeTrue) - - w.Header().Set("Content-Type", "text/event-stream") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - - for i := 0; i < 2; i++ { - time.Sleep(10 * time.Millisecond) - io.WriteString(w, "data: Hello\n\n") - f.Flush() - } - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/events", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Body.String(), ShouldEqual, "data: Hello\n\ndata: Hello\n\n") - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler_test.go deleted file mode 100644 index 02325b218b0..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "net/http" - "net/http/httptest" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Return_Handler(t *testing.T) { - Convey("Return with status and body", t, func() { - m := Classic() - m.Get("/", func() (int, string) { - return 418, "i'm a teapot" - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusTeapot) - So(resp.Body.String(), ShouldEqual, "i'm a teapot") - }) - - Convey("Return with pointer", t, func() { - m := Classic() - m.Get("/", func() *string { - str := "hello world" - return &str - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Body.String(), ShouldEqual, "hello world") - }) - - Convey("Return with byte slice", t, func() { - m := Classic() - m.Get("/", func() []byte { - return []byte("hello world") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Body.String(), ShouldEqual, "hello world") - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go deleted file mode 100644 index f4044965bb3..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "net/http" - "net/http/httptest" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Router_Handle(t *testing.T) { - Convey("Register all HTTP methods routes", t, func() { - m := Classic() - m.Get("/get", func() string { - return "GET" - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "GET") - - m.Patch("/patch", func() string { - return "PATCH" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("PATCH", "/patch", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "PATCH") - - m.Post("/post", func() string { - return "POST" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("POST", "/post", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "POST") - - m.Put("/put", func() string { - return "PUT" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("PUT", "/put", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "PUT") - - m.Delete("/delete", func() string { - return "DELETE" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("DELETE", "/delete", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "DELETE") - - m.Options("/options", func() string { - return "OPTIONS" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("OPTIONS", "/options", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "OPTIONS") - - m.Head("/head", func() string { - return "HEAD" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("HEAD", "/head", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "HEAD") - - m.Any("/any", func() string { - return "ANY" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/any", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "ANY") - - m.Route("/route", "GET,POST", func() string { - return "ROUTE" - }) - resp = httptest.NewRecorder() - req, err = http.NewRequest("POST", "/route", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "ROUTE") - }) - - Convey("Register all HTTP methods routes with combo", t, func() { - m := Classic() - m.SetURLPrefix("/prefix") - m.Use(Renderer()) - m.Combo("/", func(ctx *Context) { - ctx.Data["prefix"] = "Prefix_" - }). - Get(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "GET" }). - Patch(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PATCH" }). - Post(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "POST" }). - Put(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PUT" }). - Delete(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "DELETE" }). - Options(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "OPTIONS" }). - Head(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "HEAD" }) - - for name := range _HTTP_METHODS { - resp := httptest.NewRecorder() - req, err := http.NewRequest(name, "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Prefix_"+name) - } - - defer func() { - So(recover(), ShouldNotBeNil) - }() - m.Combo("/").Get(func() {}).Get(nil) - }) - - Convey("Register duplicated routes", t, func() { - r := NewRouter() - r.Get("/") - r.Get("/") - }) - - Convey("Register invalid HTTP method", t, func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - r := NewRouter() - r.Handle("404", "/", nil) - }) -} - -func Test_Router_Group(t *testing.T) { - Convey("Register route group", t, func() { - m := Classic() - m.Group("/api", func() { - m.Group("/v1", func() { - m.Get("/list", func() string { - return "Well done!" - }) - }) - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/api/v1/list", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Well done!") - }) -} - -func Test_Router_NotFound(t *testing.T) { - Convey("Custom not found handler", t, func() { - m := Classic() - m.Get("/", func() {}) - m.NotFound(func() string { - return "Custom not found" - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/404", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "Custom not found") - }) -} - -func Test_Router_splat(t *testing.T) { - Convey("Register router with glob", t, func() { - m := Classic() - m.Get("/*", func(ctx *Context) string { - return ctx.Params("*") - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/hahaha", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, "hahaha") - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/static_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/static_test.go deleted file mode 100644 index ecef35236c6..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/static_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - "bytes" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "path" - "strings" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -var currentRoot, _ = os.Getwd() - -func Test_Static(t *testing.T) { - Convey("Serve static files", t, func() { - m := New() - m.Use(Static("./")) - - resp := httptest.NewRecorder() - resp.Body = new(bytes.Buffer) - req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get("Expires"), ShouldBeBlank) - So(resp.Body.Len(), ShouldBeGreaterThan, 0) - - Convey("Change static path", func() { - m.Get("/", func(ctx *Context) { - ctx.ChangeStaticPath("./", "inject") - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - resp = httptest.NewRecorder() - resp.Body = new(bytes.Buffer) - req, err = http.NewRequest("GET", "http://localhost:4000/inject.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get("Expires"), ShouldBeBlank) - So(resp.Body.Len(), ShouldBeGreaterThan, 0) - }) - }) - - Convey("Serve static files with local path", t, func() { - Root = os.TempDir() - f, err := ioutil.TempFile(Root, "static_content") - So(err, ShouldBeNil) - f.WriteString("Expected Content") - f.Close() - - m := New() - m.Use(Static(".")) - - resp := httptest.NewRecorder() - resp.Body = new(bytes.Buffer) - req, err := http.NewRequest("GET", "http://localhost:4000/"+path.Base(strings.Replace(f.Name(), "\\", "/", -1)), nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Header().Get("Expires"), ShouldBeBlank) - So(resp.Body.String(), ShouldEqual, "Expected Content") - }) - - Convey("Serve static files with head", t, func() { - m := New() - m.Use(Static(currentRoot)) - - resp := httptest.NewRecorder() - resp.Body = new(bytes.Buffer) - req, err := http.NewRequest("HEAD", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusOK) - So(resp.Body.Len(), ShouldEqual, 0) - }) - - Convey("Serve static files as post", t, func() { - m := New() - m.Use(Static(currentRoot)) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("POST", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldEqual, http.StatusNotFound) - }) - - Convey("Serve static files with bad directory", t, func() { - m := Classic() - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - So(resp.Code, ShouldNotEqual, http.StatusOK) - }) -} - -func Test_Static_Options(t *testing.T) { - Convey("Serve static files with options logging", t, func() { - var buf bytes.Buffer - m := NewWithLogger(&buf) - opt := StaticOptions{} - m.Use(Static(currentRoot, opt)) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n") - - // Not disable logging. - m.Handlers() - buf.Reset() - opt.SkipLogging = true - m.Use(Static(currentRoot, opt)) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.Len(), ShouldEqual, 0) - }) - - Convey("Serve static files with options serve index", t, func() { - var buf bytes.Buffer - m := NewWithLogger(&buf) - opt := StaticOptions{IndexFile: "macaron.go"} - m.Use(Static(currentRoot, opt)) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n") - }) - - Convey("Serve static files with options prefix", t, func() { - var buf bytes.Buffer - m := NewWithLogger(&buf) - opt := StaticOptions{Prefix: "public"} - m.Use(Static(currentRoot, opt)) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/public/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n") - }) - - Convey("Serve static files with options expires", t, func() { - var buf bytes.Buffer - m := NewWithLogger(&buf) - opt := StaticOptions{Expires: func() string { return "46" }} - m.Use(Static(currentRoot, opt)) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Header().Get("Expires"), ShouldEqual, "46") - }) -} - -func Test_Static_Redirect(t *testing.T) { - Convey("Serve static files with redirect", t, func() { - m := New() - m.Use(Static(currentRoot, StaticOptions{Prefix: "/public"})) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/public", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusFound) - So(resp.Header().Get("Location"), ShouldEqual, "/public/") - }) -} - -func Test_Statics(t *testing.T) { - Convey("Serve multiple static routers", t, func() { - Convey("Register empty directory", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - - m := New() - m.Use(Statics(StaticOptions{})) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - - Convey("Serve normally", func() { - var buf bytes.Buffer - m := NewWithLogger(&buf) - m.Use(Statics(StaticOptions{}, currentRoot, currentRoot+"/inject")) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "http://localhost:4000/inject/inject.go", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - So(resp.Code, ShouldEqual, http.StatusOK) - So(buf.String(), ShouldEndWith, "[Macaron] [Static] Serving /inject/inject.go\n") - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go deleted file mode 100644 index 7bde5add69d..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright 2013 Beego Authors -// Copyright 2014 Unknwon -// -// 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 macaron - -// NOTE: last sync 0c93364 on Dec 19, 2014. - -import ( - "path" - "regexp" - "strings" - - "github.com/Unknwon/com" -) - -type leafInfo struct { - // Names of wildcards that lead to this leaf. - // eg, ["id" "name"] for the wildcard ":id" and ":name". - wildcards []string - // Not nil if the leaf is regexp. - regexps *regexp.Regexp - handle Handle -} - -func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params Params) { - if leaf.regexps == nil { - if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { - if com.IsSliceContainsStr(leaf.wildcards, ":") { - params = make(map[string]string) - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - params[v] = "" - j += 1 - } - return true, params - } - return false, nil - } else if len(wildcardValues) == 0 { - return true, nil // Static path. - } - - // Match * - if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - params = make(map[string]string) - params[":splat"] = path.Join(wildcardValues...) - return true, params - } - - // Match *.* - if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." { - params = make(map[string]string) - lastone := wildcardValues[len(wildcardValues)-1] - strs := strings.SplitN(lastone, ".", 2) - if len(strs) == 2 { - params[":ext"] = strs[1] - } else { - params[":ext"] = "" - } - params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] - return true, params - } - - // Match :id - params = make(map[string]string) - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - if v == "." { - lastone := wildcardValues[len(wildcardValues)-1] - strs := strings.SplitN(lastone, ".", 2) - if len(strs) == 2 { - params[":ext"] = strs[1] - } else { - params[":ext"] = "" - } - if len(wildcardValues[j:]) == 1 { - params[":path"] = strs[0] - } else { - params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0] - } - return true, params - } - if len(wildcardValues) <= j { - return false, nil - } - params[v] = wildcardValues[j] - j++ - } - if len(params) != len(wildcardValues) { - return false, nil - } - return true, params - } - - if !leaf.regexps.MatchString(path.Join(wildcardValues...)) { - return false, nil - } - params = make(map[string]string) - matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) - for i, match := range matches[1:] { - params[leaf.wildcards[i]] = match - } - return true, params -} - -// Tree represents a router tree for Macaron instance. -type Tree struct { - fixroutes map[string]*Tree - wildcard *Tree - leaves []*leafInfo -} - -// NewTree initializes and returns a router tree. -func NewTree() *Tree { - return &Tree{ - fixroutes: make(map[string]*Tree), - } -} - -// splitPath splites patthen into parts. -// -// Examples: -// "/" -> [] -// "/admin" -> ["admin"] -// "/admin/" -> ["admin"] -// "/admin/users" -> ["admin", "users"] -func splitPath(pattern string) []string { - if len(pattern) == 0 { - return []string{} - } - - elements := strings.Split(pattern, "/") - if elements[0] == "" { - elements = elements[1:] - } - if elements[len(elements)-1] == "" { - elements = elements[:len(elements)-1] - } - return elements -} - -// AddRouter adds a new route to router tree. -func (t *Tree) AddRouter(pattern string, handle Handle) { - t.addSegments(splitPath(pattern), handle, nil, "") -} - -// splitSegment splits segment into parts. -// -// Examples: -// "admin" -> false, nil, "" -// ":id" -> true, [:id], "" -// "?:id" -> true, [: :id], "" : meaning can empty -// ":id:int" -> true, [:id], ([0-9]+) -// ":name:string" -> true, [:name], ([\w]+) -// ":id([0-9]+)" -> true, [:id], ([0-9]+) -// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+) -// "cms_:id_:page.html" -> true, [:id :page], cms_(.+)_(.+).html -// "*" -> true, [:splat], "" -// "*.*" -> true,[. :path :ext], "" . meaning separator -func splitSegment(key string) (bool, []string, string) { - if strings.HasPrefix(key, "*") { - if key == "*.*" { - return true, []string{".", ":path", ":ext"}, "" - } else { - return true, []string{":splat"}, "" - } - } - if strings.ContainsAny(key, ":") { - var paramsNum int - var out []rune - var start bool - var startexp bool - var param []rune - var expt []rune - var skipnum int - params := []string{} - reg := regexp.MustCompile(`[a-zA-Z0-9]+`) - for i, v := range key { - if skipnum > 0 { - skipnum -= 1 - continue - } - if start { - //:id:int and :name:string - if v == ':' { - if len(key) >= i+4 { - if key[i+1:i+4] == "int" { - out = append(out, []rune("([0-9]+)")...) - params = append(params, ":"+string(param)) - start = false - startexp = false - skipnum = 3 - param = make([]rune, 0) - paramsNum += 1 - continue - } - } - if len(key) >= i+7 { - if key[i+1:i+7] == "string" { - out = append(out, []rune(`([\w]+)`)...) - params = append(params, ":"+string(param)) - paramsNum += 1 - start = false - startexp = false - skipnum = 6 - param = make([]rune, 0) - continue - } - } - } - // params only support a-zA-Z0-9 - if reg.MatchString(string(v)) { - param = append(param, v) - continue - } - if v != '(' { - out = append(out, []rune(`(.+)`)...) - params = append(params, ":"+string(param)) - param = make([]rune, 0) - paramsNum += 1 - start = false - startexp = false - } - } - if startexp { - if v != ')' { - expt = append(expt, v) - continue - } - } - if v == ':' { - param = make([]rune, 0) - start = true - } else if v == '(' { - startexp = true - start = false - params = append(params, ":"+string(param)) - paramsNum += 1 - expt = make([]rune, 0) - expt = append(expt, '(') - } else if v == ')' { - startexp = false - expt = append(expt, ')') - out = append(out, expt...) - param = make([]rune, 0) - } else if v == '?' { - params = append(params, ":") - } else { - out = append(out, v) - } - } - if len(param) > 0 { - if paramsNum > 0 { - out = append(out, []rune(`(.+)`)...) - } - params = append(params, ":"+string(param)) - } - return true, params, string(out) - } else { - return false, nil, "" - } -} - -// addSegments add segments to the router tree. -func (t *Tree) addSegments(segments []string, handle Handle, wildcards []string, reg string) { - // Fixed root route. - if len(segments) == 0 { - if reg != "" { - filterCards := make([]string, 0, len(wildcards)) - for _, v := range wildcards { - if v == ":" || v == "." { - continue - } - filterCards = append(filterCards, v) - } - t.leaves = append(t.leaves, &leafInfo{ - handle: handle, - wildcards: filterCards, - regexps: regexp.MustCompile("^" + reg + "$"), - }) - } else { - t.leaves = append(t.leaves, &leafInfo{ - handle: handle, - wildcards: wildcards, - }) - } - return - } - - seg := segments[0] - iswild, params, regexpStr := splitSegment(seg) - //for the router /login/*/access match /login/2009/11/access - if !iswild && com.IsSliceContainsStr(wildcards, ":splat") { - iswild = true - regexpStr = seg - } - if seg == "*" && len(wildcards) > 0 && reg == "" { - iswild = true - regexpStr = "(.+)" - } - if iswild { - if t.wildcard == nil { - t.wildcard = NewTree() - } - if regexpStr != "" { - if reg == "" { - rr := "" - for _, w := range wildcards { - if w == "." || w == ":" { - continue - } - if w == ":splat" { - rr = rr + "(.+)/" - } else { - rr = rr + "([^/]+)/" - } - } - regexpStr = rr + regexpStr - } else { - regexpStr = "/" + regexpStr - } - } else if reg != "" { - if seg == "*.*" { - regexpStr = "/([^.]+).(.+)" - } else { - for _, w := range params { - if w == "." || w == ":" { - continue - } - regexpStr = "/([^/]+)" + regexpStr - } - } - } - t.wildcard.addSegments(segments[1:], handle, append(wildcards, params...), reg+regexpStr) - } else { - subTree, ok := t.fixroutes[seg] - if !ok { - subTree = NewTree() - t.fixroutes[seg] = subTree - } - subTree.addSegments(segments[1:], handle, wildcards, reg) - } -} - -func (t *Tree) match(segments []string, wildcardValues []string) (handle Handle, params Params) { - // Handle leaf nodes. - if len(segments) == 0 { - for _, l := range t.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.handle, pa - } - } - if t.wildcard != nil { - for _, l := range t.wildcard.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.handle, pa - } - } - - } - return nil, nil - } - - seg, segs := segments[0], segments[1:] - - subTree, ok := t.fixroutes[seg] - if ok { - handle, params = subTree.match(segs, wildcardValues) - } else if len(segs) == 0 { //.json .xml - if subindex := strings.LastIndex(seg, "."); subindex != -1 { - subTree, ok = t.fixroutes[seg[:subindex]] - if ok { - handle, params = subTree.match(segs, wildcardValues) - if handle != nil { - if params == nil { - params = make(map[string]string) - } - params[":ext"] = seg[subindex+1:] - return handle, params - } - } - } - } - if handle == nil && t.wildcard != nil { - handle, params = t.wildcard.match(segs, append(wildcardValues, seg)) - } - if handle == nil { - for _, l := range t.leaves { - if ok, pa := l.match(append(wildcardValues, segments...)); ok { - return l.handle, pa - } - } - } - return handle, params -} - -// Match returns Handle and params if any route is matched. -func (t *Tree) Match(pattern string) (Handle, Params) { - if len(pattern) == 0 || pattern[0] != '/' { - return nil, nil - } - - return t.match(splitPath(pattern), nil) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go deleted file mode 100644 index c8144160e8c..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 macaron - -import ( - // "net/http" - "strings" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_splitSegment(t *testing.T) { - type result struct { - Ok bool - Parts []string - Regex string - } - cases := map[string]result{ - "admin": result{false, nil, ""}, - ":id": result{true, []string{":id"}, ""}, - "?:id": result{true, []string{":", ":id"}, ""}, - ":id:int": result{true, []string{":id"}, "([0-9]+)"}, - ":name:string": result{true, []string{":name"}, `([\w]+)`}, - ":id([0-9]+)": result{true, []string{":id"}, "([0-9]+)"}, - ":id([0-9]+)_:name": result{true, []string{":id", ":name"}, "([0-9]+)_(.+)"}, - "cms_:id_:page.html": result{true, []string{":id", ":page"}, "cms_(.+)_(.+).html"}, - "*": result{true, []string{":splat"}, ""}, - "*.*": result{true, []string{".", ":path", ":ext"}, ""}, - } - Convey("Splits segment into parts", t, func() { - for key, result := range cases { - ok, parts, regex := splitSegment(key) - So(ok, ShouldEqual, result.Ok) - if result.Parts == nil { - So(parts, ShouldBeNil) - } else { - So(parts, ShouldNotBeNil) - So(strings.Join(parts, " "), ShouldEqual, strings.Join(result.Parts, " ")) - } - So(regex, ShouldEqual, result.Regex) - } - }) -} - -func Test_Tree_Match(t *testing.T) { - type result struct { - pattern string - reqUrl string - params map[string]string - } - - cases := []result{ - {"/:id", "/123", map[string]string{":id": "123"}}, - {"/hello/?:id", "/hello", map[string]string{":id": ""}}, - {"/", "/", nil}, - {"", "", nil}, - {"/customer/login", "/customer/login", nil}, - {"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}}, - {"/*", "/customer/123", map[string]string{":splat": "customer/123"}}, - {"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}, - {"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}, - {"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}, - {"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}}, - {"/thumbnail/:size/uploads/*", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", - map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}}, - {"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}}, - {"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}}, - {"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}}, - {"/dl/:width:int/:height:int/*.*", "/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg", - map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}}, - {"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}}, - {"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}}, - {"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}}, - {"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}}, - {"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}}, - {"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}}, - {"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}}, - {"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}}, - {"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}}, - {"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}}, - {"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}}, - {"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}}, - } - - Convey("Match routers in tree", t, func() { - for _, c := range cases { - t := NewTree() - t.AddRouter(c.pattern, nil) - _, params := t.Match(c.reqUrl) - if params != nil { - for k, v := range c.params { - vv, ok := params[k] - So(ok, ShouldBeTrue) - So(vv, ShouldEqual, v) - } - } - } - }) -} diff --git a/Godeps/_workspace/src/github.com/go-macaron/binding/.travis.yml b/Godeps/_workspace/src/github.com/go-macaron/binding/.travis.yml new file mode 100644 index 00000000000..2774fb35d50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/binding/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/LICENSE b/Godeps/_workspace/src/github.com/go-macaron/binding/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/binding/LICENSE rename to Godeps/_workspace/src/github.com/go-macaron/binding/LICENSE diff --git a/Godeps/_workspace/src/github.com/go-macaron/binding/README.md b/Godeps/_workspace/src/github.com/go-macaron/binding/README.md new file mode 100644 index 00000000000..a6748b57a1d --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/binding/README.md @@ -0,0 +1,20 @@ +# binding [![Build Status](https://travis-ci.org/go-macaron/binding.svg?branch=master)](https://travis-ci.org/go-macaron/binding) [![](http://gocover.io/_badge/github.com/go-macaron/binding)](http://gocover.io/github.com/go-macaron/binding) + +Middleware binding provides request data binding and validation for [Macaron](https://github.com/go-macaron/macaron). + +### Installation + + go get github.com/go-macaron/binding + +## Getting Help + +- [API Reference](https://gowalker.org/github.com/go-macaron/binding) +- [Documentation](http://go-macaron.com/docs/middlewares/binding) + +## Credits + +This package is a modified version of [martini-contrib/binding](https://github.com/martini-contrib/binding). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/binding.go b/Godeps/_workspace/src/github.com/go-macaron/binding/binding.go similarity index 70% rename from Godeps/_workspace/src/github.com/macaron-contrib/binding/binding.go rename to Godeps/_workspace/src/github.com/go-macaron/binding/binding.go index 651d6660156..4b958ec964d 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/binding.go +++ b/Godeps/_workspace/src/github.com/go-macaron/binding/binding.go @@ -1,5 +1,5 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon +// Copyright 2014 Martini Authors +// Copyright 2014 The Macaron Authors // // 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 @@ -29,12 +29,10 @@ import ( "unicode/utf8" "github.com/Unknwon/com" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" ) -// NOTE: last sync 1928ed2 on Aug 26, 2014. - -const _VERSION = "0.0.4" +const _VERSION = "0.2.0" func Version() string { return _VERSION @@ -58,6 +56,7 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) { errors.Add([]string{}, ERR_CONTENT_TYPE, "Unsupported Content-Type") } ctx.Map(errors) + ctx.Map(obj) // Map a fake struct so handler won't panic. } } else { ctx.Invoke(Form(obj, ifacePtr...)) @@ -175,6 +174,14 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Hand if parseErr != nil { errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) } + + if ctx.Req.Form == nil { + ctx.Req.ParseForm() + } + for k, v := range form.Value { + ctx.Req.Form[k] = append(ctx.Req.Form[k], v...) + } + ctx.Req.MultipartForm = form } } @@ -310,122 +317,162 @@ func validateStruct(errors Errors, obj interface{}) Errors { field.Type.Elem().Kind() == reflect.Struct) { errors = validateStruct(errors, fieldValue) } + errors = validateField(errors, zero, field, fieldVal, fieldValue) + } + return errors +} - VALIDATE_RULES: - for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { - if len(rule) == 0 { - continue +func validateField(errors Errors, zero interface{}, field reflect.StructField, fieldVal reflect.Value, fieldValue interface{}) Errors { + if fieldVal.Kind() == reflect.Slice { + for i := 0; i < fieldVal.Len(); i++ { + sliceVal := fieldVal.Index(i) + if sliceVal.Kind() == reflect.Ptr { + sliceVal = sliceVal.Elem() } - switch { - case rule == "Required": - if reflect.DeepEqual(zero, fieldValue) { - errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + sliceValue := sliceVal.Interface() + zero := reflect.Zero(sliceVal.Type()).Interface() + if sliceVal.Kind() == reflect.Struct || + (sliceVal.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, sliceValue) && + sliceVal.Elem().Kind() == reflect.Struct) { + errors = validateStruct(errors, sliceValue) + } + /* Apply validation rules to each item in a slice. ISSUE #3 + else { + errors = validateField(errors, zero, field, sliceVal, sliceValue) + }*/ + } + } + +VALIDATE_RULES: + for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { + if len(rule) == 0 { + continue + } + + switch { + case rule == "OmitEmpty": + if reflect.DeepEqual(zero, fieldValue) { + break VALIDATE_RULES + } + case rule == "Required": + if reflect.DeepEqual(zero, fieldValue) { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + case rule == "AlphaDash": + if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash") + break VALIDATE_RULES + } + case rule == "AlphaDashDot": + if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Size("): + size, _ := strconv.Atoi(rule[5 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MinSize("): + min, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MaxSize("): + max, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Range("): + nums := strings.Split(rule[6:len(rule)-1], ",") + if len(nums) != 2 { + break VALIDATE_RULES + } + val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt() + if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() { + errors.Add([]string{field.Name}, ERR_RANGE, "Range") + break VALIDATE_RULES + } + case rule == "Email": + if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_EMAIL, "Email") + break VALIDATE_RULES + } + case rule == "Url": + str := fmt.Sprintf("%v", fieldValue) + if len(str) == 0 { + continue + } else if !urlPattern.MatchString(str) { + errors.Add([]string{field.Name}, ERR_URL, "Url") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "In("): + if !in(fieldValue, rule[3:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_IN, "In") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "NotIn("): + if in(fieldValue, rule[6:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Include("): + if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_INCLUDE, "Include") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Exclude("): + if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Default("): + if reflect.DeepEqual(zero, fieldValue) { + if fieldVal.CanAddr() { + setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) + } else { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default") break VALIDATE_RULES } - case rule == "AlphaDash": - if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { - errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash") + } + default: + // Apply custom validation rules. + for i := range ruleMapper { + if ruleMapper[i].IsMatch(rule) && !ruleMapper[i].IsValid(errors, field.Name, fieldValue) { break VALIDATE_RULES } - case rule == "AlphaDashDot": - if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { - errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "MinSize("): - min, _ := strconv.Atoi(rule[8 : len(rule)-1]) - if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min { - errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") - break VALIDATE_RULES - } - v := reflect.ValueOf(fieldValue) - if v.Kind() == reflect.Slice && v.Len() < min { - errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "MaxSize("): - max, _ := strconv.Atoi(rule[8 : len(rule)-1]) - if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max { - errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") - break VALIDATE_RULES - } - v := reflect.ValueOf(fieldValue) - if v.Kind() == reflect.Slice && v.Len() > max { - errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "Range("): - nums := strings.Split(rule[6:len(rule)-1], ",") - if len(nums) != 2 { - break VALIDATE_RULES - } - val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt() - if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() { - errors.Add([]string{field.Name}, ERR_RANGE, "Range") - break VALIDATE_RULES - } - case rule == "Email": - if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { - errors.Add([]string{field.Name}, ERR_EMAIL, "Email") - break VALIDATE_RULES - } - case rule == "Url": - str := fmt.Sprintf("%v", fieldValue) - if len(str) == 0 { - continue - } else if !urlPattern.MatchString(str) { - errors.Add([]string{field.Name}, ERR_URL, "Url") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "In("): - if !in(fieldValue, rule[3:len(rule)-1]) { - errors.Add([]string{field.Name}, ERR_IN, "In") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "NotIn("): - if in(fieldValue, rule[6:len(rule)-1]) { - errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "Include("): - if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { - errors.Add([]string{field.Name}, ERR_INCLUDE, "Include") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "Exclude("): - if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { - errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude") - break VALIDATE_RULES - } - case strings.HasPrefix(rule, "Default("): - if reflect.DeepEqual(zero, fieldValue) { - if fieldVal.CanAddr() { - setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) - } else { - errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default") - break VALIDATE_RULES - } - } - default: - // Apply custom validation rules. - for i := range ruleMapper { - if ruleMapper[i].IsMatch(rule) && !ruleMapper[i].IsValid(errors, field.Name, fieldValue) { - break VALIDATE_RULES - } - } } } } return errors } -// NameMapper represents a form/json tag name mapper. +// NameMapper represents a form tag name mapper. type NameMapper func(string) string var ( nameMapper = func(field string) string { - newstr := make([]rune, 0, 10) + newstr := make([]rune, 0, len(field)) for i, chr := range field { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if i > 0 { @@ -468,42 +515,40 @@ func mapForm(formStruct reflect.Value, form map[string][]string, } inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form")) - if len(inputFieldName) > 0 { - if !structField.CanSet() { - continue - } + if len(inputFieldName) == 0 || !structField.CanSet() { + continue + } - inputValue, exists := form[inputFieldName] - if exists { - numElems := len(inputValue) - if structField.Kind() == reflect.Slice && numElems > 0 { - sliceOf := structField.Type().Elem().Kind() - slice := reflect.MakeSlice(structField.Type(), numElems, numElems) - for i := 0; i < numElems; i++ { - setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) - } - formStruct.Field(i).Set(slice) - } else { - setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) - } - continue - } - - inputFile, exists := formfile[inputFieldName] - if !exists { - continue - } - fhType := reflect.TypeOf((*multipart.FileHeader)(nil)) - numElems := len(inputFile) - if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType { + inputValue, exists := form[inputFieldName] + if exists { + numElems := len(inputValue) + if structField.Kind() == reflect.Slice && numElems > 0 { + sliceOf := structField.Type().Elem().Kind() slice := reflect.MakeSlice(structField.Type(), numElems, numElems) for i := 0; i < numElems; i++ { - slice.Index(i).Set(reflect.ValueOf(inputFile[i])) + setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) } - structField.Set(slice) - } else if structField.Type() == fhType { - structField.Set(reflect.ValueOf(inputFile[0])) + formStruct.Field(i).Set(slice) + } else { + setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) } + continue + } + + inputFile, exists := formfile[inputFieldName] + if !exists { + continue + } + fhType := reflect.TypeOf((*multipart.FileHeader)(nil)) + numElems := len(inputFile) + if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType { + slice := reflect.MakeSlice(structField.Type(), numElems, numElems) + for i := 0; i < numElems; i++ { + slice.Index(i).Set(reflect.ValueOf(inputFile[i])) + } + structField.Set(slice) + } else if structField.Type() == fhType { + structField.Set(reflect.ValueOf(inputFile[0])) } } } diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errors.go b/Godeps/_workspace/src/github.com/go-macaron/binding/errors.go similarity index 97% rename from Godeps/_workspace/src/github.com/macaron-contrib/binding/errors.go rename to Godeps/_workspace/src/github.com/go-macaron/binding/errors.go index 9a9c006b866..8cbe44a9d17 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errors.go +++ b/Godeps/_workspace/src/github.com/go-macaron/binding/errors.go @@ -1,5 +1,5 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon +// Copyright 2014 Martini Authors +// Copyright 2014 The Macaron Authors // // 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 @@ -27,6 +27,7 @@ const ( ERR_REQUIRED = "RequiredError" ERR_ALPHA_DASH = "AlphaDashError" ERR_ALPHA_DASH_DOT = "AlphaDashDotError" + ERR_SIZE = "SizeError" ERR_MIN_SIZE = "MinSizeError" ERR_MAX_SIZE = "MaxSizeError" ERR_RANGE = "RangeError" diff --git a/Godeps/_workspace/src/github.com/go-macaron/gzip/.travis.yml b/Godeps/_workspace/src/github.com/go-macaron/gzip/.travis.yml new file mode 100644 index 00000000000..2774fb35d50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/gzip/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/LICENSE b/Godeps/_workspace/src/github.com/go-macaron/gzip/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/LICENSE rename to Godeps/_workspace/src/github.com/go-macaron/gzip/LICENSE diff --git a/Godeps/_workspace/src/github.com/go-macaron/gzip/README.md b/Godeps/_workspace/src/github.com/go-macaron/gzip/README.md new file mode 100644 index 00000000000..0c438a72027 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/gzip/README.md @@ -0,0 +1,20 @@ +# gzip [![Build Status](https://travis-ci.org/go-macaron/gzip.svg?branch=master)](https://travis-ci.org/go-macaron/gzip) [![](http://gocover.io/_badge/github.com/go-macaron/gzip)](http://gocover.io/github.com/go-macaron/gzip) + +Middleware gzip provides compress to responses for [Macaron](https://github.com/go-macaron/macaron). + +### Installation + + go get github.com/go-macaron/gzip + +## Getting Help + +- [API Reference](https://gowalker.org/github.com/go-macaron/gzip) +- [Documentation](http://go-macaron.com/docs/middlewares/gzip) + +## Credits + +This package is a modified version of [martini-contrib/gzip](https://github.com/martini-contrib/gzip). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/go-macaron/gzip/gzip.go b/Godeps/_workspace/src/github.com/go-macaron/gzip/gzip.go new file mode 100644 index 00000000000..ca54ec67d32 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/gzip/gzip.go @@ -0,0 +1,118 @@ +// Copyright 2013 Martini Authors +// Copyright 2015 The Macaron Authors +// +// 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 gzip + +import ( + "bufio" + "fmt" + "net" + "net/http" + "strings" + + "github.com/klauspost/compress/gzip" + "gopkg.in/macaron.v1" +) + +const ( + _HEADER_ACCEPT_ENCODING = "Accept-Encoding" + _HEADER_CONTENT_ENCODING = "Content-Encoding" + _HEADER_CONTENT_LENGTH = "Content-Length" + _HEADER_CONTENT_TYPE = "Content-Type" + _HEADER_VARY = "Vary" +) + +// Options represents a struct for specifying configuration options for the GZip middleware. +type Options struct { + // Compression level. Can be DefaultCompression(-1), ConstantCompression(-2) + // or any integer value between BestSpeed(1) and BestCompression(9) inclusive. + CompressionLevel int +} + +func isCompressionLevelValid(level int) bool { + return level == gzip.DefaultCompression || + level == gzip.ConstantCompression || + (level >= gzip.BestSpeed && level <= gzip.BestCompression) +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + + if !isCompressionLevelValid(opt.CompressionLevel) { + // For web content, level 4 seems to be a sweet spot. + opt.CompressionLevel = 4 + } + return opt +} + +// Gziper returns a Handler that adds gzip compression to all requests. +// Make sure to include the Gzip middleware above other middleware +// that alter the response body (like the render middleware). +func Gziper(options ...Options) macaron.Handler { + opt := prepareOptions(options) + + return func(ctx *macaron.Context) { + if !strings.Contains(ctx.Req.Header.Get(_HEADER_ACCEPT_ENCODING), "gzip") { + return + } + + headers := ctx.Resp.Header() + headers.Set(_HEADER_CONTENT_ENCODING, "gzip") + headers.Set(_HEADER_VARY, _HEADER_ACCEPT_ENCODING) + + // We've made sure compression level is valid in prepareGzipOptions, + // no need to check same error again. + gz, err := gzip.NewWriterLevel(ctx.Resp, opt.CompressionLevel) + if err != nil { + panic(err.Error()) + } + defer gz.Close() + + gzw := gzipResponseWriter{gz, ctx.Resp} + ctx.Resp = gzw + ctx.MapTo(gzw, (*http.ResponseWriter)(nil)) + if ctx.Render != nil { + ctx.Render.SetResponseWriter(gzw) + } + + ctx.Next() + + // delete content length after we know we have been written to + gzw.Header().Del("Content-Length") + } +} + +type gzipResponseWriter struct { + w *gzip.Writer + macaron.ResponseWriter +} + +func (grw gzipResponseWriter) Write(p []byte) (int, error) { + if len(grw.Header().Get(_HEADER_CONTENT_TYPE)) == 0 { + grw.Header().Set(_HEADER_CONTENT_TYPE, http.DetectContentType(p)) + } + return grw.w.Write(p) +} + +func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := grw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} diff --git a/Godeps/_workspace/src/github.com/go-macaron/inject/.travis.yml b/Godeps/_workspace/src/github.com/go-macaron/inject/.travis.yml new file mode 100644 index 00000000000..2774fb35d50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/inject/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/Godeps/_workspace/src/github.com/go-macaron/inject/LICENSE b/Godeps/_workspace/src/github.com/go-macaron/inject/LICENSE new file mode 100644 index 00000000000..37ec93a14fd --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/inject/LICENSE @@ -0,0 +1,191 @@ +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +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 +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. diff --git a/Godeps/_workspace/src/github.com/go-macaron/inject/README.md b/Godeps/_workspace/src/github.com/go-macaron/inject/README.md new file mode 100644 index 00000000000..c65c76955dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/inject/README.md @@ -0,0 +1,11 @@ +# inject [![Build Status](https://travis-ci.org/go-macaron/inject.svg?branch=master)](https://travis-ci.org/go-macaron/inject) [![](http://gocover.io/_badge/github.com/go-macaron/inject)](http://gocover.io/github.com/go-macaron/inject) + +Package inject provides utilities for mapping and injecting dependencies in various ways. + +**This a modified version of [codegangsta/inject](https://github.com/codegangsta/inject) for special purpose of Macaron** + +**Please use the original version if you need dependency injection feature** + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.go b/Godeps/_workspace/src/github.com/go-macaron/inject/inject.go similarity index 89% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.go rename to Godeps/_workspace/src/github.com/go-macaron/inject/inject.go index 87cbe22b5e8..b4a6ea45fe7 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/inject/inject.go +++ b/Godeps/_workspace/src/github.com/go-macaron/inject/inject.go @@ -1,3 +1,18 @@ +// Copyright 2013 Jeremy Saenz +// Copyright 2015 The Macaron Authors +// +// 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 inject provides utilities for mapping and injecting dependencies in various ways. package inject diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/.gitignore b/Godeps/_workspace/src/github.com/go-macaron/session/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/.gitignore rename to Godeps/_workspace/src/github.com/go-macaron/session/.gitignore diff --git a/Godeps/_workspace/src/github.com/go-macaron/session/.travis.yml b/Godeps/_workspace/src/github.com/go-macaron/session/.travis.yml new file mode 100644 index 00000000000..2774fb35d50 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/session/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/LICENSE b/Godeps/_workspace/src/github.com/go-macaron/session/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/LICENSE rename to Godeps/_workspace/src/github.com/go-macaron/session/LICENSE diff --git a/Godeps/_workspace/src/github.com/go-macaron/session/README.md b/Godeps/_workspace/src/github.com/go-macaron/session/README.md new file mode 100644 index 00000000000..280ace36dc7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-macaron/session/README.md @@ -0,0 +1,20 @@ +# session [![Build Status](https://travis-ci.org/go-macaron/session.svg?branch=master)](https://travis-ci.org/go-macaron/session) [![](http://gocover.io/_badge/github.com/go-macaron/session)](http://gocover.io/github.com/go-macaron/session) + +Middleware session provides session management for [Macaron](https://github.com/go-macaron/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase, Ledis and Nodb. + +### Installation + + go get github.com/go-macaron/session + +## Getting Help + +- [API Reference](https://gowalker.org/github.com/go-macaron/session) +- [Documentation](http://go-macaron.com/docs/middlewares/session) + +## Credits + +This package is a modified version of [beego/session](https://github.com/astaxie/beego/tree/master/session). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/couchbase/couchbase.go b/Godeps/_workspace/src/github.com/go-macaron/session/couchbase/couchbase.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/couchbase/couchbase.go rename to Godeps/_workspace/src/github.com/go-macaron/session/couchbase/couchbase.go index 93953c69724..1a77a42b5f3 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/couchbase/couchbase.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/couchbase/couchbase.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -21,7 +21,7 @@ import ( "github.com/couchbaselabs/go-couchbase" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // CouchbaseSessionStore represents a couchbase session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go b/Godeps/_workspace/src/github.com/go-macaron/session/file.go similarity index 94% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/file.go rename to Godeps/_workspace/src/github.com/go-macaron/session/file.go index cab807d00bd..438269ea845 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/file.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -78,6 +78,9 @@ func (s *FileStore) ID() string { // Release releases resource and save data to provider. func (s *FileStore) Release() error { + s.p.lock.Lock() + defer s.p.lock.Unlock() + data, err := EncodeGob(s.data) if err != nil { return err @@ -97,14 +100,17 @@ func (s *FileStore) Flush() error { // FileProvider represents a file session provider implementation. type FileProvider struct { + lock sync.RWMutex maxlifetime int64 rootPath string } // Init initializes file session provider with given root path. func (p *FileProvider) Init(maxlifetime int64, rootPath string) error { + p.lock.Lock() p.maxlifetime = maxlifetime p.rootPath = rootPath + p.lock.Unlock() return nil } @@ -118,6 +124,8 @@ func (p *FileProvider) Read(sid string) (_ RawStore, err error) { if err = os.MkdirAll(path.Dir(filename), os.ModePerm); err != nil { return nil, err } + p.lock.RLock() + defer p.lock.RUnlock() var f *os.File if com.IsFile(filename) { @@ -152,15 +160,22 @@ func (p *FileProvider) Read(sid string) (_ RawStore, err error) { // Exist returns true if session with given ID exists. func (p *FileProvider) Exist(sid string) bool { + p.lock.RLock() + defer p.lock.RUnlock() return com.IsFile(p.filepath(sid)) } // Destory deletes a session by session ID. func (p *FileProvider) Destory(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() return os.Remove(p.filepath(sid)) } func (p *FileProvider) regenerate(oldsid, sid string) (err error) { + p.lock.Lock() + defer p.lock.Unlock() + filename := p.filepath(sid) if com.IsExist(filename) { return fmt.Errorf("new sid '%s' already exists", sid) @@ -219,6 +234,9 @@ func (p *FileProvider) Count() int { // GC calls GC to clean expired sessions. func (p *FileProvider) GC() { + p.lock.RLock() + defer p.lock.RUnlock() + if !com.IsExist(p.rootPath) { return } diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go b/Godeps/_workspace/src/github.com/go-macaron/session/ledis/ledis.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go rename to Godeps/_workspace/src/github.com/go-macaron/session/ledis/ledis.go index afde7134cbd..73e27f5f22a 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/ledis/ledis.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -25,7 +25,7 @@ import ( "github.com/siddontang/ledisdb/ledis" "gopkg.in/ini.v1" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // LedisStore represents a ledis session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/ledis/ledis.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/ledis/ledis.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go b/Godeps/_workspace/src/github.com/go-macaron/session/memcache/memcache.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go rename to Godeps/_workspace/src/github.com/go-macaron/session/memcache/memcache.go index b4fcdde62bd..f4a9e81d257 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/memcache/memcache.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -22,7 +22,7 @@ import ( "github.com/bradfitz/gomemcache/memcache" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // MemcacheStore represents a memcache session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/memcache/memcache.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/memcache/memcache.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go b/Godeps/_workspace/src/github.com/go-macaron/session/memory.go similarity index 93% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go rename to Godeps/_workspace/src/github.com/go-macaron/session/memory.go index e717635b951..4ad929352e9 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/memory.go @@ -1,212 +1,217 @@ -// Copyright 2013 Beego Authors -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "container/list" - "fmt" - "sync" - "time" -) - -// MemStore represents a in-memory session store implementation. -type MemStore struct { - sid string - lock sync.RWMutex - data map[interface{}]interface{} - lastAccess time.Time -} - -// NewMemStore creates and returns a memory session store. -func NewMemStore(sid string) *MemStore { - return &MemStore{ - sid: sid, - data: make(map[interface{}]interface{}), - lastAccess: time.Now(), - } -} - -// Set sets value to given key in session. -func (s *MemStore) Set(key, val interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data[key] = val - return nil -} - -// Get gets value by given key in session. -func (s *MemStore) Get(key interface{}) interface{} { - s.lock.RLock() - defer s.lock.RUnlock() - - return s.data[key] -} - -// Delete deletes a key from session. -func (s *MemStore) Delete(key interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - delete(s.data, key) - return nil -} - -// ID returns current session ID. -func (s *MemStore) ID() string { - return s.sid -} - -// Release releases resource and save data to provider. -func (_ *MemStore) Release() error { - return nil -} - -// Flush deletes all session data. -func (s *MemStore) Flush() error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = make(map[interface{}]interface{}) - return nil -} - -// MemProvider represents a in-memory session provider implementation. -type MemProvider struct { - lock sync.RWMutex - maxLifetime int64 - data map[string]*list.Element - // A priority list whose lastAccess newer gets higer priority. - list *list.List -} - -// Init initializes memory session provider. -func (p *MemProvider) Init(maxLifetime int64, _ string) error { - p.maxLifetime = maxLifetime - return nil -} - -// update expands time of session store by given ID. -func (p *MemProvider) update(sid string) error { - p.lock.Lock() - defer p.lock.Unlock() - - if e, ok := p.data[sid]; ok { - e.Value.(*MemStore).lastAccess = time.Now() - p.list.MoveToFront(e) - return nil - } - return nil -} - -// Read returns raw session store by session ID. -func (p *MemProvider) Read(sid string) (_ RawStore, err error) { - p.lock.RLock() - e, ok := p.data[sid] - p.lock.RUnlock() - - if ok { - if err = p.update(sid); err != nil { - return nil, err - } - return e.Value.(*MemStore), nil - } - - // Create a new session. - p.lock.Lock() - defer p.lock.Unlock() - - s := NewMemStore(sid) - p.data[sid] = p.list.PushBack(s) - return s, nil -} - -// Exist returns true if session with given ID exists. -func (p *MemProvider) Exist(sid string) bool { - p.lock.RLock() - defer p.lock.RUnlock() - - _, ok := p.data[sid] - return ok -} - -// Destory deletes a session by session ID. -func (p *MemProvider) Destory(sid string) error { - p.lock.Lock() - defer p.lock.Unlock() - - e, ok := p.data[sid] - if !ok { - return nil - } - - p.list.Remove(e) - delete(p.data, sid) - return nil -} - -// Regenerate regenerates a session store from old session ID to new one. -func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { - if p.Exist(sid) { - return nil, fmt.Errorf("new sid '%s' already exists", sid) - } - - s, err := p.Read(oldsid) - if err != nil { - return nil, err - } - - if err = p.Destory(oldsid); err != nil { - return nil, err - } - - s.(*MemStore).sid = sid - p.data[sid] = p.list.PushBack(s) - return s, nil -} - -// Count counts and returns number of sessions. -func (p *MemProvider) Count() int { - return p.list.Len() -} - -// GC calls GC to clean expired sessions. -func (p *MemProvider) GC() { - p.lock.RLock() - for { - // No session in the list. - e := p.list.Back() - if e == nil { - break - } - - if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { - p.lock.RUnlock() - p.lock.Lock() - p.list.Remove(e) - delete(p.data, e.Value.(*MemStore).sid) - p.lock.Unlock() - p.lock.RLock() - } else { - break - } - } - p.lock.RUnlock() -} - -func init() { - Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) -} +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// 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 session + +import ( + "container/list" + "fmt" + "sync" + "time" +) + +// MemStore represents a in-memory session store implementation. +type MemStore struct { + sid string + lock sync.RWMutex + data map[interface{}]interface{} + lastAccess time.Time +} + +// NewMemStore creates and returns a memory session store. +func NewMemStore(sid string) *MemStore { + return &MemStore{ + sid: sid, + data: make(map[interface{}]interface{}), + lastAccess: time.Now(), + } +} + +// Set sets value to given key in session. +func (s *MemStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *MemStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete deletes a key from session. +func (s *MemStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *MemStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (_ *MemStore) Release() error { + return nil +} + +// Flush deletes all session data. +func (s *MemStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// MemProvider represents a in-memory session provider implementation. +type MemProvider struct { + lock sync.RWMutex + maxLifetime int64 + data map[string]*list.Element + // A priority list whose lastAccess newer gets higer priority. + list *list.List +} + +// Init initializes memory session provider. +func (p *MemProvider) Init(maxLifetime int64, _ string) error { + p.lock.Lock() + p.maxLifetime = maxLifetime + p.lock.Unlock() + return nil +} + +// update expands time of session store by given ID. +func (p *MemProvider) update(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() + + if e, ok := p.data[sid]; ok { + e.Value.(*MemStore).lastAccess = time.Now() + p.list.MoveToFront(e) + return nil + } + return nil +} + +// Read returns raw session store by session ID. +func (p *MemProvider) Read(sid string) (_ RawStore, err error) { + p.lock.RLock() + e, ok := p.data[sid] + p.lock.RUnlock() + + if ok { + if err = p.update(sid); err != nil { + return nil, err + } + return e.Value.(*MemStore), nil + } + + // Create a new session. + p.lock.Lock() + defer p.lock.Unlock() + + s := NewMemStore(sid) + p.data[sid] = p.list.PushBack(s) + return s, nil +} + +// Exist returns true if session with given ID exists. +func (p *MemProvider) Exist(sid string) bool { + p.lock.RLock() + defer p.lock.RUnlock() + + _, ok := p.data[sid] + return ok +} + +// Destory deletes a session by session ID. +func (p *MemProvider) Destory(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() + + e, ok := p.data[sid] + if !ok { + return nil + } + + p.list.Remove(e) + delete(p.data, sid) + return nil +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + s, err := p.Read(oldsid) + if err != nil { + return nil, err + } + + if err = p.Destory(oldsid); err != nil { + return nil, err + } + + s.(*MemStore).sid = sid + + p.lock.Lock() + defer p.lock.Unlock() + p.data[sid] = p.list.PushBack(s) + return s, nil +} + +// Count counts and returns number of sessions. +func (p *MemProvider) Count() int { + return p.list.Len() +} + +// GC calls GC to clean expired sessions. +func (p *MemProvider) GC() { + p.lock.RLock() + for { + // No session in the list. + e := p.list.Back() + if e == nil { + break + } + + if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { + p.lock.RUnlock() + p.lock.Lock() + p.list.Remove(e) + delete(p.data, e.Value.(*MemStore).sid) + p.lock.Unlock() + p.lock.RLock() + } else { + break + } + } + p.lock.RUnlock() +} + +func init() { + Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go b/Godeps/_workspace/src/github.com/go-macaron/session/mysql/mysql.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go rename to Godeps/_workspace/src/github.com/go-macaron/session/mysql/mysql.go index 7997e03c0d8..0d96fb8ec18 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/mysql/mysql.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -24,7 +24,7 @@ import ( _ "github.com/go-sql-driver/mysql" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // MysqlStore represents a mysql session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/mysql/mysql.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/mysql/mysql.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go b/Godeps/_workspace/src/github.com/go-macaron/session/nodb/nodb.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go rename to Godeps/_workspace/src/github.com/go-macaron/session/nodb/nodb.go index 7f017bf0457..c02f323f322 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/nodb/nodb.go @@ -1,4 +1,4 @@ -// Copyright 2015 Unknwon +// Copyright 2015 The Macaron Authors // // 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 @@ -21,7 +21,7 @@ import ( "github.com/lunny/nodb" "github.com/lunny/nodb/config" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // NodbStore represents a nodb session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/nodb/nodb.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/nodb/nodb.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go b/Godeps/_workspace/src/github.com/go-macaron/session/postgres/postgres.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go rename to Godeps/_workspace/src/github.com/go-macaron/session/postgres/postgres.go index 5cb4c82ea84..8749f575f8d 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/postgres/postgres.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -24,7 +24,7 @@ import ( _ "github.com/lib/pq" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // PostgresStore represents a postgres session store implementation. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/postgres/postgres.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/postgres/postgres.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go b/Godeps/_workspace/src/github.com/go-macaron/session/redis/redis.go similarity index 81% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go rename to Godeps/_workspace/src/github.com/go-macaron/session/redis/redis.go index 6d6a2c464c8..ca1cf88de61 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/redis/redis.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -25,22 +25,23 @@ import ( "gopkg.in/ini.v1" "gopkg.in/redis.v2" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" ) // RedisStore represents a redis session store implementation. type RedisStore struct { - c *redis.Client - sid string - duration time.Duration - lock sync.RWMutex - data map[interface{}]interface{} + c *redis.Client + prefix, sid string + duration time.Duration + lock sync.RWMutex + data map[interface{}]interface{} } // NewRedisStore creates and returns a redis session store. -func NewRedisStore(c *redis.Client, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { +func NewRedisStore(c *redis.Client, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { return &RedisStore{ c: c, + prefix: prefix, sid: sid, duration: dur, data: kv, @@ -85,7 +86,7 @@ func (s *RedisStore) Release() error { return err } - return s.c.SetEx(s.sid, s.duration, string(data)).Err() + return s.c.SetEx(s.prefix+s.sid, s.duration, string(data)).Err() } // Flush deletes all session data. @@ -101,10 +102,11 @@ func (s *RedisStore) Flush() error { type RedisProvider struct { c *redis.Client duration time.Duration + prefix string } // Init initializes redis session provider. -// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 +// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session; func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime)) if err != nil { @@ -136,6 +138,8 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { if err != nil { return fmt.Errorf("error parsing idle timeout: %v", err) } + case "prefix": + p.prefix = v default: return fmt.Errorf("session/redis: unsupported option '%s'", k) } @@ -147,14 +151,15 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { // Read returns raw session store by session ID. func (p *RedisProvider) Read(sid string) (session.RawStore, error) { + psid := p.prefix + sid if !p.Exist(sid) { - if err := p.c.Set(sid, "").Err(); err != nil { + if err := p.c.Set(psid, "").Err(); err != nil { return nil, err } } var kv map[interface{}]interface{} - kvs, err := p.c.Get(sid).Result() + kvs, err := p.c.Get(psid).Result() if err != nil { return nil, err } @@ -167,37 +172,40 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) { } } - return NewRedisStore(p.c, sid, p.duration, kv), nil + return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil } // Exist returns true if session with given ID exists. func (p *RedisProvider) Exist(sid string) bool { - has, err := p.c.Exists(sid).Result() + has, err := p.c.Exists(p.prefix + sid).Result() return err == nil && has } // Destory deletes a session by session ID. func (p *RedisProvider) Destory(sid string) error { - return p.c.Del(sid).Err() + return p.c.Del(p.prefix + sid).Err() } // Regenerate regenerates a session store from old session ID to new one. func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + poldsid := p.prefix + oldsid + psid := p.prefix + sid + if p.Exist(sid) { return nil, fmt.Errorf("new sid '%s' already exists", sid) } else if !p.Exist(oldsid) { // Make a fake old session. - if err = p.c.SetEx(oldsid, p.duration, "").Err(); err != nil { + if err = p.c.SetEx(poldsid, p.duration, "").Err(); err != nil { return nil, err } } - if err = p.c.Rename(oldsid, sid).Err(); err != nil { + if err = p.c.Rename(poldsid, psid).Err(); err != nil { return nil, err } var kv map[interface{}]interface{} - kvs, err := p.c.Get(sid).Result() + kvs, err := p.c.Get(psid).Result() if err != nil { return nil, err } @@ -211,7 +219,7 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err } } - return NewRedisStore(p.c, sid, p.duration, kv), nil + return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil } // Count counts and returns number of sessions. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.goconvey b/Godeps/_workspace/src/github.com/go-macaron/session/redis/redis.goconvey similarity index 100% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.goconvey rename to Godeps/_workspace/src/github.com/go-macaron/session/redis/redis.goconvey diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go b/Godeps/_workspace/src/github.com/go-macaron/session/session.go similarity index 98% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/session.go rename to Godeps/_workspace/src/github.com/go-macaron/session/session.go index 9cc1d528749..7e7b833c552 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/session.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -16,8 +16,6 @@ // Package session a middleware that provides the session management of Macaron. package session -// NOTE: last sync 000033e on Nov 4, 2014. - import ( "encoding/hex" "fmt" @@ -25,10 +23,10 @@ import ( "net/url" "time" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" ) -const _VERSION = "0.1.6" +const _VERSION = "0.3.0" func Version() string { return _VERSION diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go b/Godeps/_workspace/src/github.com/go-macaron/session/utils.go similarity index 80% rename from Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go rename to Godeps/_workspace/src/github.com/go-macaron/session/utils.go index 6c9ea495fe5..07a1283df94 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go +++ b/Godeps/_workspace/src/github.com/go-macaron/session/utils.go @@ -1,5 +1,5 @@ // Copyright 2013 Beego Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -24,6 +24,17 @@ import ( "github.com/Unknwon/com" ) +func init() { + gob.Register([]interface{}{}) + gob.Register(map[int]interface{}{}) + gob.Register(map[string]interface{}{}) + gob.Register(map[interface{}]interface{}{}) + gob.Register(map[string]string{}) + gob.Register(map[int]string{}) + gob.Register(map[int]int{}) + gob.Register(map[int]int64{}) +} + func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { for _, v := range obj { gob.Register(v) diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/LICENSE b/Godeps/_workspace/src/github.com/klauspost/compress/LICENSE new file mode 100644 index 00000000000..74487567632 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the 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. diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/copy.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/copy.go new file mode 100644 index 00000000000..a3200a8f49e --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/copy.go @@ -0,0 +1,32 @@ +// Copyright 2012 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. + +package flate + +// forwardCopy is like the built-in copy function except that it always goes +// forward from the start, even if the dst and src overlap. +// It is equivalent to: +// for i := 0; i < n; i++ { +// mem[dst+i] = mem[src+i] +// } +func forwardCopy(mem []byte, dst, src, n int) { + if dst <= src { + copy(mem[dst:dst+n], mem[src:src+n]) + return + } + for { + if dst >= src+n { + copy(mem[dst:dst+n], mem[src:src+n]) + return + } + // There is some forward overlap. The destination + // will be filled with a repeated pattern of mem[src:src+k]. + // We copy one instance of the pattern here, then repeat. + // Each time around this loop k will double. + k := dst - src + copy(mem[dst:dst+k], mem[src:src+k]) + n -= k + dst += k + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.go new file mode 100644 index 00000000000..45d52f629c2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.go @@ -0,0 +1,39 @@ +//+build !noasm +//+build !appengine + +// Copyright 2015, Klaus Post, see LICENSE for details. + +package flate + +import ( + "github.com/klauspost/cpuid" +) + +// crc32sse returns a hash for the first 4 bytes of the slice +// len(a) must be >= 4. +//go:noescape +func crc32sse(a []byte) hash + +// crc32sseAll calculates hashes for each 4-byte set in a. +// dst must be east len(a) - 4 in size. +// The size is not checked by the assembly. +//go:noescape +func crc32sseAll(a []byte, dst []hash) + +// matchLenSSE4 returns the number of matching bytes in a and b +// up to length 'max'. Both slices must be at least 'max' +// bytes in size. +// It uses the PCMPESTRI SSE 4.2 instruction. +//go:noescape +func matchLenSSE4(a, b []byte, max int) int + +// histogram accumulates a histogram of b in h. +// h must be at least 256 entries in length, +// and must be cleared before calling this function. +//go:noescape +func histogram(b []byte, h []int32) + +// Detect SSE 4.2 feature. +func init() { + useSSE42 = cpuid.CPU.SSE42() +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.s b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.s new file mode 100644 index 00000000000..f94969b7603 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_amd64.s @@ -0,0 +1,212 @@ +//+build !noasm !appengine + +// Copyright 2015, Klaus Post, see LICENSE for details. + +// func crc32sse(a []byte) hash +TEXT ·crc32sse(SB), 7, $0 + MOVQ a+0(FP), R10 + XORQ BX, BX + + // CRC32 dword (R10), EBX + BYTE $0xF2; BYTE $0x41; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0x1a + + MOVL BX, ret+24(FP) + RET + +// func crc32sseAll(a []byte, dst []hash) +TEXT ·crc32sseAll(SB), 7, $0 + MOVQ a+0(FP), R8 // R8: src + MOVQ a_len+8(FP), R10 // input length + MOVQ dst+24(FP), R9 // R9: dst + SUBQ $4, R10 + JS end + JZ one_crc + MOVQ R10, R13 + SHRQ $2, R10 // len/4 + ANDQ $3, R13 // len&3 + XORQ BX, BX + ADDQ $1, R13 + TESTQ R10, R10 + JZ rem_loop + +crc_loop: + MOVQ (R8), R11 + XORQ BX, BX + XORQ DX, DX + XORQ DI, DI + MOVQ R11, R12 + SHRQ $8, R11 + MOVQ R12, AX + MOVQ R11, CX + SHRQ $16, R12 + SHRQ $16, R11 + MOVQ R12, SI + + // CRC32 EAX, EBX + BYTE $0xF2; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0xd8 + + // CRC32 ECX, EDX + BYTE $0xF2; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0xd1 + + // CRC32 ESI, EDI + BYTE $0xF2; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0xfe + MOVL BX, (R9) + MOVL DX, 4(R9) + MOVL DI, 8(R9) + + XORQ BX, BX + MOVL R11, AX + + // CRC32 EAX, EBX + BYTE $0xF2; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0xd8 + MOVL BX, 12(R9) + + ADDQ $16, R9 + ADDQ $4, R8 + XORQ BX, BX + SUBQ $1, R10 + JNZ crc_loop + +rem_loop: + MOVL (R8), AX + + // CRC32 EAX, EBX + BYTE $0xF2; BYTE $0x0f + BYTE $0x38; BYTE $0xf1; BYTE $0xd8 + + MOVL BX, (R9) + ADDQ $4, R9 + ADDQ $1, R8 + XORQ BX, BX + SUBQ $1, R13 + JNZ rem_loop + +end: + RET + +one_crc: + MOVQ $1, R13 + XORQ BX, BX + JMP rem_loop + +// func matchLenSSE4(a, b []byte, max int) int +TEXT ·matchLenSSE4(SB), 7, $0 + MOVQ a+0(FP), R8 // R8: &a + MOVQ b+24(FP), R9 // R9: &b + MOVQ max+48(FP), R10 // R10: max + XORQ R11, R11 // match length + + MOVQ R10, R12 + SHRQ $4, R10 // max/16 + ANDQ $15, R12 // max & 15 + CMPQ R10, $0 + JEQ matchlen_verysmall + +loopback_matchlen: + MOVOU (R8), X0 // a[x] + MOVOU (R9), X1 // b[x] + + // PCMPESTRI $0x18, X1, X0 + BYTE $0x66; BYTE $0x0f; BYTE $0x3a + BYTE $0x61; BYTE $0xc1; BYTE $0x18 + + JC match_ended + + ADDQ $16, R8 + ADDQ $16, R9 + ADDQ $16, R11 + + SUBQ $1, R10 + JNZ loopback_matchlen + +matchlen_verysmall: + CMPQ R12, $0 + JEQ done_matchlen + +loopback_matchlen_single: + // Naiive, but small use + MOVB (R8), R13 + MOVB (R9), R14 + CMPB R13, R14 + JNE done_matchlen + ADDQ $1, R8 + ADDQ $1, R9 + ADDQ $1, R11 + SUBQ $1, R12 + JNZ loopback_matchlen_single + MOVQ R11, ret+56(FP) + RET + +match_ended: + ADDQ CX, R11 + +done_matchlen: + MOVQ R11, ret+56(FP) + RET + +// func histogram(b []byte, h []int32) +TEXT ·histogram(SB), 7, $0 + MOVQ b+0(FP), SI // SI: &b + MOVQ b_len+8(FP), R9 // R9: len(b) + MOVQ h+24(FP), DI // DI: Histogram + MOVQ R9, R8 + SHRQ $3, R8 + JZ hist1 + XORQ R11, R11 + +loop_hist8: + MOVQ (SI), R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + MOVB R10, R11 + INCL (DI)(R11*4) + SHRQ $8, R10 + + INCL (DI)(R10*4) + + ADDQ $8, SI + DECQ R8 + JNZ loop_hist8 + +hist1: + ANDQ $7, R9 + JZ end_hist + XORQ R10, R10 + +loop_hist1: + MOVB (SI), R10 + INCL (DI)(R10*4) + INCQ SI + DECQ R9 + JNZ loop_hist1 + +end_hist: + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_noasm.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_noasm.go new file mode 100644 index 00000000000..1c6d23eed63 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/crc32_noasm.go @@ -0,0 +1,34 @@ +//+build !amd64 noasm appengine + +// Copyright 2015, Klaus Post, see LICENSE for details. + +package flate + +func init() { + useSSE42 = false +} + +// crc32sse should never be called. +func crc32sse(a []byte) hash { + panic("no assembler") +} + +// crc32sseAll should never be called. +func crc32sseAll(a []byte, dst []hash) { + panic("no assembler") +} + +// matchLenSSE4 should never be called. +func matchLenSSE4(a, b []byte, max int) int { + panic("no assembler") + return 0 +} + +// histogram accumulates a histogram of b in h. +// h must be at least 256 entries in length, +// and must be cleared before calling this function. +func histogram(b []byte, h []int32) { + for _, t := range b { + h[t]++ + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/deflate.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/deflate.go new file mode 100644 index 00000000000..a103c8f4ef5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/deflate.go @@ -0,0 +1,1293 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright (c) 2015 Klaus Post +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "fmt" + "io" + "math" +) + +const ( + NoCompression = 0 + BestSpeed = 1 + fastCompression = 3 + BestCompression = 9 + DefaultCompression = -1 + ConstantCompression = -2 // Does only Huffman encoding + logWindowSize = 15 + windowSize = 1 << logWindowSize + windowMask = windowSize - 1 + logMaxOffsetSize = 15 // Standard DEFLATE + minMatchLength = 4 // The smallest match that the compressor looks for + maxMatchLength = 258 // The longest match for the compressor + minOffsetSize = 1 // The shortest offset that makes any sense + + // The maximum number of tokens we put into a single flat block, just too + // stop things from getting too large. + maxFlateBlockTokens = 1 << 14 + maxStoreBlockSize = 65535 + hashBits = 17 // After 17 performance degrades + hashSize = 1 << hashBits + hashMask = (1 << hashBits) - 1 + hashShift = (hashBits + minMatchLength - 1) / minMatchLength + maxHashOffset = 1 << 24 + + skipNever = math.MaxInt32 +) + +var useSSE42 bool + +type compressionLevel struct { + good, lazy, nice, chain, fastSkipHashing, level int +} + +var levels = []compressionLevel{ + {}, // 0 + // For levels 1-3 we don't bother trying with lazy matches + {4, 0, 8, 4, 4, 1}, + {4, 0, 16, 8, 5, 2}, + {4, 0, 32, 32, 6, 3}, + // Levels 4-9 use increasingly more lazy matching + // and increasingly stringent conditions for "good enough". + {4, 4, 16, 16, skipNever, 4}, + {8, 16, 32, 32, skipNever, 5}, + {8, 16, 128, 128, skipNever, 6}, + {8, 32, 128, 256, skipNever, 7}, + {32, 128, 258, 1024, skipNever, 8}, + {32, 258, 258, 4096, skipNever, 9}, +} + +type hashid uint32 + +type compressor struct { + compressionLevel + + w *huffmanBitWriter + bulkHasher func([]byte, []hash) + + // compression algorithm + fill func(*compressor, []byte) int // copy data to window + step func(*compressor) // process window + sync bool // requesting flush + + // Input hash chains + // hashHead[hashValue] contains the largest inputIndex with the specified hash value + // If hashHead[hashValue] is within the current window, then + // hashPrev[hashHead[hashValue] & windowMask] contains the previous index + // with the same hash value. + chainHead int + hashHead []hashid + hashPrev []hashid + hashOffset int + + // input window: unprocessed data is window[index:windowEnd] + index int + window []byte + windowEnd int + blockStart int // window index where current tokens start + byteAvailable bool // if true, still need to process window[index-1]. + + // queued output tokens + tokens tokens + + // deflate state + length int + offset int + hash hash + maxInsertIndex int + err error + ii uint16 // position of last match, intended to overflow to reset. + + hashMatch [maxMatchLength + minMatchLength]hash +} + +type hash int32 + +func (d *compressor) fillDeflate(b []byte) int { + if d.index >= 2*windowSize-(minMatchLength+maxMatchLength) { + // shift the window by windowSize + copy(d.window, d.window[windowSize:2*windowSize]) + d.index -= windowSize + d.windowEnd -= windowSize + if d.blockStart >= windowSize { + d.blockStart -= windowSize + } else { + d.blockStart = math.MaxInt32 + } + d.hashOffset += windowSize + if d.hashOffset > maxHashOffset { + delta := d.hashOffset - 1 + d.hashOffset -= delta + d.chainHead -= delta + for i, v := range d.hashPrev { + if int(v) > delta { + d.hashPrev[i] = hashid(int(v) - delta) + } else { + d.hashPrev[i] = 0 + } + } + for i, v := range d.hashHead { + if int(v) > delta { + d.hashHead[i] = hashid(int(v) - delta) + } else { + d.hashHead[i] = 0 + } + } + } + } + n := copy(d.window[d.windowEnd:], b) + d.windowEnd += n + return n +} + +func (d *compressor) writeBlock(tok tokens, index int, eof bool) error { + if index > 0 || eof { + var window []byte + if d.blockStart <= index { + window = d.window[d.blockStart:index] + } + d.blockStart = index + d.w.writeBlock(tok, eof, window) + return d.w.err + } + return nil +} + +// fillWindow will fill the current window with the supplied +// dictionary and calculate all hashes. +// This is much faster than doing a full encode. +// Should only be used after a start/reset. +func (d *compressor) fillWindow(b []byte) { + // Do not fill window if we are in store-only mode, + // use constant or Snappy compression. + if d.compressionLevel.level == 0 { + return + } + // If we are given too much, cut it. + if len(b) > windowSize { + b = b[len(b)-windowSize:] + } + // Add all to window. + n := copy(d.window[d.windowEnd:], b) + + // Calculate 256 hashes at the time (more L1 cache hits) + loops := (n + 256 - minMatchLength) / 256 + for j := 0; j < loops; j++ { + startindex := j * 256 + end := startindex + 256 + minMatchLength - 1 + if end > n { + end = n + } + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + + if dstSize <= 0 { + continue + } + + dst := d.hashMatch[:dstSize] + d.bulkHasher(tocheck, dst) + var newH hash + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + d.hashPrev[di&windowMask] = d.hashHead[newH] + // Set the head of the hash chain to us. + d.hashHead[newH] = hashid(di + d.hashOffset) + } + d.hash = newH + } + // Update window information. + d.windowEnd += n + d.index = n +} + +// Try to find a match starting at index whose length is greater than prevSize. +// We only look at chainCount possibilities before giving up. +// pos = d.index, prevHead = d.chainHead-d.hashOffset, prevLength=minMatchLength-1, lookahead +func (d *compressor) findMatch(pos int, prevHead int, prevLength int, lookahead int) (length, offset int, ok bool) { + minMatchLook := maxMatchLength + if lookahead < minMatchLook { + minMatchLook = lookahead + } + + win := d.window[0 : pos+minMatchLook] + + // We quit when we get a match that's at least nice long + nice := len(win) - pos + if d.nice < nice { + nice = d.nice + } + + // If we've got a match that's good enough, only look in 1/4 the chain. + tries := d.chain + length = prevLength + if length >= d.good { + tries >>= 2 + } + + wEnd := win[pos+length] + wPos := win[pos:] + minIndex := pos - windowSize + + for i := prevHead; tries > 0; tries-- { + if wEnd == win[i+length] { + n := matchLen(win[i:], wPos, minMatchLook) + + if n > length && (n > minMatchLength || pos-i <= 4096) { + length = n + offset = pos - i + ok = true + if n >= nice { + // The match is good enough that we don't try to find a better one. + break + } + wEnd = win[pos+n] + } + } + if i == minIndex { + // hashPrev[i & windowMask] has already been overwritten, so stop now. + break + } + i = int(d.hashPrev[i&windowMask]) - d.hashOffset + if i < minIndex || i < 0 { + break + } + } + return +} + +// Try to find a match starting at index whose length is greater than prevSize. +// We only look at chainCount possibilities before giving up. +// pos = d.index, prevHead = d.chainHead-d.hashOffset, prevLength=minMatchLength-1, lookahead +func (d *compressor) findMatchSSE(pos int, prevHead int, prevLength int, lookahead int) (length, offset int, ok bool) { + minMatchLook := maxMatchLength + if lookahead < minMatchLook { + minMatchLook = lookahead + } + + win := d.window[0 : pos+minMatchLook] + + // We quit when we get a match that's at least nice long + nice := len(win) - pos + if d.nice < nice { + nice = d.nice + } + + // If we've got a match that's good enough, only look in 1/4 the chain. + tries := d.chain + length = prevLength + if length >= d.good { + tries >>= 2 + } + + wEnd := win[pos+length] + wPos := win[pos:] + minIndex := pos - windowSize + + for i := prevHead; tries > 0; tries-- { + if wEnd == win[i+length] { + n := matchLenSSE4(win[i:], wPos, minMatchLook) + + if n > length && (n > minMatchLength || pos-i <= 4096) { + length = n + offset = pos - i + ok = true + if n >= nice { + // The match is good enough that we don't try to find a better one. + break + } + wEnd = win[pos+n] + } + } + if i == minIndex { + // hashPrev[i & windowMask] has already been overwritten, so stop now. + break + } + i = int(d.hashPrev[i&windowMask]) - d.hashOffset + if i < minIndex || i < 0 { + break + } + } + return +} + +func (d *compressor) writeStoredBlock(buf []byte) error { + if d.w.writeStoredHeader(len(buf), false); d.w.err != nil { + return d.w.err + } + d.w.writeBytes(buf) + return d.w.err +} + +// oldHash is the hash function used when no native crc32 calculation +// or similar is present. +func oldHash(b []byte) hash { + return hash(b[0])<<(hashShift*3) + hash(b[1])<<(hashShift*2) + hash(b[2])< d.windowEnd { + panic("index > windowEnd") + } + lookahead := d.windowEnd - d.index + if lookahead < minMatchLength+maxMatchLength { + if !d.sync { + return + } + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + if lookahead == 0 { + if d.tokens.n > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + return + } + } + if d.index < d.maxInsertIndex { + // Update the hash + d.hash = oldHash(d.window[d.index:d.index+minMatchLength]) & hashMask + ch := d.hashHead[d.hash] + d.chainHead = int(ch) + d.hashPrev[d.index&windowMask] = ch + d.hashHead[d.hash] = hashid(d.index + d.hashOffset) + } + d.length = minMatchLength - 1 + d.offset = 0 + minIndex := d.index - windowSize + if minIndex < 0 { + minIndex = 0 + } + + if d.chainHead-d.hashOffset >= minIndex && lookahead > minMatchLength-1 { + if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { + d.length = newLength + d.offset = newOffset + } + } + if d.length >= minMatchLength { + d.ii = 0 + // There was a match at the previous step, and the current match is + // not better. Output the previous match. + // "d.length-3" should NOT be "d.length-minMatchLength", since the format always assume 3 + d.tokens.tokens[d.tokens.n] = matchToken(uint32(d.length-3), uint32(d.offset-minOffsetSize)) + d.tokens.n++ + // Insert in the hash table all strings up to the end of the match. + // index and index-1 are already inserted. If there is not enough + // lookahead, the last two strings are not inserted into the hash + // table. + if d.length <= d.fastSkipHashing { + var newIndex int + newIndex = d.index + d.length + // Calculate missing hashes + end := newIndex + if end > d.maxInsertIndex { + end = d.maxInsertIndex + } + end += minMatchLength - 1 + startindex := d.index + 1 + if startindex > d.maxInsertIndex { + startindex = d.maxInsertIndex + } + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + if dstSize > 0 { + dst := d.hashMatch[:dstSize] + oldBulkHash(tocheck, dst) + var newH hash + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + d.hashPrev[di&windowMask] = d.hashHead[newH] + // Set the head of the hash chain to us. + d.hashHead[newH] = hashid(di + d.hashOffset) + } + d.hash = newH + } + d.index = newIndex + } else { + // For matches this long, we don't bother inserting each individual + // item into the table. + d.index += d.length + if d.index < d.maxInsertIndex { + d.hash = oldHash(d.window[d.index:d.index+minMatchLength]) & hashMask + } + } + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } else { + d.ii++ + end := d.index + int(d.ii>>uint(d.fastSkipHashing)) + 1 + if end > d.windowEnd { + end = d.windowEnd + } + for i := d.index; i < end; i++ { + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[i])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, i+1, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } + d.index = end + } + } +} + +// deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever, +// meaning it always has lazy matching on. +func (d *compressor) deflateLazy() { + // Sanity enables additional runtime tests. + // It's intended to be used during development + // to supplement the currently ad-hoc unit tests. + const sanity = false + + if d.windowEnd-d.index < minMatchLength+maxMatchLength && !d.sync { + return + } + + d.maxInsertIndex = d.windowEnd - (minMatchLength - 1) + if d.index < d.maxInsertIndex { + d.hash = oldHash(d.window[d.index:d.index+minMatchLength]) & hashMask + } + + for { + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + lookahead := d.windowEnd - d.index + if lookahead < minMatchLength+maxMatchLength { + if !d.sync { + return + } + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + if lookahead == 0 { + // Flush current output block if any. + if d.byteAvailable { + // There is still one pending token that needs to be flushed + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + d.byteAvailable = false + } + if d.tokens.n > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + return + } + } + if d.index < d.maxInsertIndex { + // Update the hash + d.hash = oldHash(d.window[d.index:d.index+minMatchLength]) & hashMask + ch := d.hashHead[d.hash] + d.chainHead = int(ch) + d.hashPrev[d.index&windowMask] = ch + d.hashHead[d.hash] = hashid(d.index + d.hashOffset) + } + prevLength := d.length + prevOffset := d.offset + d.length = minMatchLength - 1 + d.offset = 0 + minIndex := d.index - windowSize + if minIndex < 0 { + minIndex = 0 + } + + if d.chainHead-d.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy { + if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { + d.length = newLength + d.offset = newOffset + } + } + if prevLength >= minMatchLength && d.length <= prevLength { + // There was a match at the previous step, and the current match is + // not better. Output the previous match. + d.tokens.tokens[d.tokens.n] = matchToken(uint32(prevLength-3), uint32(prevOffset-minOffsetSize)) + d.tokens.n++ + + // Insert in the hash table all strings up to the end of the match. + // index and index-1 are already inserted. If there is not enough + // lookahead, the last two strings are not inserted into the hash + // table. + var newIndex int + newIndex = d.index + prevLength - 1 + // Calculate missing hashes + end := newIndex + if end > d.maxInsertIndex { + end = d.maxInsertIndex + } + end += minMatchLength - 1 + startindex := d.index + 1 + if startindex > d.maxInsertIndex { + startindex = d.maxInsertIndex + } + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + if dstSize > 0 { + dst := d.hashMatch[:dstSize] + oldBulkHash(tocheck, dst) + var newH hash + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + d.hashPrev[di&windowMask] = d.hashHead[newH] + // Set the head of the hash chain to us. + d.hashHead[newH] = hashid(di + d.hashOffset) + } + d.hash = newH + } + + d.index = newIndex + d.byteAvailable = false + d.length = minMatchLength - 1 + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } else { + // Reset, if we got a match this run. + if d.length >= minMatchLength { + d.ii = 0 + } + // We have a byte waiting. Emit it. + if d.byteAvailable { + d.ii++ + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + d.index++ + + // If we have a long run of no matches, skip additional bytes + // Resets when d.ii overflows after 64KB. + if d.ii > 31 { + n := int(d.ii >> 6) + for j := 0; j < n; j++ { + if d.index >= d.windowEnd-1 { + break + } + + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + d.index++ + } + // Flush last byte + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + d.byteAvailable = false + // d.length = minMatchLength - 1 // not needed, since d.ii is reset above, so it should never be > minMatchLength + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } + } else { + d.index++ + d.byteAvailable = true + } + } + } +} + +// Assumes that d.fastSkipHashing != skipNever, +// otherwise use deflateNoSkip +func (d *compressor) deflateSSE() { + + // Sanity enables additional runtime tests. + // It's intended to be used during development + // to supplement the currently ad-hoc unit tests. + const sanity = false + + if d.windowEnd-d.index < minMatchLength+maxMatchLength && !d.sync { + return + } + + d.maxInsertIndex = d.windowEnd - (minMatchLength - 1) + if d.index < d.maxInsertIndex { + d.hash = oldHash(d.window[d.index:d.index+minMatchLength]) & hashMask + } + + for { + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + lookahead := d.windowEnd - d.index + if lookahead < minMatchLength+maxMatchLength { + if !d.sync { + return + } + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + if lookahead == 0 { + if d.tokens.n > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + return + } + } + if d.index < d.maxInsertIndex { + // Update the hash + d.hash = crc32sse(d.window[d.index:d.index+minMatchLength]) & hashMask + ch := d.hashHead[d.hash] + d.chainHead = int(ch) + d.hashPrev[d.index&windowMask] = ch + d.hashHead[d.hash] = hashid(d.index + d.hashOffset) + } + d.length = minMatchLength - 1 + d.offset = 0 + minIndex := d.index - windowSize + if minIndex < 0 { + minIndex = 0 + } + + if d.chainHead-d.hashOffset >= minIndex && lookahead > minMatchLength-1 { + if newLength, newOffset, ok := d.findMatchSSE(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { + d.length = newLength + d.offset = newOffset + } + } + if d.length >= minMatchLength { + d.ii = 0 + // There was a match at the previous step, and the current match is + // not better. Output the previous match. + // "d.length-3" should NOT be "d.length-minMatchLength", since the format always assume 3 + d.tokens.tokens[d.tokens.n] = matchToken(uint32(d.length-3), uint32(d.offset-minOffsetSize)) + d.tokens.n++ + // Insert in the hash table all strings up to the end of the match. + // index and index-1 are already inserted. If there is not enough + // lookahead, the last two strings are not inserted into the hash + // table. + if d.length <= d.fastSkipHashing { + var newIndex int + newIndex = d.index + d.length + // Calculate missing hashes + end := newIndex + if end > d.maxInsertIndex { + end = d.maxInsertIndex + } + end += minMatchLength - 1 + startindex := d.index + 1 + if startindex > d.maxInsertIndex { + startindex = d.maxInsertIndex + } + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + if dstSize > 0 { + dst := d.hashMatch[:dstSize] + + crc32sseAll(tocheck, dst) + var newH hash + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + d.hashPrev[di&windowMask] = d.hashHead[newH] + // Set the head of the hash chain to us. + d.hashHead[newH] = hashid(di + d.hashOffset) + } + d.hash = newH + } + d.index = newIndex + } else { + // For matches this long, we don't bother inserting each individual + // item into the table. + d.index += d.length + if d.index < d.maxInsertIndex { + d.hash = crc32sse(d.window[d.index:d.index+minMatchLength]) & hashMask + } + } + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } else { + d.ii++ + end := d.index + int(d.ii>>uint(d.fastSkipHashing)) + 1 + if end > d.windowEnd { + end = d.windowEnd + } + for i := d.index; i < end; i++ { + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[i])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, i+1, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } + d.index = end + } + } +} + +// deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever, +// meaning it always has lazy matching on. +func (d *compressor) deflateLazySSE() { + // Sanity enables additional runtime tests. + // It's intended to be used during development + // to supplement the currently ad-hoc unit tests. + const sanity = false + + if d.windowEnd-d.index < minMatchLength+maxMatchLength && !d.sync { + return + } + + d.maxInsertIndex = d.windowEnd - (minMatchLength - 1) + if d.index < d.maxInsertIndex { + d.hash = crc32sse(d.window[d.index:d.index+minMatchLength]) & hashMask + } + + for { + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + lookahead := d.windowEnd - d.index + if lookahead < minMatchLength+maxMatchLength { + if !d.sync { + return + } + if sanity && d.index > d.windowEnd { + panic("index > windowEnd") + } + if lookahead == 0 { + // Flush current output block if any. + if d.byteAvailable { + // There is still one pending token that needs to be flushed + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + d.byteAvailable = false + } + if d.tokens.n > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + return + } + } + if d.index < d.maxInsertIndex { + // Update the hash + d.hash = crc32sse(d.window[d.index:d.index+minMatchLength]) & hashMask + ch := d.hashHead[d.hash] + d.chainHead = int(ch) + d.hashPrev[d.index&windowMask] = ch + d.hashHead[d.hash] = hashid(d.index + d.hashOffset) + } + prevLength := d.length + prevOffset := d.offset + d.length = minMatchLength - 1 + d.offset = 0 + minIndex := d.index - windowSize + if minIndex < 0 { + minIndex = 0 + } + + if d.chainHead-d.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy { + if newLength, newOffset, ok := d.findMatchSSE(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { + d.length = newLength + d.offset = newOffset + } + } + if prevLength >= minMatchLength && d.length <= prevLength { + // There was a match at the previous step, and the current match is + // not better. Output the previous match. + d.tokens.tokens[d.tokens.n] = matchToken(uint32(prevLength-3), uint32(prevOffset-minOffsetSize)) + d.tokens.n++ + + // Insert in the hash table all strings up to the end of the match. + // index and index-1 are already inserted. If there is not enough + // lookahead, the last two strings are not inserted into the hash + // table. + var newIndex int + newIndex = d.index + prevLength - 1 + // Calculate missing hashes + end := newIndex + if end > d.maxInsertIndex { + end = d.maxInsertIndex + } + end += minMatchLength - 1 + startindex := d.index + 1 + if startindex > d.maxInsertIndex { + startindex = d.maxInsertIndex + } + tocheck := d.window[startindex:end] + dstSize := len(tocheck) - minMatchLength + 1 + if dstSize > 0 { + dst := d.hashMatch[:dstSize] + crc32sseAll(tocheck, dst) + var newH hash + for i, val := range dst { + di := i + startindex + newH = val & hashMask + // Get previous value with the same hash. + // Our chain should point to the previous value. + d.hashPrev[di&windowMask] = d.hashHead[newH] + // Set the head of the hash chain to us. + d.hashHead[newH] = hashid(di + d.hashOffset) + } + d.hash = newH + } + + d.index = newIndex + d.byteAvailable = false + d.length = minMatchLength - 1 + if d.tokens.n == maxFlateBlockTokens { + // The block includes the current character + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } else { + // Reset, if we got a match this run. + if d.length >= minMatchLength { + d.ii = 0 + } + // We have a byte waiting. Emit it. + if d.byteAvailable { + d.ii++ + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + d.index++ + + // If we have a long run of no matches, skip additional bytes + // Resets when d.ii overflows after 64KB. + if d.ii > 31 { + n := int(d.ii >> 6) + for j := 0; j < n; j++ { + if d.index >= d.windowEnd-1 { + break + } + + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + d.index++ + } + // Flush last byte + d.tokens.tokens[d.tokens.n] = literalToken(uint32(d.window[d.index-1])) + d.tokens.n++ + d.byteAvailable = false + // d.length = minMatchLength - 1 // not needed, since d.ii is reset above, so it should never be > minMatchLength + if d.tokens.n == maxFlateBlockTokens { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { + return + } + d.tokens.n = 0 + } + } + } else { + d.index++ + d.byteAvailable = true + } + } + } +} + +func (d *compressor) fillStore(b []byte) int { + n := copy(d.window[d.windowEnd:], b) + d.windowEnd += n + return n +} + +func (d *compressor) store() { + if d.windowEnd > 0 { + d.err = d.writeStoredBlock(d.window[:d.windowEnd]) + } + d.windowEnd = 0 +} + +// fillHuff will fill the buffer with data for huffman-only compression. +// The number of bytes copied is returned. +func (d *compressor) fillHuff(b []byte) int { + n := copy(d.window[d.windowEnd:], b) + d.windowEnd += n + return n +} + +// storeHuff will compress and store the currently added data, +// if enough has been accumulated or we at the end of the stream. +// Any error that occurred will be in d.err +func (d *compressor) storeHuff() { + // We only compress if we have maxStoreBlockSize or we are at end-of-stream + if d.windowEnd < maxStoreBlockSize && !d.sync { + return + } + if d.windowEnd == 0 { + return + } + d.w.writeBlockHuff(false, d.window[:d.windowEnd]) + d.err = d.w.err + d.windowEnd = 0 +} + +// storeHuff will compress and store the currently added data, +// if enough has been accumulated or we at the end of the stream. +// Any error that occurred will be in d.err +func (d *compressor) storeSnappy() { + // We only compress if we have maxStoreBlockSize. + if d.windowEnd < maxStoreBlockSize && !d.sync { + return + } + if d.windowEnd == 0 { + return + } + snappyEncode(&d.tokens, d.window[:d.windowEnd]) + d.w.writeBlock(d.tokens, false, d.window[:d.windowEnd]) + d.err = d.w.err + d.tokens.n = 0 + d.windowEnd = 0 +} + +// write will add input byte to the stream. +// Unless an error occurs all bytes will be consumed. +func (d *compressor) write(b []byte) (n int, err error) { + if d.err != nil { + return 0, d.err + } + n = len(b) + for len(b) > 0 { + d.step(d) + b = b[d.fill(d, b):] + if d.err != nil { + return 0, d.err + } + } + return n, d.err +} + +func (d *compressor) syncFlush() error { + d.sync = true + if d.err != nil { + return d.err + } + d.step(d) + if d.err == nil { + d.w.writeStoredHeader(0, false) + d.w.flush() + d.err = d.w.err + } + d.sync = false + return d.err +} + +func (d *compressor) init(w io.Writer, level int) (err error) { + d.w = newHuffmanBitWriter(w) + + switch { + case level == NoCompression: + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillStore + d.step = (*compressor).store + case level == ConstantCompression: + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillHuff + d.step = (*compressor).storeHuff + case level == 1: + d.window = make([]byte, maxStoreBlockSize) + d.fill = (*compressor).fillHuff + d.step = (*compressor).storeSnappy + d.tokens.tokens = make([]token, maxStoreBlockSize+1) + case level == DefaultCompression: + level = 6 + fallthrough + case 2 <= level && level <= 9: + d.compressionLevel = levels[level] + d.initDeflate() + d.fill = (*compressor).fillDeflate + if d.fastSkipHashing == skipNever { + if useSSE42 { + d.step = (*compressor).deflateLazySSE + } else { + d.step = (*compressor).deflateLazy + } + } else { + if useSSE42 { + d.step = (*compressor).deflateSSE + } else { + d.step = (*compressor).deflate + + } + } + default: + return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level) + } + return nil +} + +// Used for zeroing the hash slice +var hzeroes [256]hashid + +// reset the state of the compressor. +func (d *compressor) reset(w io.Writer) { + d.w.reset(w) + d.sync = false + d.err = nil + switch d.compressionLevel.chain { + case 0: + // level was NoCompression or ConstantCompresssion. + d.windowEnd = 0 + default: + d.chainHead = -1 + for s := d.hashHead; len(s) > 0; { + n := copy(s, hzeroes[:]) + s = s[n:] + } + for s := d.hashPrev; len(s) > 0; s = s[len(hzeroes):] { + copy(s, hzeroes[:]) + } + d.hashOffset = 1 + + d.index, d.windowEnd = 0, 0 + d.blockStart, d.byteAvailable = 0, false + + d.tokens.n = 0 + d.length = minMatchLength - 1 + d.offset = 0 + d.hash = 0 + d.ii = 0 + d.maxInsertIndex = 0 + } +} + +func (d *compressor) close() error { + if d.err != nil { + return d.err + } + d.sync = true + d.step(d) + if d.err != nil { + return d.err + } + if d.w.writeStoredHeader(0, true); d.w.err != nil { + return d.w.err + } + d.w.flush() + return d.w.err +} + +// NewWriter returns a new Writer compressing data at the given level. +// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression); +// higher levels typically run slower but compress more. Level 0 +// (NoCompression) does not attempt any compression; it only adds the +// necessary DEFLATE framing. Level -1 (DefaultCompression) uses the default +// compression level. +// Level -2 (ConstantCompression) will use Huffman compression only, giving +// a very fast compression for all types of input, but sacrificing considerable +// compression efficiency. +// +// If level is in the range [-2, 9] then the error returned will be nil. +// Otherwise the error returned will be non-nil. +func NewWriter(w io.Writer, level int) (*Writer, error) { + var dw Writer + if err := dw.d.init(w, level); err != nil { + return nil, err + } + return &dw, nil +} + +// NewWriterDict is like NewWriter but initializes the new +// Writer with a preset dictionary. The returned Writer behaves +// as if the dictionary had been written to it without producing +// any compressed output. The compressed data written to w +// can only be decompressed by a Reader initialized with the +// same dictionary. +func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { + dw := &dictWriter{w} + zw, err := NewWriter(dw, level) + if err != nil { + return nil, err + } + zw.d.fillWindow(dict) + zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method. + return zw, err +} + +type dictWriter struct { + w io.Writer +} + +func (w *dictWriter) Write(b []byte) (n int, err error) { + return w.w.Write(b) +} + +// A Writer takes data written to it and writes the compressed +// form of that data to an underlying writer (see NewWriter). +type Writer struct { + d compressor + dict []byte +} + +// Write writes data to w, which will eventually write the +// compressed form of data to its underlying writer. +func (w *Writer) Write(data []byte) (n int, err error) { + return w.d.write(data) +} + +// Flush flushes any pending compressed data to the underlying writer. +// It is useful mainly in compressed network protocols, to ensure that +// a remote reader has enough data to reconstruct a packet. +// Flush does not return until the data has been written. +// If the underlying writer returns an error, Flush returns that error. +// +// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. +func (w *Writer) Flush() error { + // For more about flushing: + // http://www.bolet.org/~pornin/deflate-flush.html + return w.d.syncFlush() +} + +// Close flushes and closes the writer. +func (w *Writer) Close() error { + return w.d.close() +} + +// Reset discards the writer's state and makes it equivalent to +// the result of NewWriter or NewWriterDict called with dst +// and w's level and dictionary. +func (w *Writer) Reset(dst io.Writer) { + if dw, ok := w.d.w.w.(*dictWriter); ok { + // w was created with NewWriterDict + dw.w = dst + w.d.reset(dw) + w.d.fillWindow(w.dict) + } else { + // w was created with NewWriter + w.d.reset(dst) + } +} + +// ResetDict discards the writer's state and makes it equivalent to +// the result of NewWriter or NewWriterDict called with dst +// and w's level, but sets a specific dictionary. +func (w *Writer) ResetDict(dst io.Writer, dict []byte) { + w.dict = dict + w.d.reset(dst) + w.d.fillWindow(w.dict) +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/fixedhuff.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/fixedhuff.go new file mode 100644 index 00000000000..7df8b9a293f --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/fixedhuff.go @@ -0,0 +1,78 @@ +// Copyright 2013 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. + +package flate + +// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT + +var fixedHuffmanDecoder = huffmanDecoder{ + 7, + [huffmanNumChunks]uint32{ + 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c09, + 0x1087, 0x0608, 0x0208, 0x0a09, 0x0008, 0x0808, 0x0408, 0x0e09, + 0x1047, 0x0588, 0x0188, 0x0909, 0x1147, 0x0788, 0x0388, 0x0d09, + 0x10c7, 0x0688, 0x0288, 0x0b09, 0x0088, 0x0888, 0x0488, 0x0f09, + 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c89, + 0x10a7, 0x0648, 0x0248, 0x0a89, 0x0048, 0x0848, 0x0448, 0x0e89, + 0x1067, 0x05c8, 0x01c8, 0x0989, 0x1167, 0x07c8, 0x03c8, 0x0d89, + 0x10e7, 0x06c8, 0x02c8, 0x0b89, 0x00c8, 0x08c8, 0x04c8, 0x0f89, + 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c49, + 0x1097, 0x0628, 0x0228, 0x0a49, 0x0028, 0x0828, 0x0428, 0x0e49, + 0x1057, 0x05a8, 0x01a8, 0x0949, 0x1157, 0x07a8, 0x03a8, 0x0d49, + 0x10d7, 0x06a8, 0x02a8, 0x0b49, 0x00a8, 0x08a8, 0x04a8, 0x0f49, + 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cc9, + 0x10b7, 0x0668, 0x0268, 0x0ac9, 0x0068, 0x0868, 0x0468, 0x0ec9, + 0x1077, 0x05e8, 0x01e8, 0x09c9, 0x1177, 0x07e8, 0x03e8, 0x0dc9, + 0x10f7, 0x06e8, 0x02e8, 0x0bc9, 0x00e8, 0x08e8, 0x04e8, 0x0fc9, + 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c29, + 0x1087, 0x0618, 0x0218, 0x0a29, 0x0018, 0x0818, 0x0418, 0x0e29, + 0x1047, 0x0598, 0x0198, 0x0929, 0x1147, 0x0798, 0x0398, 0x0d29, + 0x10c7, 0x0698, 0x0298, 0x0b29, 0x0098, 0x0898, 0x0498, 0x0f29, + 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0ca9, + 0x10a7, 0x0658, 0x0258, 0x0aa9, 0x0058, 0x0858, 0x0458, 0x0ea9, + 0x1067, 0x05d8, 0x01d8, 0x09a9, 0x1167, 0x07d8, 0x03d8, 0x0da9, + 0x10e7, 0x06d8, 0x02d8, 0x0ba9, 0x00d8, 0x08d8, 0x04d8, 0x0fa9, + 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c69, + 0x1097, 0x0638, 0x0238, 0x0a69, 0x0038, 0x0838, 0x0438, 0x0e69, + 0x1057, 0x05b8, 0x01b8, 0x0969, 0x1157, 0x07b8, 0x03b8, 0x0d69, + 0x10d7, 0x06b8, 0x02b8, 0x0b69, 0x00b8, 0x08b8, 0x04b8, 0x0f69, + 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0ce9, + 0x10b7, 0x0678, 0x0278, 0x0ae9, 0x0078, 0x0878, 0x0478, 0x0ee9, + 0x1077, 0x05f8, 0x01f8, 0x09e9, 0x1177, 0x07f8, 0x03f8, 0x0de9, + 0x10f7, 0x06f8, 0x02f8, 0x0be9, 0x00f8, 0x08f8, 0x04f8, 0x0fe9, + 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c19, + 0x1087, 0x0608, 0x0208, 0x0a19, 0x0008, 0x0808, 0x0408, 0x0e19, + 0x1047, 0x0588, 0x0188, 0x0919, 0x1147, 0x0788, 0x0388, 0x0d19, + 0x10c7, 0x0688, 0x0288, 0x0b19, 0x0088, 0x0888, 0x0488, 0x0f19, + 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c99, + 0x10a7, 0x0648, 0x0248, 0x0a99, 0x0048, 0x0848, 0x0448, 0x0e99, + 0x1067, 0x05c8, 0x01c8, 0x0999, 0x1167, 0x07c8, 0x03c8, 0x0d99, + 0x10e7, 0x06c8, 0x02c8, 0x0b99, 0x00c8, 0x08c8, 0x04c8, 0x0f99, + 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c59, + 0x1097, 0x0628, 0x0228, 0x0a59, 0x0028, 0x0828, 0x0428, 0x0e59, + 0x1057, 0x05a8, 0x01a8, 0x0959, 0x1157, 0x07a8, 0x03a8, 0x0d59, + 0x10d7, 0x06a8, 0x02a8, 0x0b59, 0x00a8, 0x08a8, 0x04a8, 0x0f59, + 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cd9, + 0x10b7, 0x0668, 0x0268, 0x0ad9, 0x0068, 0x0868, 0x0468, 0x0ed9, + 0x1077, 0x05e8, 0x01e8, 0x09d9, 0x1177, 0x07e8, 0x03e8, 0x0dd9, + 0x10f7, 0x06e8, 0x02e8, 0x0bd9, 0x00e8, 0x08e8, 0x04e8, 0x0fd9, + 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c39, + 0x1087, 0x0618, 0x0218, 0x0a39, 0x0018, 0x0818, 0x0418, 0x0e39, + 0x1047, 0x0598, 0x0198, 0x0939, 0x1147, 0x0798, 0x0398, 0x0d39, + 0x10c7, 0x0698, 0x0298, 0x0b39, 0x0098, 0x0898, 0x0498, 0x0f39, + 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0cb9, + 0x10a7, 0x0658, 0x0258, 0x0ab9, 0x0058, 0x0858, 0x0458, 0x0eb9, + 0x1067, 0x05d8, 0x01d8, 0x09b9, 0x1167, 0x07d8, 0x03d8, 0x0db9, + 0x10e7, 0x06d8, 0x02d8, 0x0bb9, 0x00d8, 0x08d8, 0x04d8, 0x0fb9, + 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c79, + 0x1097, 0x0638, 0x0238, 0x0a79, 0x0038, 0x0838, 0x0438, 0x0e79, + 0x1057, 0x05b8, 0x01b8, 0x0979, 0x1157, 0x07b8, 0x03b8, 0x0d79, + 0x10d7, 0x06b8, 0x02b8, 0x0b79, 0x00b8, 0x08b8, 0x04b8, 0x0f79, + 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0cf9, + 0x10b7, 0x0678, 0x0278, 0x0af9, 0x0078, 0x0878, 0x0478, 0x0ef9, + 0x1077, 0x05f8, 0x01f8, 0x09f9, 0x1177, 0x07f8, 0x03f8, 0x0df9, + 0x10f7, 0x06f8, 0x02f8, 0x0bf9, 0x00f8, 0x08f8, 0x04f8, 0x0ff9, + }, + nil, 0, +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/gen.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/gen.go new file mode 100644 index 00000000000..154c89a488e --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/gen.go @@ -0,0 +1,265 @@ +// Copyright 2012 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. + +// +build ignore + +// This program generates fixedhuff.go +// Invoke as +// +// go run gen.go -output fixedhuff.go + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "io/ioutil" + "log" +) + +var filename = flag.String("output", "fixedhuff.go", "output file name") + +const maxCodeLen = 16 + +// Note: the definition of the huffmanDecoder struct is copied from +// inflate.go, as it is private to the implementation. + +// chunk & 15 is number of bits +// chunk >> 4 is value, including table link + +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) + +type huffmanDecoder struct { + min int // the minimum code length + chunks [huffmanNumChunks]uint32 // chunks as described above + links [][]uint32 // overflow links + linkMask uint32 // mask the width of the link table +} + +// Initialize Huffman decoding tables from array of code lengths. +// Following this function, h is guaranteed to be initialized into a complete +// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a +// degenerate case where the tree has only a single symbol with length 1. Empty +// trees are permitted. +func (h *huffmanDecoder) init(bits []int) bool { + // Sanity enables additional runtime tests during Huffman + // table construction. It's intended to be used during + // development to supplement the currently ad-hoc unit tests. + const sanity = false + + if h.min != 0 { + *h = huffmanDecoder{} + } + + // Count number of codes of each length, + // compute min and max length. + var count [maxCodeLen]int + var min, max int + for _, n := range bits { + if n == 0 { + continue + } + if min == 0 || n < min { + min = n + } + if n > max { + max = n + } + count[n]++ + } + + // Empty tree. The decompressor.huffSym function will fail later if the tree + // is used. Technically, an empty tree is only valid for the HDIST tree and + // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree + // is guaranteed to fail since it will attempt to use the tree to decode the + // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is + // guaranteed to fail later since the compressed data section must be + // composed of at least one symbol (the end-of-block marker). + if max == 0 { + return true + } + + code := 0 + var nextcode [maxCodeLen]int + for i := min; i <= max; i++ { + code <<= 1 + nextcode[i] = code + code += count[i] + } + + // Check that the coding is complete (i.e., that we've + // assigned all 2-to-the-max possible bit sequences). + // Exception: To be compatible with zlib, we also need to + // accept degenerate single-code codings. See also + // TestDegenerateHuffmanCoding. + if code != 1< huffmanChunkBits { + numLinks := 1 << (uint(max) - huffmanChunkBits) + h.linkMask = uint32(numLinks - 1) + + // create link tables + link := nextcode[huffmanChunkBits+1] >> 1 + h.links = make([][]uint32, huffmanNumChunks-link) + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + if sanity && h.chunks[reverse] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[reverse] = uint32(off<>8]) | int(reverseByte[code&0xff])<<8 + reverse >>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < len(h.chunks); off += 1 << uint(n) { + // We should never need to overwrite + // an existing chunk. Also, 0 is + // never a valid chunk, because the + // lower 4 "count" bits should be + // between 1 and 15. + if sanity && h.chunks[off] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[off] = chunk + } + } else { + j := reverse & (huffmanNumChunks - 1) + if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { + // Longer codes should have been + // associated with a link table above. + panic("impossible: not an indirect chunk") + } + value := h.chunks[j] >> huffmanValueShift + linktab := h.links[value] + reverse >>= huffmanChunkBits + for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { + if sanity && linktab[off] != 0 { + panic("impossible: overwriting existing chunk") + } + linktab[off] = chunk + } + } + } + + if sanity { + // Above we've sanity checked that we never overwrote + // an existing entry. Here we additionally check that + // we filled the tables completely. + for i, chunk := range h.chunks { + if chunk == 0 { + // As an exception, in the degenerate + // single-code case, we allow odd + // chunks to be missing. + if code == 1 && i%2 == 1 { + continue + } + panic("impossible: missing chunk") + } + } + for _, linktab := range h.links { + for _, chunk := range linktab { + if chunk == 0 { + panic("impossible: missing chunk") + } + } + } + } + + return true +} + +func main() { + flag.Parse() + + var h huffmanDecoder + var bits [288]int + initReverseByte() + for i := 0; i < 144; i++ { + bits[i] = 8 + } + for i := 144; i < 256; i++ { + bits[i] = 9 + } + for i := 256; i < 280; i++ { + bits[i] = 7 + } + for i := 280; i < 288; i++ { + bits[i] = 8 + } + h.init(bits[:]) + if h.links != nil { + log.Fatal("Unexpected links table in fixed Huffman decoder") + } + + var buf bytes.Buffer + + fmt.Fprintf(&buf, `// Copyright 2013 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.`+"\n\n") + + fmt.Fprintln(&buf, "package flate") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, "// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, "var fixedHuffmanDecoder = huffmanDecoder{") + fmt.Fprintf(&buf, "\t%d,\n", h.min) + fmt.Fprintln(&buf, "\t[huffmanNumChunks]uint32{") + for i := 0; i < huffmanNumChunks; i++ { + if i&7 == 0 { + fmt.Fprintf(&buf, "\t\t") + } else { + fmt.Fprintf(&buf, " ") + } + fmt.Fprintf(&buf, "0x%04x,", h.chunks[i]) + if i&7 == 7 { + fmt.Fprintln(&buf) + } + } + fmt.Fprintln(&buf, "\t},") + fmt.Fprintln(&buf, "\tnil, 0,") + fmt.Fprintln(&buf, "}") + + data, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile(*filename, data, 0644) + if err != nil { + log.Fatal(err) + } +} + +var reverseByte [256]byte + +func initReverseByte() { + for x := 0; x < 256; x++ { + var result byte + for i := uint(0); i < 8; i++ { + result |= byte(((x >> i) & 1) << (7 - i)) + } + reverseByte[x] = result + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_bit_writer.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_bit_writer.go new file mode 100644 index 00000000000..56985ef000b --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_bit_writer.go @@ -0,0 +1,690 @@ +// 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. + +package flate + +import ( + "io" + "math" +) + +const ( + // The largest offset code. + offsetCodeCount = 30 + + // The special code used to mark the end of a block. + endBlockMarker = 256 + + // The first length code. + lengthCodesStart = 257 + + // The number of codegen codes. + codegenCodeCount = 19 + badCode = 255 + + // Output byte buffer size + // Must be multiple of 6 (48 bits) + 8 + bufferSize = 240 + 8 +) + +// The number of extra bits needed by length code X - LENGTH_CODES_START. +var lengthExtraBits = []int8{ + /* 257 */ 0, 0, 0, + /* 260 */ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, + /* 270 */ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + /* 280 */ 4, 5, 5, 5, 5, 0, +} + +// The length indicated by length code X - LENGTH_CODES_START. +var lengthBase = []uint32{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, + 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 255, +} + +// offset code word extra bits. +var offsetExtraBits = []int8{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + /* extended window */ + 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, +} + +var offsetBase = []uint32{ + /* normal deflate */ + 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, + 0x000006, 0x000008, 0x00000c, 0x000010, 0x000018, + 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, + 0x0000c0, 0x000100, 0x000180, 0x000200, 0x000300, + 0x000400, 0x000600, 0x000800, 0x000c00, 0x001000, + 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, + + /* extended window */ + 0x008000, 0x00c000, 0x010000, 0x018000, 0x020000, + 0x030000, 0x040000, 0x060000, 0x080000, 0x0c0000, + 0x100000, 0x180000, 0x200000, 0x300000, +} + +// The odd order in which the codegen code sizes are written. +var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + +type huffmanBitWriter struct { + w io.Writer + // Data waiting to be written is bytes[0:nbytes] + // and then the low nbits of bits. + bits uint64 + nbits uint + bytes [bufferSize]byte + nbytes int + literalFreq []int32 + offsetFreq []int32 + codegen []uint8 + codegenFreq []int32 + literalEncoding *huffmanEncoder + offsetEncoding *huffmanEncoder + codegenEncoding *huffmanEncoder + err error +} + +func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { + return &huffmanBitWriter{ + w: w, + literalFreq: make([]int32, maxNumLit), + offsetFreq: make([]int32, offsetCodeCount), + codegen: make([]uint8, maxNumLit+offsetCodeCount+1), + codegenFreq: make([]int32, codegenCodeCount), + literalEncoding: newHuffmanEncoder(maxNumLit), + offsetEncoding: newHuffmanEncoder(offsetCodeCount), + codegenEncoding: newHuffmanEncoder(codegenCodeCount), + } +} + +func (w *huffmanBitWriter) reset(writer io.Writer) { + w.w = writer + w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil + w.bytes = [bufferSize]byte{} + for i := range w.codegen { + w.codegen[i] = 0 + } + for _, s := range [...][]int32{w.literalFreq, w.offsetFreq, w.codegenFreq} { + for i := range s { + s[i] = 0 + } + } + encs := []*huffmanEncoder{w.literalEncoding, w.codegenEncoding} + // Don't reset, if we are huffman only mode + if w.offsetEncoding != huffOffset { + encs = append(encs, w.offsetEncoding) + } + for _, enc := range encs { + for i := range enc.codes { + enc.codes[i] = 0 + } + } +} + +/* Inlined in writeBits +func (w *huffmanBitWriter) flushBits() { + if w.err != nil { + w.nbits = 0 + return + } + bits := w.bits + w.bits >>= 16 + w.nbits -= 16 + n := w.nbytes + w.bytes[n] = byte(bits) + w.bytes[n+1] = byte(bits >> 8) + if n += 2; n >= len(w.bytes) { + _, w.err = w.w.Write(w.bytes[0:]) + n = 0 + } + w.nbytes = n +} +*/ + +func (w *huffmanBitWriter) flush() { + if w.err != nil { + w.nbits = 0 + return + } + n := w.nbytes + for w.nbits != 0 { + w.bytes[n] = byte(w.bits) + w.bits >>= 8 + if w.nbits > 8 { // Avoid underflow + w.nbits -= 8 + } else { + w.nbits = 0 + } + n++ + } + w.bits = 0 + _, w.err = w.w.Write(w.bytes[0:n]) + w.nbytes = 0 +} + +func (w *huffmanBitWriter) writeBits(b int32, nb uint) { + w.bits |= uint64(b) << w.nbits + w.nbits += nb + if w.nbits >= 48 { + bits := w.bits + w.bits >>= 48 + w.nbits -= 48 + n := w.nbytes + w.bytes[n] = byte(bits) + w.bytes[n+1] = byte(bits >> 8) + w.bytes[n+2] = byte(bits >> 16) + w.bytes[n+3] = byte(bits >> 24) + w.bytes[n+4] = byte(bits >> 32) + w.bytes[n+5] = byte(bits >> 40) + n += 6 + if n >= bufferSize-8 { + _, w.err = w.w.Write(w.bytes[:bufferSize-8]) + n = 0 + } + w.nbytes = n + } +} + +func (w *huffmanBitWriter) writeBytes(bytes []byte) { + if w.err != nil { + return + } + n := w.nbytes + for w.nbits != 0 { + w.bytes[n] = byte(w.bits) + w.bits >>= 8 + w.nbits -= 8 + n++ + } + if w.nbits != 0 { + w.err = InternalError("writeBytes with unfinished bits") + return + } + if n != 0 { + _, w.err = w.w.Write(w.bytes[0:n]) + if w.err != nil { + return + } + } + w.nbytes = 0 + _, w.err = w.w.Write(bytes) +} + +// RFC 1951 3.2.7 specifies a special run-length encoding for specifying +// the literal and offset lengths arrays (which are concatenated into a single +// array). This method generates that run-length encoding. +// +// The result is written into the codegen array, and the frequencies +// of each code is written into the codegenFreq array. +// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional +// information. Code badCode is an end marker +// +// numLiterals The number of literals in literalEncoding +// numOffsets The number of offsets in offsetEncoding +func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { + for i := range w.codegenFreq { + w.codegenFreq[i] = 0 + } + // Note that we are using codegen both as a temporary variable for holding + // a copy of the frequencies, and as the place where we put the result. + // This is fine because the output is always shorter than the input used + // so far. + codegen := w.codegen // cache + // Copy the concatenated code sizes to codegen. Put a marker at the end. + //copy(codegen[0:numLiterals], w.literalEncoding.codeBits) + cgnl := codegen[0:numLiterals] + for i := range cgnl { + cgnl[i] = uint8(w.literalEncoding.codes[i].bits()) + } + + //copy(codegen[numLiterals:numLiterals+numOffsets], w.offsetEncoding.codeBits) + cgnl = codegen[numLiterals : numLiterals+numOffsets] + for i := range cgnl { + cgnl[i] = uint8(w.offsetEncoding.codes[i].bits()) + } + codegen[numLiterals+numOffsets] = badCode + + size := codegen[0] + count := 1 + outIndex := 0 + for inIndex := 1; size != badCode; inIndex++ { + // INVARIANT: We have seen "count" copies of size that have not yet + // had output generated for them. + nextSize := codegen[inIndex] + if nextSize == size { + count++ + continue + } + // We need to generate codegen indicating "count" of size. + if size != 0 { + codegen[outIndex] = size + outIndex++ + w.codegenFreq[size]++ + count-- + for count >= 3 { + n := 6 + if n > count { + n = count + } + codegen[outIndex] = 16 + outIndex++ + codegen[outIndex] = uint8(n - 3) + outIndex++ + w.codegenFreq[16]++ + count -= n + } + } else { + for count >= 11 { + n := 138 + if n > count { + n = count + } + codegen[outIndex] = 18 + outIndex++ + codegen[outIndex] = uint8(n - 11) + outIndex++ + w.codegenFreq[18]++ + count -= n + } + if count >= 3 { + // count >= 3 && count <= 10 + codegen[outIndex] = 17 + outIndex++ + codegen[outIndex] = uint8(count - 3) + outIndex++ + w.codegenFreq[17]++ + count = 0 + } + } + count-- + for ; count >= 0; count-- { + codegen[outIndex] = size + outIndex++ + w.codegenFreq[size]++ + } + // Set up invariant for next time through the loop. + size = nextSize + count = 1 + } + // Marker indicating the end of the codegen. + codegen[outIndex] = badCode +} + +/* non-inlined: +func (w *huffmanBitWriter) writeCode(code *huffmanEncoder, literal uint32) { + if w.err != nil { + return + } + c := code.codes[literal] + w.writeBits(int32(c.code()), int32(c.bits())) +} +*/ + +func (w *huffmanBitWriter) writeCode(code *huffmanEncoder, literal uint32) { + if w.err != nil { + return + } + c := code.codes[literal] + w.bits |= uint64(c.code()) << w.nbits + w.nbits += c.bits() + if w.nbits >= 48 { + bits := w.bits + w.bits >>= 48 + w.nbits -= 48 + n := w.nbytes + w.bytes[n] = byte(bits) + w.bytes[n+1] = byte(bits >> 8) + w.bytes[n+2] = byte(bits >> 16) + w.bytes[n+3] = byte(bits >> 24) + w.bytes[n+4] = byte(bits >> 32) + w.bytes[n+5] = byte(bits >> 40) + n += 6 + if n >= bufferSize-8 { + _, w.err = w.w.Write(w.bytes[:bufferSize-8]) + n = 0 + } + w.nbytes = n + } + +} + +// Write the header of a dynamic Huffman block to the output stream. +// +// numLiterals The number of literals specified in codegen +// numOffsets The number of offsets specified in codegen +// numCodegens The number of codegens used in codegen +func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, numCodegens int, isEof bool) { + if w.err != nil { + return + } + var firstBits int32 = 4 + if isEof { + firstBits = 5 + } + w.writeBits(firstBits, 3) + w.writeBits(int32(numLiterals-257), 5) + w.writeBits(int32(numOffsets-1), 5) + w.writeBits(int32(numCodegens-4), 4) + + for i := 0; i < numCodegens; i++ { + //value := w.codegenEncoding.codeBits[codegenOrder[i]] + value := w.codegenEncoding.codes[codegenOrder[i]].bits() + w.writeBits(int32(value), 3) + } + + i := 0 + for { + var codeWord int = int(w.codegen[i]) + i++ + if codeWord == badCode { + break + } + // The low byte contains the actual code to generate. + w.writeCode(w.codegenEncoding, uint32(codeWord)) + + switch codeWord { + case 16: + w.writeBits(int32(w.codegen[i]), 2) + i++ + break + case 17: + w.writeBits(int32(w.codegen[i]), 3) + i++ + break + case 18: + w.writeBits(int32(w.codegen[i]), 7) + i++ + break + } + } +} + +func (w *huffmanBitWriter) writeStoredHeader(length int, isEof bool) { + if w.err != nil { + return + } + var flag int32 + if isEof { + flag = 1 + } + w.writeBits(flag, 3) + w.flush() + w.writeBits(int32(length), 16) + w.writeBits(int32(^uint16(length)), 16) +} + +func (w *huffmanBitWriter) writeFixedHeader(isEof bool) { + if w.err != nil { + return + } + // Indicate that we are a fixed Huffman block + var value int32 = 2 + if isEof { + value = 3 + } + w.writeBits(value, 3) +} + +func (w *huffmanBitWriter) writeBlock(tok tokens, eof bool, input []byte) { + if w.err != nil { + return + } + copy(w.literalFreq, zeroLits[:]) + + for i := range w.offsetFreq { + w.offsetFreq[i] = 0 + } + + tok.tokens[tok.n] = endBlockMarker + tokens := tok.tokens[0 : tok.n+1] + + for _, t := range tokens { + switch t.typ() { + case literalType: + w.literalFreq[t.literal()]++ + case matchType: + length := t.length() + offset := t.offset() + w.literalFreq[lengthCodesStart+lengthCode(length)]++ + w.offsetFreq[offsetCode(offset)]++ + } + } + + // get the number of literals + numLiterals := len(w.literalFreq) + for w.literalFreq[numLiterals-1] == 0 { + numLiterals-- + } + // get the number of offsets + numOffsets := len(w.offsetFreq) + for numOffsets > 0 && w.offsetFreq[numOffsets-1] == 0 { + numOffsets-- + } + if numOffsets == 0 { + // We haven't found a single match. If we want to go with the dynamic encoding, + // we should count at least one offset to be sure that the offset huffman tree could be encoded. + w.offsetFreq[0] = 1 + numOffsets = 1 + } + + w.literalEncoding.generate(w.literalFreq, 15) + w.offsetEncoding.generate(w.offsetFreq, 15) + + storedBytes := 0 + if input != nil { + storedBytes = len(input) + } + var extraBits int64 + var storedSize int64 = math.MaxInt64 + if storedBytes <= maxStoreBlockSize && input != nil { + storedSize = int64((storedBytes + 5) * 8) + // We only bother calculating the costs of the extra bits required by + // the length of offset fields (which will be the same for both fixed + // and dynamic encoding), if we need to compare those two encodings + // against stored encoding. + for lengthCode := lengthCodesStart + 8; lengthCode < numLiterals; lengthCode++ { + // First eight length codes have extra size = 0. + extraBits += int64(w.literalFreq[lengthCode]) * int64(lengthExtraBits[lengthCode-lengthCodesStart]) + } + for offsetCode := 4; offsetCode < numOffsets; offsetCode++ { + // First four offset codes have extra size = 0. + extraBits += int64(w.offsetFreq[offsetCode]) * int64(offsetExtraBits[offsetCode]) + } + } + + // Figure out smallest code. + // Fixed Huffman baseline. + var size = int64(3) + + fixedLiteralEncoding.bitLength(w.literalFreq) + + fixedOffsetEncoding.bitLength(w.offsetFreq) + + extraBits + var literalEncoding = fixedLiteralEncoding + var offsetEncoding = fixedOffsetEncoding + + // Dynamic Huffman? + var numCodegens int + + // Generate codegen and codegenFrequencies, which indicates how to encode + // the literalEncoding and the offsetEncoding. + w.generateCodegen(numLiterals, numOffsets) + w.codegenEncoding.generate(w.codegenFreq, 7) + numCodegens = len(w.codegenFreq) + for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { + numCodegens-- + } + dynamicHeader := int64(3+5+5+4+(3*numCodegens)) + + w.codegenEncoding.bitLength(w.codegenFreq) + + int64(extraBits) + + int64(w.codegenFreq[16]*2) + + int64(w.codegenFreq[17]*3) + + int64(w.codegenFreq[18]*7) + dynamicSize := dynamicHeader + + w.literalEncoding.bitLength(w.literalFreq) + + w.offsetEncoding.bitLength(w.offsetFreq) + + if dynamicSize < size { + size = dynamicSize + literalEncoding = w.literalEncoding + offsetEncoding = w.offsetEncoding + } + + // Stored bytes? + if storedSize < size { + w.writeStoredHeader(storedBytes, eof) + w.writeBytes(input[0:storedBytes]) + return + } + + // Huffman. + if literalEncoding == fixedLiteralEncoding { + w.writeFixedHeader(eof) + } else { + w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) + } + for _, t := range tokens { + switch t.typ() { + case literalType: + w.writeCode(literalEncoding, t.literal()) + break + case matchType: + // Write the length + length := t.length() + lengthCode := lengthCode(length) + w.writeCode(literalEncoding, lengthCode+lengthCodesStart) + extraLengthBits := uint(lengthExtraBits[lengthCode]) + if extraLengthBits > 0 { + extraLength := int32(length - lengthBase[lengthCode]) + w.writeBits(extraLength, extraLengthBits) + } + // Write the offset + offset := t.offset() + offsetCode := offsetCode(offset) + w.writeCode(offsetEncoding, offsetCode) + extraOffsetBits := uint(offsetExtraBits[offsetCode]) + if extraOffsetBits > 0 { + extraOffset := int32(offset - offsetBase[offsetCode]) + w.writeBits(extraOffset, extraOffsetBits) + } + break + default: + panic("unknown token type: " + string(t)) + } + } +} + +var huffOffset *huffmanEncoder +var zeroLits [maxNumLit]int32 + +func init() { + var w = newHuffmanBitWriter(nil) + w.offsetFreq[0] = 1 + w.offsetEncoding = newHuffmanEncoder(offsetCodeCount) + w.offsetEncoding.generate(w.offsetFreq, 15) + huffOffset = w.offsetEncoding +} + +// writeBlockHuff will write a block of bytes as either +// Huffman encoded literals, or uncompressed bytes depending +// on what yields the smallest result. +func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { + if w.err != nil { + return + } + // Clear histogram + copy(w.literalFreq, zeroLits[:]) + + // Add everything as literals + histogram(input, w.literalFreq) + + w.literalFreq[endBlockMarker]++ + + // get the number of literals + numLiterals := len(w.literalFreq) + for w.literalFreq[numLiterals-1] == 0 { + numLiterals-- + } + + numOffsets := 1 + + w.literalEncoding.generate(w.literalFreq, 15) + w.offsetEncoding = huffOffset + + storedBytes := len(input) + + var extraBits int64 + var storedSize int64 = math.MaxInt64 + if storedBytes <= maxStoreBlockSize { + storedSize = int64((storedBytes + 5) * 8) + // We only bother calculating the costs of the extra bits required by + // the length of offset fields (which will be the same for both fixed + // and dynamic encoding), if we need to compare those two encodings + // against stored encoding. + for lengthCode := lengthCodesStart + 8; lengthCode < numLiterals; lengthCode++ { + // First eight length codes have extra size = 0. + extraBits += int64(w.literalFreq[lengthCode]) * int64(lengthExtraBits[lengthCode-lengthCodesStart]) + } + } + + // Figure out smallest code. + // Always use dynamic Huffman or Store + var numCodegens int + + // Generate codegen and codegenFrequencies, which indicates how to encode + // the literalEncoding and the offsetEncoding. + w.generateCodegen(numLiterals, numOffsets) + w.codegenEncoding.generate(w.codegenFreq, 7) + numCodegens = len(w.codegenFreq) + for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { + numCodegens-- + } + dynamicHeader := int64(3+5+5+4+(3*numCodegens)) + + w.codegenEncoding.bitLength(w.codegenFreq) + + int64(extraBits) + + int64(w.codegenFreq[16]*2) + + int64(w.codegenFreq[17]*3) + + int64(w.codegenFreq[18]*7) + size := dynamicHeader + + w.literalEncoding.bitLength(w.literalFreq) + + 1 /*w.offsetEncoding.bitLength(w.offsetFreq)*/ + + // Stored bytes? + if storedSize < size { + w.writeStoredHeader(storedBytes, eof) + w.writeBytes(input[0:storedBytes]) + return + } + + // Huffman. + w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) + for _, t := range input { + // Bitwriting inlined, ~30% speedup + c := w.literalEncoding.codes[t] + w.bits |= uint64(c.code()) << w.nbits + w.nbits += c.bits() + if w.nbits >= 48 { + bits := w.bits + w.bits >>= 48 + w.nbits -= 48 + n := w.nbytes + w.bytes[n] = byte(bits) + w.bytes[n+1] = byte(bits >> 8) + w.bytes[n+2] = byte(bits >> 16) + w.bytes[n+3] = byte(bits >> 24) + w.bytes[n+4] = byte(bits >> 32) + w.bytes[n+5] = byte(bits >> 40) + n += 6 + if n >= bufferSize-8 { + _, w.err = w.w.Write(w.bytes[:bufferSize-8]) + w.nbytes = 0 + } else { + w.nbytes = n + } + } + } + // Write EOB + w.writeCode(w.literalEncoding, endBlockMarker) +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_code.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_code.go new file mode 100644 index 00000000000..9dba0faf339 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/huffman_code.go @@ -0,0 +1,363 @@ +// 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. + +package flate + +import ( + "math" + "sort" +) + +type hcode uint32 + +type huffmanEncoder struct { + codes []hcode + freqcache []literalNode + bitCount [17]int32 + lns literalNodeSorter + lfs literalFreqSorter +} + +type literalNode struct { + literal uint16 + freq int32 +} + +// A levelInfo describes the state of the constructed tree for a given depth. +type levelInfo struct { + // Our level. for better printing + level int32 + + // The frequency of the last node at this level + lastFreq int32 + + // The frequency of the next character to add to this level + nextCharFreq int32 + + // The frequency of the next pair (from level below) to add to this level. + // Only valid if the "needed" value of the next lower level is 0. + nextPairFreq int32 + + // The number of chains remaining to generate for this level before moving + // up to the next level + needed int32 +} + +func (h hcode) codeBits() (code uint16, bits uint8) { + return uint16(h), uint8(h >> 16) +} + +func (h *hcode) set(code uint16, bits uint8) { + *h = hcode(code) | hcode(uint32(bits)<<16) +} + +func (h *hcode) setBits(bits uint8) { + *h = hcode(*h&0xffff) | hcode(uint32(bits)<<16) +} + +func toCode(code uint16, bits uint8) hcode { + return hcode(code) | hcode(uint32(bits)<<16) +} + +func (h hcode) code() (code uint16) { + return uint16(h) +} + +func (h hcode) bits() (bits uint) { + return uint(h >> 16) +} + +func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} } + +func newHuffmanEncoder(size int) *huffmanEncoder { + return &huffmanEncoder{codes: make([]hcode, size), freqcache: nil} +} + +// Generates a HuffmanCode corresponding to the fixed literal table +func generateFixedLiteralEncoding() *huffmanEncoder { + h := newHuffmanEncoder(maxNumLit) + codes := h.codes + var ch uint16 + for ch = 0; ch < maxNumLit; ch++ { + var bits uint16 + var size uint8 + switch { + case ch < 144: + // size 8, 000110000 .. 10111111 + bits = ch + 48 + size = 8 + break + case ch < 256: + // size 9, 110010000 .. 111111111 + bits = ch + 400 - 144 + size = 9 + break + case ch < 280: + // size 7, 0000000 .. 0010111 + bits = ch - 256 + size = 7 + break + default: + // size 8, 11000000 .. 11000111 + bits = ch + 192 - 280 + size = 8 + } + codes[ch] = toCode(reverseBits(bits, size), size) + } + return h +} + +func generateFixedOffsetEncoding() *huffmanEncoder { + h := newHuffmanEncoder(30) + codes := h.codes + for ch := uint16(0); ch < 30; ch++ { + codes[ch] = toCode(reverseBits(ch, 5), 5) + } + return h +} + +var fixedLiteralEncoding *huffmanEncoder = generateFixedLiteralEncoding() +var fixedOffsetEncoding *huffmanEncoder = generateFixedOffsetEncoding() + +func (h *huffmanEncoder) bitLength(freq []int32) int64 { + var total int64 + for i, f := range freq { + if f != 0 { + total += int64(f) * int64(h.codes[i].bits()) + } + } + return total +} + +const maxBitsLimit = 16 + +// Return the number of literals assigned to each bit size in the Huffman encoding +// +// This method is only called when list.length >= 3 +// The cases of 0, 1, and 2 literals are handled by special case code. +// +// list An array of the literals with non-zero frequencies +// and their associated frequencies. The array is in order of increasing +// frequency, and has as its last element a special element with frequency +// MaxInt32 +// maxBits The maximum number of bits that should be used to encode any literal. +// Must be less than 16. +// return An integer array in which array[i] indicates the number of literals +// that should be encoded in i bits. +func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { + if maxBits >= maxBitsLimit { + panic("flate: maxBits too large") + } + n := int32(len(list)) + list = list[0 : n+1] + list[n] = maxNode() + + // The tree can't have greater depth than n - 1, no matter what. This + // saves a little bit of work in some small cases + if maxBits > n-1 { + maxBits = n - 1 + } + + // Create information about each of the levels. + // A bogus "Level 0" whose sole purpose is so that + // level1.prev.needed==0. This makes level1.nextPairFreq + // be a legitimate value that never gets chosen. + var levels [maxBitsLimit]levelInfo + // leafCounts[i] counts the number of literals at the left + // of ancestors of the rightmost node at level i. + // leafCounts[i][j] is the number of literals at the left + // of the level j ancestor. + var leafCounts [maxBitsLimit][maxBitsLimit]int32 + + for level := int32(1); level <= maxBits; level++ { + // For every level, the first two items are the first two characters. + // We initialize the levels as if we had already figured this out. + levels[level] = levelInfo{ + level: level, + lastFreq: list[1].freq, + nextCharFreq: list[2].freq, + nextPairFreq: list[0].freq + list[1].freq, + } + leafCounts[level][level] = 2 + if level == 1 { + levels[level].nextPairFreq = math.MaxInt32 + } + } + + // We need a total of 2*n - 2 items at top level and have already generated 2. + levels[maxBits].needed = 2*n - 4 + + level := maxBits + for { + l := &levels[level] + if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { + // We've run out of both leafs and pairs. + // End all calculations for this level. + // To make sure we never come back to this level or any lower level, + // set nextPairFreq impossibly large. + l.needed = 0 + levels[level+1].nextPairFreq = math.MaxInt32 + level++ + continue + } + + prevFreq := l.lastFreq + if l.nextCharFreq < l.nextPairFreq { + // The next item on this row is a leaf node. + n := leafCounts[level][level] + 1 + l.lastFreq = l.nextCharFreq + // Lower leafCounts are the same of the previous node. + leafCounts[level][level] = n + l.nextCharFreq = list[n].freq + } else { + // The next item on this row is a pair from the previous row. + // nextPairFreq isn't valid until we generate two + // more values in the level below + l.lastFreq = l.nextPairFreq + // Take leaf counts from the lower level, except counts[level] remains the same. + copy(leafCounts[level][:level], leafCounts[level-1][:level]) + levels[l.level-1].needed = 2 + } + + if l.needed--; l.needed == 0 { + // We've done everything we need to do for this level. + // Continue calculating one level up. Fill in nextPairFreq + // of that level with the sum of the two nodes we've just calculated on + // this level. + if l.level == maxBits { + // All done! + break + } + levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq + level++ + } else { + // If we stole from below, move down temporarily to replenish it. + for levels[level-1].needed > 0 { + level-- + } + } + } + + // Somethings is wrong if at the end, the top level is null or hasn't used + // all of the leaves. + if leafCounts[maxBits][maxBits] != n { + panic("leafCounts[maxBits][maxBits] != n") + } + + bitCount := h.bitCount[:maxBits+1] + //make([]int32, maxBits+1) + bits := 1 + counts := &leafCounts[maxBits] + for level := maxBits; level > 0; level-- { + // chain.leafCount gives the number of literals requiring at least "bits" + // bits to encode. + bitCount[bits] = counts[level] - counts[level-1] + bits++ + } + return bitCount +} + +// Look at the leaves and assign them a bit count and an encoding as specified +// in RFC 1951 3.2.2 +func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalNode) { + code := uint16(0) + for n, bits := range bitCount { + code <<= 1 + if n == 0 || bits == 0 { + continue + } + // The literals list[len(list)-bits] .. list[len(list)-bits] + // are encoded using "bits" bits, and get the values + // code, code + 1, .... The code values are + // assigned in literal order (not frequency order). + chunk := list[len(list)-int(bits):] + + h.lns.Sort(chunk) + for _, node := range chunk { + h.codes[node.literal] = toCode(reverseBits(code, uint8(n)), uint8(n)) + code++ + } + list = list[0 : len(list)-int(bits)] + } +} + +// Update this Huffman Code object to be the minimum code for the specified frequency count. +// +// freq An array of frequencies, in which frequency[i] gives the frequency of literal i. +// maxBits The maximum number of bits to use for any literal. +func (h *huffmanEncoder) generate(freq []int32, maxBits int32) { + if h.freqcache == nil { + h.freqcache = make([]literalNode, 300) + } + list := h.freqcache[:len(freq)+1] + // Number of non-zero literals + count := 0 + // Set list to be the set of all non-zero literals and their frequencies + for i, f := range freq { + if f != 0 { + list[count] = literalNode{uint16(i), f} + count++ + } else { + list[count] = literalNode{} + //h.codeBits[i] = 0 + h.codes[i].setBits(0) + } + } + list[len(freq)] = literalNode{} + // If freq[] is shorter than codeBits[], fill rest of codeBits[] with zeros + // FIXME: Doesn't do what it says on the tin (klauspost) + //h.codeBits = h.codeBits[0:len(freq)] + + list = list[0:count] + if count <= 2 { + // Handle the small cases here, because they are awkward for the general case code. With + // two or fewer literals, everything has bit length 1. + for i, node := range list { + // "list" is in order of increasing literal value. + h.codes[node.literal].set(uint16(i), 1) + //h.codeBits[node.literal] = 1 + //h.code[node.literal] = uint16(i) + } + return + } + h.lfs.Sort(list) + + // Get the number of literals for each bit count + bitCount := h.bitCounts(list, maxBits) + // And do the assignment + h.assignEncodingAndSize(bitCount, list) +} + +type literalNodeSorter []literalNode + +func (s *literalNodeSorter) Sort(a []literalNode) { + *s = literalNodeSorter(a) + sort.Sort(s) +} + +func (s literalNodeSorter) Len() int { return len(s) } + +func (s literalNodeSorter) Less(i, j int) bool { + return s[i].literal < s[j].literal +} + +func (s literalNodeSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type literalFreqSorter []literalNode + +func (s *literalFreqSorter) Sort(a []literalNode) { + *s = literalFreqSorter(a) + sort.Sort(s) +} + +func (s literalFreqSorter) Len() int { return len(s) } + +func (s literalFreqSorter) Less(i, j int) bool { + if s[i].freq == s[j].freq { + return s[i].literal < s[j].literal + } + return s[i].freq < s[j].freq +} + +func (s literalFreqSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/inflate.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/inflate.go new file mode 100644 index 00000000000..91e27e7e280 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/inflate.go @@ -0,0 +1,846 @@ +// 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. + +//go:generate go run gen.go -output fixedhuff.go + +// Package flate implements the DEFLATE compressed data format, described in +// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file +// formats. +package flate + +import ( + "bufio" + "io" + "strconv" +) + +const ( + maxCodeLen = 16 // max length of Huffman code + maxHist = 32768 // max history required + // The next three numbers come from the RFC section 3.2.7, with the + // additional proviso in section 3.2.5 which implies that distance codes + // 30 and 31 should never occur in compressed data. + maxNumLit = 286 + maxNumDist = 30 + numCodes = 19 // number of codes in Huffman meta-code +) + +// A CorruptInputError reports the presence of corrupt input at a given offset. +type CorruptInputError int64 + +func (e CorruptInputError) Error() string { + return "flate: corrupt input before offset " + strconv.FormatInt(int64(e), 10) +} + +// An InternalError reports an error in the flate code itself. +type InternalError string + +func (e InternalError) Error() string { return "flate: internal error: " + string(e) } + +// A ReadError reports an error encountered while reading input. +type ReadError struct { + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Read +} + +func (e *ReadError) Error() string { + return "flate: read error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() +} + +// A WriteError reports an error encountered while writing output. +type WriteError struct { + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Write +} + +func (e *WriteError) Error() string { + return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() +} + +// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to +// to switch to a new underlying Reader. This permits reusing a ReadCloser +// instead of allocating a new one. +type Resetter interface { + // Reset discards any buffered data and resets the Resetter as if it was + // newly initialized with the given reader. + Reset(r io.Reader, dict []byte) error +} + +// Note that much of the implementation of huffmanDecoder is also copied +// into gen.go (in package main) for the purpose of precomputing the +// fixed huffman tables so they can be included statically. + +// The data structure for decoding Huffman tables is based on that of +// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits), +// For codes smaller than the table width, there are multiple entries +// (each combination of trailing bits has the same value). For codes +// larger than the table width, the table contains a link to an overflow +// table. The width of each entry in the link table is the maximum code +// size minus the chunk width. + +// Note that you can do a lookup in the table even without all bits +// filled. Since the extra bits are zero, and the DEFLATE Huffman codes +// have the property that shorter codes come before longer ones, the +// bit length estimate in the result is a lower bound on the actual +// number of bits. + +// chunk & 15 is number of bits +// chunk >> 4 is value, including table link + +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) + +type huffmanDecoder struct { + min int // the minimum code length + chunks [huffmanNumChunks]uint32 // chunks as described above + links [][]uint32 // overflow links + linkMask uint32 // mask the width of the link table +} + +// Initialize Huffman decoding tables from array of code lengths. +// Following this function, h is guaranteed to be initialized into a complete +// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a +// degenerate case where the tree has only a single symbol with length 1. Empty +// trees are permitted. +func (h *huffmanDecoder) init(bits []int) bool { + // Sanity enables additional runtime tests during Huffman + // table construction. It's intended to be used during + // development to supplement the currently ad-hoc unit tests. + const sanity = false + + if h.min != 0 { + *h = huffmanDecoder{} + } + + // Count number of codes of each length, + // compute min and max length. + var count [maxCodeLen]int + var min, max int + for _, n := range bits { + if n == 0 { + continue + } + if min == 0 || n < min { + min = n + } + if n > max { + max = n + } + count[n]++ + } + + // Empty tree. The decompressor.huffSym function will fail later if the tree + // is used. Technically, an empty tree is only valid for the HDIST tree and + // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree + // is guaranteed to fail since it will attempt to use the tree to decode the + // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is + // guaranteed to fail later since the compressed data section must be + // composed of at least one symbol (the end-of-block marker). + if max == 0 { + return true + } + + code := 0 + var nextcode [maxCodeLen]int + for i := min; i <= max; i++ { + code <<= 1 + nextcode[i] = code + code += count[i] + } + + // Check that the coding is complete (i.e., that we've + // assigned all 2-to-the-max possible bit sequences). + // Exception: To be compatible with zlib, we also need to + // accept degenerate single-code codings. See also + // TestDegenerateHuffmanCoding. + if code != 1< huffmanChunkBits { + numLinks := 1 << (uint(max) - huffmanChunkBits) + h.linkMask = uint32(numLinks - 1) + + // create link tables + link := nextcode[huffmanChunkBits+1] >> 1 + h.links = make([][]uint32, huffmanNumChunks-link) + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + if sanity && h.chunks[reverse] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[reverse] = uint32(off<>8]) | int(reverseByte[code&0xff])<<8 + reverse >>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < len(h.chunks); off += 1 << uint(n) { + // We should never need to overwrite + // an existing chunk. Also, 0 is + // never a valid chunk, because the + // lower 4 "count" bits should be + // between 1 and 15. + if sanity && h.chunks[off] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[off] = chunk + } + } else { + j := reverse & (huffmanNumChunks - 1) + if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { + // Longer codes should have been + // associated with a link table above. + panic("impossible: not an indirect chunk") + } + value := h.chunks[j] >> huffmanValueShift + linktab := h.links[value] + reverse >>= huffmanChunkBits + for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { + if sanity && linktab[off] != 0 { + panic("impossible: overwriting existing chunk") + } + linktab[off] = chunk + } + } + } + + if sanity { + // Above we've sanity checked that we never overwrote + // an existing entry. Here we additionally check that + // we filled the tables completely. + for i, chunk := range h.chunks { + if chunk == 0 { + // As an exception, in the degenerate + // single-code case, we allow odd + // chunks to be missing. + if code == 1 && i%2 == 1 { + continue + } + panic("impossible: missing chunk") + } + } + for _, linktab := range h.links { + for _, chunk := range linktab { + if chunk == 0 { + panic("impossible: missing chunk") + } + } + } + } + + return true +} + +// The actual read interface needed by NewReader. +// If the passed in io.Reader does not also have ReadByte, +// the NewReader will introduce its own buffering. +type Reader interface { + io.Reader + io.ByteReader +} + +// Decompress state. +type decompressor struct { + // Input source. + r Reader + roffset int64 + woffset int64 + + // Input bits, in top of b. + b uint32 + nb uint + + // Huffman decoders for literal/length, distance. + h1, h2 huffmanDecoder + + // Length arrays used to define Huffman codes. + bits *[maxNumLit + maxNumDist]int + codebits *[numCodes]int + + // Output history, buffer. + hist *[maxHist]byte + hp int // current output position in buffer + hw int // have written hist[0:hw] already + hfull bool // buffer has filled at least once + + // Temporary buffer (avoids repeated allocation). + buf [4]byte + + // Next step in the decompression, + // and decompression state. + step func(*decompressor) + final bool + err error + toRead []byte + hl, hd *huffmanDecoder + copyLen int + copyDist int +} + +func (f *decompressor) nextBlock() { + if f.final { + if f.hw != f.hp { + f.flush((*decompressor).nextBlock) + return + } + f.err = io.EOF + return + } + for f.nb < 1+2 { + if f.err = f.moreBits(); f.err != nil { + return + } + } + f.final = f.b&1 == 1 + f.b >>= 1 + typ := f.b & 3 + f.b >>= 2 + f.nb -= 1 + 2 + switch typ { + case 0: + f.dataBlock() + case 1: + // compressed, fixed Huffman tables + f.hl = &fixedHuffmanDecoder + f.hd = nil + f.huffmanBlock() + case 2: + // compressed, dynamic Huffman tables + if f.err = f.readHuffman(); f.err != nil { + break + } + f.hl = &f.h1 + f.hd = &f.h2 + f.huffmanBlock() + default: + // 3 is reserved. + f.err = CorruptInputError(f.roffset) + } +} + +func (f *decompressor) Read(b []byte) (int, error) { + for { + if len(f.toRead) > 0 { + n := copy(b, f.toRead) + f.toRead = f.toRead[n:] + return n, nil + } + if f.err != nil { + return 0, f.err + } + f.step(f) + } +} + +// Support the io.WriteTo interface for io.Copy and friends. +func (f *decompressor) WriteTo(w io.Writer) (int64, error) { + total := int64(0) + for { + if f.err != nil { + if f.err == io.EOF { + return total, nil + } + return total, f.err + } + if len(f.toRead) > 0 { + var n int + n, f.err = w.Write(f.toRead) + if f.err != nil { + return total, f.err + } + if n != len(f.toRead) { + return total, io.ErrShortWrite + } + f.toRead = f.toRead[:0] + total += int64(n) + } + f.step(f) + } +} + +func (f *decompressor) Close() error { + if f.err == io.EOF { + return nil + } + return f.err +} + +// RFC 1951 section 3.2.7. +// Compression with dynamic Huffman codes + +var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + +func (f *decompressor) readHuffman() error { + // HLIT[5], HDIST[5], HCLEN[4]. + for f.nb < 5+5+4 { + if err := f.moreBits(); err != nil { + return err + } + } + nlit := int(f.b&0x1F) + 257 + if nlit > maxNumLit { + return CorruptInputError(f.roffset) + } + f.b >>= 5 + ndist := int(f.b&0x1F) + 1 + if ndist > maxNumDist { + return CorruptInputError(f.roffset) + } + f.b >>= 5 + nclen := int(f.b&0xF) + 4 + // numCodes is 19, so nclen is always valid. + f.b >>= 4 + f.nb -= 5 + 5 + 4 + + // (HCLEN+4)*3 bits: code lengths in the magic codeOrder order. + for i := 0; i < nclen; i++ { + for f.nb < 3 { + if err := f.moreBits(); err != nil { + return err + } + } + f.codebits[codeOrder[i]] = int(f.b & 0x7) + f.b >>= 3 + f.nb -= 3 + } + for i := nclen; i < len(codeOrder); i++ { + f.codebits[codeOrder[i]] = 0 + } + if !f.h1.init(f.codebits[0:]) { + return CorruptInputError(f.roffset) + } + + // HLIT + 257 code lengths, HDIST + 1 code lengths, + // using the code length Huffman code. + for i, n := 0, nlit+ndist; i < n; { + x, err := f.huffSym(&f.h1) + if err != nil { + return err + } + if x < 16 { + // Actual length. + f.bits[i] = x + i++ + continue + } + // Repeat previous length or zero. + var rep int + var nb uint + var b int + switch x { + default: + return InternalError("unexpected length code") + case 16: + rep = 3 + nb = 2 + if i == 0 { + return CorruptInputError(f.roffset) + } + b = f.bits[i-1] + case 17: + rep = 3 + nb = 3 + b = 0 + case 18: + rep = 11 + nb = 7 + b = 0 + } + for f.nb < nb { + if err := f.moreBits(); err != nil { + return err + } + } + rep += int(f.b & uint32(1<>= nb + f.nb -= nb + if i+rep > n { + return CorruptInputError(f.roffset) + } + for j := 0; j < rep; j++ { + f.bits[i] = b + i++ + } + } + + if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) { + return CorruptInputError(f.roffset) + } + + // In order to preserve the property that we never read any extra bytes + // after the end of the DEFLATE stream, huffSym conservatively reads min + // bits at a time until it decodes the symbol. However, since every block + // must end with an EOB marker, we can use that as the minimum number of + // bits to read and guarantee we never read past the end of the stream. + if f.bits[endBlockMarker] > 0 { + f.h1.min = f.bits[endBlockMarker] // Length of EOB marker + } + + return nil +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding associated with fixed Huffman blocks. +func (f *decompressor) huffmanBlock() { + for { + v, err := f.huffSym(f.hl) + if err != nil { + f.err = err + return + } + var n uint // number of bits extra + var length int + switch { + case v < 256: + f.hist[f.hp] = byte(v) + f.hp++ + if f.hp == len(f.hist) { + // After the flush, continue this loop. + f.flush((*decompressor).huffmanBlock) + return + } + continue + case v == 256: + // Done with huffman block; read next block. + f.step = (*decompressor).nextBlock + return + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3) + n = 0 + case v < 269: + length = v*2 - (265*2 - 11) + n = 1 + case v < 273: + length = v*4 - (269*4 - 19) + n = 2 + case v < 277: + length = v*8 - (273*8 - 35) + n = 3 + case v < 281: + length = v*16 - (277*16 - 67) + n = 4 + case v < 285: + length = v*32 - (281*32 - 131) + n = 5 + case v < maxNumLit: + length = 258 + n = 0 + default: + f.err = CorruptInputError(f.roffset) + return + } + if n > 0 { + for f.nb < n { + if err = f.moreBits(); err != nil { + f.err = err + return + } + } + length += int(f.b & uint32(1<>= n + f.nb -= n + } + + var dist int + if f.hd == nil { + for f.nb < 5 { + if err = f.moreBits(); err != nil { + f.err = err + return + } + } + dist = int(reverseByte[(f.b&0x1F)<<3]) + f.b >>= 5 + f.nb -= 5 + } else { + if dist, err = f.huffSym(f.hd); err != nil { + f.err = err + return + } + } + + switch { + case dist < 4: + dist++ + case dist < maxNumDist: + nb := uint(dist-2) >> 1 + // have 1 bit in bottom of dist, need nb more. + extra := (dist & 1) << nb + for f.nb < nb { + if err = f.moreBits(); err != nil { + f.err = err + return + } + } + extra |= int(f.b & uint32(1<>= nb + f.nb -= nb + dist = 1<<(nb+1) + 1 + extra + default: + f.err = CorruptInputError(f.roffset) + return + } + + // Copy history[-dist:-dist+length] into output. + if dist > len(f.hist) { + f.err = InternalError("bad history distance") + return + } + + // No check on length; encoding can be prescient. + if !f.hfull && dist > f.hp { + f.err = CorruptInputError(f.roffset) + return + } + + f.copyLen, f.copyDist = length, dist + if f.copyHist() { + return + } + } +} + +// copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself. +// It reports whether the f.hist buffer is full. +func (f *decompressor) copyHist() bool { + p := f.hp - f.copyDist + if p < 0 { + p += len(f.hist) + } + for f.copyLen > 0 { + n := f.copyLen + if x := len(f.hist) - f.hp; n > x { + n = x + } + if x := len(f.hist) - p; n > x { + n = x + } + forwardCopy(f.hist[:], f.hp, p, n) + p += n + f.hp += n + f.copyLen -= n + if f.hp == len(f.hist) { + // After flush continue copying out of history. + f.flush((*decompressor).copyHuff) + return true + } + if p == len(f.hist) { + p = 0 + } + } + return false +} + +func (f *decompressor) copyHuff() { + if f.copyHist() { + return + } + f.huffmanBlock() +} + +// Copy a single uncompressed data block from input to output. +func (f *decompressor) dataBlock() { + // Uncompressed. + // Discard current half-byte. + f.nb = 0 + f.b = 0 + + // Length then ones-complement of length. + nr, err := io.ReadFull(f.r, f.buf[0:4]) + f.roffset += int64(nr) + if err != nil { + f.err = &ReadError{f.roffset, err} + return + } + n := int(f.buf[0]) | int(f.buf[1])<<8 + nn := int(f.buf[2]) | int(f.buf[3])<<8 + if uint16(nn) != uint16(^n) { + f.err = CorruptInputError(f.roffset) + return + } + + if n == 0 { + // 0-length block means sync + f.flush((*decompressor).nextBlock) + return + } + + f.copyLen = n + f.copyData() +} + +// copyData copies f.copyLen bytes from the underlying reader into f.hist. +// It pauses for reads when f.hist is full. +func (f *decompressor) copyData() { + n := f.copyLen + for n > 0 { + m := len(f.hist) - f.hp + if m > n { + m = n + } + m, err := io.ReadFull(f.r, f.hist[f.hp:f.hp+m]) + f.roffset += int64(m) + if err != nil { + f.err = &ReadError{f.roffset, err} + return + } + n -= m + f.hp += m + if f.hp == len(f.hist) { + f.copyLen = n + f.flush((*decompressor).copyData) + return + } + } + f.step = (*decompressor).nextBlock +} + +func (f *decompressor) setDict(dict []byte) { + if len(dict) > len(f.hist) { + // Will only remember the tail. + dict = dict[len(dict)-len(f.hist):] + } + + f.hp = copy(f.hist[:], dict) + if f.hp == len(f.hist) { + f.hp = 0 + f.hfull = true + } + f.hw = f.hp +} + +func (f *decompressor) moreBits() error { + c, err := f.r.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return err + } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 + return nil +} + +// Read the next Huffman-encoded symbol from f according to h. +func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(h.min) + for { + for f.nb < n { + if err := f.moreBits(); err != nil { + return 0, err + } + } + chunk := h.chunks[f.b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= f.nb { + if n == 0 { + f.err = CorruptInputError(f.roffset) + return 0, f.err + } + f.b >>= n + f.nb -= n + return int(chunk >> huffmanValueShift), nil + } + } +} + +// Flush any buffered output to the underlying writer. +func (f *decompressor) flush(step func(*decompressor)) { + f.toRead = f.hist[f.hw:f.hp] + f.woffset += int64(f.hp - f.hw) + f.hw = f.hp + if f.hp == len(f.hist) { + f.hp = 0 + f.hw = 0 + f.hfull = true + } + f.step = step +} + +func makeReader(r io.Reader) Reader { + if rr, ok := r.(Reader); ok { + return rr + } + return bufio.NewReader(r) +} + +func (f *decompressor) Reset(r io.Reader, dict []byte) error { + *f = decompressor{ + r: makeReader(r), + bits: f.bits, + codebits: f.codebits, + hist: f.hist, + step: (*decompressor).nextBlock, + } + if dict != nil { + f.setDict(dict) + } + return nil +} + +// NewReader returns a new ReadCloser that can be used +// to read the uncompressed version of r. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// It is the caller's responsibility to call Close on the ReadCloser +// when finished reading. +// +// The ReadCloser returned by NewReader also implements Resetter. +func NewReader(r io.Reader) io.ReadCloser { + var f decompressor + f.bits = new([maxNumLit + maxNumDist]int) + f.codebits = new([numCodes]int) + f.r = makeReader(r) + f.hist = new([maxHist]byte) + f.step = (*decompressor).nextBlock + return &f +} + +// NewReaderDict is like NewReader but initializes the reader +// with a preset dictionary. The returned Reader behaves as if +// the uncompressed data stream started with the given dictionary, +// which has already been read. NewReaderDict is typically used +// to read data compressed by NewWriterDict. +// +// The ReadCloser returned by NewReader also implements Resetter. +func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { + var f decompressor + f.r = makeReader(r) + f.hist = new([maxHist]byte) + f.bits = new([maxNumLit + maxNumDist]int) + f.codebits = new([numCodes]int) + f.step = (*decompressor).nextBlock + f.setDict(dict) + return &f +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/reverse_bits.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/reverse_bits.go new file mode 100644 index 00000000000..c1a02720d1a --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/reverse_bits.go @@ -0,0 +1,48 @@ +// 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. + +package flate + +var reverseByte = [256]byte{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +} + +func reverseUint16(v uint16) uint16 { + return uint16(reverseByte[v>>8]) | uint16(reverseByte[v&0xFF])<<8 +} + +func reverseBits(number uint16, bitLength byte) uint16 { + return reverseUint16(number << uint8(16-bitLength)) +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/snappy.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/snappy.go new file mode 100644 index 00000000000..78199380d18 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/snappy.go @@ -0,0 +1,97 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Modified for deflate by Klaus Post (c) 2015. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// We limit how far copy back-references can go, the same as the C++ code. +const maxOffset = 1 << 15 + +// emitLiteral writes a literal chunk and returns the number of bytes written. +func emitLiteral(dst *tokens, lit []byte) { + ol := dst.n + for i, v := range lit { + dst.tokens[i+ol] = token(v) + } + dst.n += len(lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +func emitCopy(dst *tokens, offset, length int) { + dst.tokens[dst.n] = matchToken(uint32(length-3), uint32(offset-minOffsetSize)) + dst.n++ +} + +// snappyEncode uses Snappy-like compression, but stores as Huffman +// blocks. +func snappyEncode(dst *tokens, src []byte) { + // Return early if src is short. + if len(src) <= 4 { + if len(src) != 0 { + emitLiteral(dst, src) + } + return + } + + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + const maxTableSize = 1 << 14 + shift, tableSize := uint(32-8), 1<<8 + for tableSize < maxTableSize && tableSize < len(src) { + shift-- + tableSize *= 2 + } + var table [maxTableSize]int + var misses int + // Iterate over the source bytes. + var ( + s int // The iterator position. + t int // The last position with the same hash as s. + lit int // The start position of any pending literal bytes. + ) + for s+3 < len(src) { + // Update the hash table. + b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] + h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 + p := &table[(h*0x1e35a7bd)>>shift] + // We need to to store values in [-1, inf) in table. To save + // some initialization time, (re)use the table's zero value + // and shift the values against this zero: add 1 on writes, + // subtract 1 on reads. + t, *p = *p-1, s+1 + // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. + if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { + misses++ + // Skip 1 byte for 16 consecutive missed. + s += 1 + (misses >> 4) + continue + } + // Otherwise, we have a match. First, emit any pending literal bytes. + if lit != s { + emitLiteral(dst, src[lit:s]) + } + // Extend the match to be as long as possible. + s0 := s + s1 := s + maxMatchLength + if s1 > len(src) { + s1 = len(src) + } + s, t = s+4, t+4 + for s < s1 && src[s] == src[t] { + s++ + t++ + } + misses = 0 + // Emit the copied bytes. + // inlined: emitCopy(dst, s-t, s-s0) + + dst.tokens[dst.n] = matchToken(uint32(s-s0-3), uint32(s-t-minOffsetSize)) + dst.n++ + lit = s + } + + // Emit any final pending literal bytes and return. + if lit != len(src) { + emitLiteral(dst, src[lit:]) + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/flate/token.go b/Godeps/_workspace/src/github.com/klauspost/compress/flate/token.go new file mode 100644 index 00000000000..94fa5eb93ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/flate/token.go @@ -0,0 +1,105 @@ +// 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. + +package flate + +const ( + // 2 bits: type 0 = literal 1=EOF 2=Match 3=Unused + // 8 bits: xlength = length - MIN_MATCH_LENGTH + // 22 bits xoffset = offset - MIN_OFFSET_SIZE, or literal + lengthShift = 22 + offsetMask = 1< pair into a match token. +func matchToken(xlength uint32, xoffset uint32) token { + return token(matchType + xlength<> lengthShift) } + +func lengthCode(len uint32) uint32 { return lengthCodes[len] } + +// Returns the offset code corresponding to a specific offset +func offsetCode(off uint32) uint32 { + if off < uint32(len(offsetCodes)) { + return offsetCodes[off] + } else if off>>7 < uint32(len(offsetCodes)) { + return offsetCodes[off>>7] + 14 + } else { + return offsetCodes[off>>14] + 28 + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gunzip.go b/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gunzip.go new file mode 100644 index 00000000000..f0d49197be1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gunzip.go @@ -0,0 +1,342 @@ +// 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. + +// Package gzip implements reading and writing of gzip format compressed files, +// as specified in RFC 1952. +package gzip + +import ( + "bufio" + "errors" + "hash" + "io" + "time" + + "github.com/klauspost/compress/flate" + "github.com/klauspost/crc32" +) + +const ( + gzipID1 = 0x1f + gzipID2 = 0x8b + gzipDeflate = 8 + flagText = 1 << 0 + flagHdrCrc = 1 << 1 + flagExtra = 1 << 2 + flagName = 1 << 3 + flagComment = 1 << 4 +) + +func makeReader(r io.Reader) flate.Reader { + if rr, ok := r.(flate.Reader); ok { + return rr + } + return bufio.NewReader(r) +} + +var ( + // ErrChecksum is returned when reading GZIP data that has an invalid checksum. + ErrChecksum = errors.New("gzip: invalid checksum") + // ErrHeader is returned when reading GZIP data that has an invalid header. + ErrHeader = errors.New("gzip: invalid header") +) + +// The gzip file stores a header giving metadata about the compressed file. +// That header is exposed as the fields of the Writer and Reader structs. +type Header struct { + Comment string // comment + Extra []byte // "extra data" + ModTime time.Time // modification time + Name string // file name + OS byte // operating system type +} + +// A Reader is an io.Reader that can be read to retrieve +// uncompressed data from a gzip-format compressed file. +// +// In general, a gzip file can be a concatenation of gzip files, +// each with its own header. Reads from the Reader +// return the concatenation of the uncompressed data of each. +// Only the first header is recorded in the Reader fields. +// +// Gzip files store a length and checksum of the uncompressed data. +// The Reader will return a ErrChecksum when Read +// reaches the end of the uncompressed data if it does not +// have the expected length or checksum. Clients should treat data +// returned by Read as tentative until they receive the io.EOF +// marking the end of the data. +type Reader struct { + Header + r flate.Reader + decompressor io.ReadCloser + digest hash.Hash32 + size uint32 + flg byte + buf [512]byte + err error + multistream bool +} + +// NewReader creates a new Reader reading the given reader. +// If r does not also implement io.ByteReader, +// the decompressor may read more data than necessary from r. +// It is the caller's responsibility to call Close on the Reader when done. +func NewReader(r io.Reader) (*Reader, error) { + z := new(Reader) + z.r = makeReader(r) + z.multistream = true + z.digest = crc32.NewIEEE() + if err := z.readHeader(true); err != nil { + return nil, err + } + return z, nil +} + +// Reset discards the Reader z's state and makes it equivalent to the +// result of its original state from NewReader, but reading from r instead. +// This permits reusing a Reader rather than allocating a new one. +func (z *Reader) Reset(r io.Reader) error { + z.r = makeReader(r) + if z.digest == nil { + z.digest = crc32.NewIEEE() + } else { + z.digest.Reset() + } + z.size = 0 + z.err = nil + z.multistream = true + return z.readHeader(true) +} + +// Multistream controls whether the reader supports multistream files. +// +// If enabled (the default), the Reader expects the input to be a sequence +// of individually gzipped data streams, each with its own header and +// trailer, ending at EOF. The effect is that the concatenation of a sequence +// of gzipped files is treated as equivalent to the gzip of the concatenation +// of the sequence. This is standard behavior for gzip readers. +// +// Calling Multistream(false) disables this behavior; disabling the behavior +// can be useful when reading file formats that distinguish individual gzip +// data streams or mix gzip data streams with other data streams. +// In this mode, when the Reader reaches the end of the data stream, +// Read returns io.EOF. If the underlying reader implements io.ByteReader, +// it will be left positioned just after the gzip stream. +// To start the next stream, call z.Reset(r) followed by z.Multistream(false). +// If there is no next stream, z.Reset(r) will return io.EOF. +func (z *Reader) Multistream(ok bool) { + z.multistream = ok +} + +// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). +func get4(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 +} + +func (z *Reader) readString() (string, error) { + var err error + needconv := false + for i := 0; ; i++ { + if i >= len(z.buf) { + return "", ErrHeader + } + z.buf[i], err = z.r.ReadByte() + if err != nil { + return "", err + } + if z.buf[i] > 0x7f { + needconv = true + } + if z.buf[i] == 0 { + // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). + if needconv { + s := make([]rune, 0, i) + for _, v := range z.buf[0:i] { + s = append(s, rune(v)) + } + return string(s), nil + } + return string(z.buf[0:i]), nil + } + } +} + +func (z *Reader) read2() (uint32, error) { + _, err := io.ReadFull(z.r, z.buf[0:2]) + if err != nil { + return 0, err + } + return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil +} + +func (z *Reader) readHeader(save bool) error { + _, err := io.ReadFull(z.r, z.buf[0:10]) + if err != nil { + return err + } + if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { + return ErrHeader + } + z.flg = z.buf[3] + if save { + z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0) + // z.buf[8] is xfl, ignored + z.OS = z.buf[9] + } + z.digest.Reset() + z.digest.Write(z.buf[0:10]) + + if z.flg&flagExtra != 0 { + n, err := z.read2() + if err != nil { + return err + } + data := make([]byte, n) + if _, err = io.ReadFull(z.r, data); err != nil { + return err + } + if save { + z.Extra = data + } + } + + var s string + if z.flg&flagName != 0 { + if s, err = z.readString(); err != nil { + return err + } + if save { + z.Name = s + } + } + + if z.flg&flagComment != 0 { + if s, err = z.readString(); err != nil { + return err + } + if save { + z.Comment = s + } + } + + if z.flg&flagHdrCrc != 0 { + n, err := z.read2() + if err != nil { + return err + } + sum := z.digest.Sum32() & 0xFFFF + if n != sum { + return ErrHeader + } + } + + z.digest.Reset() + if z.decompressor == nil { + z.decompressor = flate.NewReader(z.r) + } else { + z.decompressor.(flate.Resetter).Reset(z.r, nil) + } + return nil +} + +func (z *Reader) Read(p []byte) (n int, err error) { + if z.err != nil { + return 0, z.err + } + if len(p) == 0 { + return 0, nil + } + + n, err = z.decompressor.Read(p) + z.digest.Write(p[0:n]) + z.size += uint32(n) + if n != 0 || err != io.EOF { + z.err = err + return + } + + // Finished file; check checksum + size. + if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil { + z.err = err + return 0, err + } + crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) + sum := z.digest.Sum32() + if sum != crc32 || isize != z.size { + z.err = ErrChecksum + return 0, z.err + } + + // File is ok; is there another? + if !z.multistream { + return 0, io.EOF + } + + if err = z.readHeader(false); err != nil { + z.err = err + return + } + + // Yes. Reset and read from it. + z.digest.Reset() + z.size = 0 + return z.Read(p) +} + +// Support the io.WriteTo interface for io.Copy and friends. +func (z *Reader) WriteTo(w io.Writer) (int64, error) { + total := int64(0) + for { + if z.err != nil { + if z.err == io.EOF { + return total, nil + } + return total, z.err + } + + // We write both to output and digest. + mw := io.MultiWriter(w, z.digest) + n, err := z.decompressor.(io.WriterTo).WriteTo(mw) + total += n + z.size += uint32(n) + if err != nil { + z.err = err + return total, z.err + } + + // Finished file; check checksum + size. + if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil { + z.err = err + return 0, err + } + crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) + sum := z.digest.Sum32() + if sum != crc32 || isize != z.size { + z.err = ErrChecksum + return 0, z.err + } + + // File is ok; is there another? + if !z.multistream { + return total, nil + } + + err = z.readHeader(false) + // There was not more + if err == io.EOF { + return total, nil + } + if err != nil { + z.err = err + return total, err + } + + // Yes. Reset and read from it. + z.digest.Reset() + z.size = 0 + } +} + +// Close closes the Reader. It does not close the underlying io.Reader. +func (z *Reader) Close() error { return z.decompressor.Close() } diff --git a/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gzip.go b/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gzip.go new file mode 100644 index 00000000000..a590c883cd1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/compress/gzip/gzip.go @@ -0,0 +1,274 @@ +// Copyright 2010 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. + +package gzip + +import ( + "errors" + "fmt" + "hash" + "io" + + "github.com/klauspost/compress/flate" + "github.com/klauspost/crc32" +) + +// These constants are copied from the flate package, so that code that imports +// "compress/gzip" does not also have to import "compress/flate". +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression + ConstantCompression = flate.ConstantCompression +) + +// A Writer is an io.WriteCloser. +// Writes to a Writer are compressed and written to w. +type Writer struct { + Header + w io.Writer + level int + wroteHeader bool + compressor *flate.Writer + digest hash.Hash32 + size uint32 + closed bool + buf [10]byte + err error +} + +// NewWriter returns a new Writer. +// Writes to the returned writer are compressed and written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +// +// Callers that wish to set the fields in Writer.Header must do so before +// the first call to Write or Close. The Comment and Name header fields are +// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO +// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an +// error on Write. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevel(w, DefaultCompression) + return z +} + +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be ConstantCompression, DefaultCompression, +// NoCompression, or any integer value between BestSpeed and BestCompression +// inclusive. The error returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + if level < ConstantCompression || level > BestCompression { + return nil, fmt.Errorf("gzip: invalid compression level: %d", level) + } + z := new(Writer) + z.init(w, level) + return z, nil +} + +func (z *Writer) init(w io.Writer, level int) { + digest := z.digest + if digest != nil { + digest.Reset() + } else { + digest = crc32.NewIEEE() + } + compressor := z.compressor + if compressor != nil { + compressor.Reset(w) + } + *z = Writer{ + Header: Header{ + OS: 255, // unknown + }, + w: w, + level: level, + digest: digest, + compressor: compressor, + } +} + +// Reset discards the Writer z's state and makes it equivalent to the +// result of its original state from NewWriter or NewWriterLevel, but +// writing to w instead. This permits reusing a Writer rather than +// allocating a new one. +func (z *Writer) Reset(w io.Writer) { + z.init(w, z.level) +} + +// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). +func put2(p []byte, v uint16) { + p[0] = uint8(v >> 0) + p[1] = uint8(v >> 8) +} + +func put4(p []byte, v uint32) { + p[0] = uint8(v >> 0) + p[1] = uint8(v >> 8) + p[2] = uint8(v >> 16) + p[3] = uint8(v >> 24) +} + +// writeBytes writes a length-prefixed byte slice to z.w. +func (z *Writer) writeBytes(b []byte) error { + if len(b) > 0xffff { + return errors.New("gzip.Write: Extra data is too large") + } + put2(z.buf[0:2], uint16(len(b))) + _, err := z.w.Write(z.buf[0:2]) + if err != nil { + return err + } + _, err = z.w.Write(b) + return err +} + +// writeString writes a UTF-8 string s in GZIP's format to z.w. +// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). +func (z *Writer) writeString(s string) (err error) { + // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. + needconv := false + for _, v := range s { + if v == 0 || v > 0xff { + return errors.New("gzip.Write: non-Latin-1 header string") + } + if v > 0x7f { + needconv = true + } + } + if needconv { + b := make([]byte, 0, len(s)) + for _, v := range s { + b = append(b, byte(v)) + } + _, err = z.w.Write(b) + } else { + _, err = io.WriteString(z.w, s) + } + if err != nil { + return err + } + // GZIP strings are NUL-terminated. + z.buf[0] = 0 + _, err = z.w.Write(z.buf[0:1]) + return err +} + +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed. +func (z *Writer) Write(p []byte) (int, error) { + if z.err != nil { + return 0, z.err + } + var n int + // Write the GZIP header lazily. + if !z.wroteHeader { + z.wroteHeader = true + z.buf[0] = gzipID1 + z.buf[1] = gzipID2 + z.buf[2] = gzipDeflate + z.buf[3] = 0 + if z.Extra != nil { + z.buf[3] |= 0x04 + } + if z.Name != "" { + z.buf[3] |= 0x08 + } + if z.Comment != "" { + z.buf[3] |= 0x10 + } + put4(z.buf[4:8], uint32(z.ModTime.Unix())) + if z.level == BestCompression { + z.buf[8] = 2 + } else if z.level == BestSpeed { + z.buf[8] = 4 + } else { + z.buf[8] = 0 + } + z.buf[9] = z.OS + n, z.err = z.w.Write(z.buf[0:10]) + if z.err != nil { + return n, z.err + } + if z.Extra != nil { + z.err = z.writeBytes(z.Extra) + if z.err != nil { + return n, z.err + } + } + if z.Name != "" { + z.err = z.writeString(z.Name) + if z.err != nil { + return n, z.err + } + } + if z.Comment != "" { + z.err = z.writeString(z.Comment) + if z.err != nil { + return n, z.err + } + } + if z.compressor == nil { + z.compressor, _ = flate.NewWriter(z.w, z.level) + } + } + z.size += uint32(len(p)) + z.digest.Write(p) + n, z.err = z.compressor.Write(p) + return n, z.err +} + +// Flush flushes any pending compressed data to the underlying writer. +// +// It is useful mainly in compressed network protocols, to ensure that +// a remote reader has enough data to reconstruct a packet. Flush does +// not return until the data has been written. If the underlying +// writer returns an error, Flush returns that error. +// +// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. +func (z *Writer) Flush() error { + if z.err != nil { + return z.err + } + if z.closed { + return nil + } + if !z.wroteHeader { + z.Write(nil) + if z.err != nil { + return z.err + } + } + z.err = z.compressor.Flush() + return z.err +} + +// Close closes the Writer, flushing any unwritten data to the underlying +// io.Writer, but does not close the underlying io.Writer. +func (z *Writer) Close() error { + if z.err != nil { + return z.err + } + if z.closed { + return nil + } + z.closed = true + if !z.wroteHeader { + z.Write(nil) + if z.err != nil { + return z.err + } + } + z.err = z.compressor.Close() + if z.err != nil { + return z.err + } + put4(z.buf[0:4], z.digest.Sum32()) + put4(z.buf[4:8], z.size) + _, z.err = z.w.Write(z.buf[0:8]) + return z.err +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/.gitignore b/Godeps/_workspace/src/github.com/klauspost/cpuid/.gitignore new file mode 100644 index 00000000000..daf913b1b34 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/.travis.yml b/Godeps/_workspace/src/github.com/klauspost/cpuid/.travis.yml new file mode 100644 index 00000000000..fb74de6b878 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/LICENSE b/Godeps/_workspace/src/github.com/klauspost/cpuid/LICENSE new file mode 100644 index 00000000000..5cec7ee949b --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Klaus Post + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/README.md b/Godeps/_workspace/src/github.com/klauspost/cpuid/README.md new file mode 100644 index 00000000000..b2b6bee879a --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/README.md @@ -0,0 +1,145 @@ +# cpuid +Package cpuid provides information about the CPU running the current program. + +CPU features are detected on startup, and kept for fast access through the life of the application. +Currently x86 / x64 (AMD64) is supported, and no external C (cgo) code is used, which should make the library very easy to use. + +You can access the CPU information by accessing the shared CPU variable of the cpuid library. + +Package home: https://github.com/klauspost/cpuid + +[![GoDoc][1]][2] [![Build Status][3]][4] + +[1]: https://godoc.org/github.com/klauspost/cpuid?status.svg +[2]: https://godoc.org/github.com/klauspost/cpuid +[3]: https://travis-ci.org/klauspost/cpuid.svg +[4]: https://travis-ci.org/klauspost/cpuid + +# features +## CPU Instructions +* **CMOV** (i686 CMOV) +* **NX** (NX (No-Execute) bit) +* **AMD3DNOW** (AMD 3DNOW) +* **AMD3DNOWEXT** (AMD 3DNowExt) +* **MMX** (standard MMX) +* **MMXEXT** (SSE integer functions or AMD MMX ext) +* **SSE** (SSE functions) +* **SSE2** (P4 SSE functions) +* **SSE3** (Prescott SSE3 functions) +* **SSSE3** (Conroe SSSE3 functions) +* **SSE4** (Penryn SSE4.1 functions) +* **SSE4A** (AMD Barcelona microarchitecture SSE4a instructions) +* **SSE42** (Nehalem SSE4.2 functions) +* **AVX** (AVX functions) +* **AVX2** (AVX2 functions) +* **FMA3** (Intel FMA 3) +* **FMA4** (Bulldozer FMA4 functions) +* **XOP** (Bulldozer XOP functions) +* **F16C** (Half-precision floating-point conversion) +* **BMI1** (Bit Manipulation Instruction Set 1) +* **BMI2** (Bit Manipulation Instruction Set 2) +* **TBM** (AMD Trailing Bit Manipulation) +* **LZCNT** (LZCNT instruction) +* **POPCNT** (POPCNT instruction) +* **AESNI** (Advanced Encryption Standard New Instructions) +* **CLMUL** (Carry-less Multiplication) +* **HTT** (Hyperthreading (enabled)) +* **HLE** (Hardware Lock Elision) +* **RTM** (Restricted Transactional Memory) +* **RDRAND** (RDRAND instruction is available) +* **RDSEED** (RDSEED instruction is available) +* **ADX** (Intel ADX (Multi-Precision Add-Carry Instruction Extensions)) +* **SHA** (Intel SHA Extensions) +* **AVX512F** (AVX-512 Foundation) +* **AVX512DQ** (AVX-512 Doubleword and Quadword Instructions) +* **AVX512IFMA** (AVX-512 Integer Fused Multiply-Add Instructions) +* **AVX512PF** (AVX-512 Prefetch Instructions) +* **AVX512ER** (AVX-512 Exponential and Reciprocal Instructions) +* **AVX512CD** (AVX-512 Conflict Detection Instructions) +* **AVX512BW** (AVX-512 Byte and Word Instructions) +* **AVX512VL** (AVX-512 Vector Length Extensions) +* **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions) +* **MPX** (Intel MPX (Memory Protection Extensions)) +* **ERMS** (Enhanced REP MOVSB/STOSB) +* **RDTSCP** (RDTSCP Instruction) +* **CX16** (CMPXCHG16B Instruction) +* **SGX** (Software Guard Extensions, with activation details) + +## Performance +* **RDTSCP()** Returns current cycle count. Can be used for benchmarking. +* **SSE2SLOW** (SSE2 is supported, but usually not faster) +* **SSE3SLOW** (SSE3 is supported, but usually not faster) +* **ATOM** (Atom processor, some SSSE3 instructions are slower) +* **Cache line** (Probable size of a cache line). +* **L1, L2, L3 Cache size** on newer Intel/AMD CPUs. + +## Cpu Vendor/VM +* **Intel** +* **AMD** +* **VIA** +* **Transmeta** +* **NSC** +* **KVM** (Kernel-based Virtual Machine) +* **MSVM** (Microsoft Hyper-V or Windows Virtual PC) +* **VMware** +* **XenHVM** + +# installing + +```go get github.com/klauspost/cpuid``` + +# example + +```Go +package main + +import ( + "fmt" + "github.com/klauspost/cpuid" +) + +func main() { + // Print basic CPU information: + fmt.Println("Name:", cpuid.CPU.BrandName) + fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores) + fmt.Println("ThreadsPerCore:", cpuid.CPU.ThreadsPerCore) + fmt.Println("LogicalCores:", cpuid.CPU.LogicalCores) + fmt.Println("Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model) + fmt.Println("Features:", cpuid.CPU.Features) + fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine) + fmt.Println("L1 Data Cache:", cpuid.CPU.Cache.L1D, "bytes") + fmt.Println("L1 Instruction Cache:", cpuid.CPU.Cache.L1D, "bytes") + fmt.Println("L2 Cache:", cpuid.CPU.Cache.L2, "bytes") + fmt.Println("L3 Cache:", cpuid.CPU.Cache.L3, "bytes") + + // Test if we have a specific feature: + if cpuid.CPU.SSE() { + fmt.Println("We have Streaming SIMD Extensions") + } +} +``` + +Sample output: +``` +>go run main.go +Name: Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz +PhysicalCores: 2 +ThreadsPerCore: 2 +LogicalCores: 4 +Family 6 Model: 42 +Features: CMOV,MMX,MMXEXT,SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2,AVX,AESNI,CLMUL +Cacheline bytes: 64 +We have Streaming SIMD Extensions +``` + +# private package + +In the "private" folder you can find an autogenerated version of the library you can include in your own packages. + +For this purpose all exports are removed, and functions and constants are lowercased. + +This is not a recommended way of using the library, but provided for convenience, if it is difficult for you to use external packages. + +# license + +This code is published under an MIT license. See LICENSE file for more information. diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid.go new file mode 100644 index 00000000000..9230ca56282 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid.go @@ -0,0 +1,1022 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// Package cpuid provides information about the CPU running the current program. +// +// CPU features are detected on startup, and kept for fast access through the life of the application. +// Currently x86 / x64 (AMD64) is supported. +// +// You can access the CPU information by accessing the shared CPU variable of the cpuid library. +// +// Package home: https://github.com/klauspost/cpuid +package cpuid + +import "strings" + +// Vendor is a representation of a CPU vendor. +type Vendor int + +const ( + Other Vendor = iota + Intel + AMD + VIA + Transmeta + NSC + KVM // Kernel-based Virtual Machine + MSVM // Microsoft Hyper-V or Windows Virtual PC + VMware + XenHVM +) + +const ( + CMOV = 1 << iota // i686 CMOV + NX // NX (No-Execute) bit + AMD3DNOW // AMD 3DNOW + AMD3DNOWEXT // AMD 3DNowExt + MMX // standard MMX + MMXEXT // SSE integer functions or AMD MMX ext + SSE // SSE functions + SSE2 // P4 SSE functions + SSE3 // Prescott SSE3 functions + SSSE3 // Conroe SSSE3 functions + SSE4 // Penryn SSE4.1 functions + SSE4A // AMD Barcelona microarchitecture SSE4a instructions + SSE42 // Nehalem SSE4.2 functions + AVX // AVX functions + AVX2 // AVX2 functions + FMA3 // Intel FMA 3 + FMA4 // Bulldozer FMA4 functions + XOP // Bulldozer XOP functions + F16C // Half-precision floating-point conversion + BMI1 // Bit Manipulation Instruction Set 1 + BMI2 // Bit Manipulation Instruction Set 2 + TBM // AMD Trailing Bit Manipulation + LZCNT // LZCNT instruction + POPCNT // POPCNT instruction + AESNI // Advanced Encryption Standard New Instructions + CLMUL // Carry-less Multiplication + HTT // Hyperthreading (enabled) + HLE // Hardware Lock Elision + RTM // Restricted Transactional Memory + RDRAND // RDRAND instruction is available + RDSEED // RDSEED instruction is available + ADX // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + SHA // Intel SHA Extensions + AVX512F // AVX-512 Foundation + AVX512DQ // AVX-512 Doubleword and Quadword Instructions + AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions + AVX512PF // AVX-512 Prefetch Instructions + AVX512ER // AVX-512 Exponential and Reciprocal Instructions + AVX512CD // AVX-512 Conflict Detection Instructions + AVX512BW // AVX-512 Byte and Word Instructions + AVX512VL // AVX-512 Vector Length Extensions + AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions + MPX // Intel MPX (Memory Protection Extensions) + ERMS // Enhanced REP MOVSB/STOSB + RDTSCP // RDTSCP Instruction + CX16 // CMPXCHG16B Instruction + SGX // Software Guard Extensions + + // Performance indicators + SSE2SLOW // SSE2 is supported, but usually not faster + SSE3SLOW // SSE3 is supported, but usually not faster + ATOM // Atom processor, some SSSE3 instructions are slower +) + +var flagNames = map[Flags]string{ + CMOV: "CMOV", // i686 CMOV + NX: "NX", // NX (No-Execute) bit + AMD3DNOW: "AMD3DNOW", // AMD 3DNOW + AMD3DNOWEXT: "AMD3DNOWEXT", // AMD 3DNowExt + MMX: "MMX", // Standard MMX + MMXEXT: "MMXEXT", // SSE integer functions or AMD MMX ext + SSE: "SSE", // SSE functions + SSE2: "SSE2", // P4 SSE2 functions + SSE3: "SSE3", // Prescott SSE3 functions + SSSE3: "SSSE3", // Conroe SSSE3 functions + SSE4: "SSE4.1", // Penryn SSE4.1 functions + SSE4A: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions + SSE42: "SSE4.2", // Nehalem SSE4.2 functions + AVX: "AVX", // AVX functions + AVX2: "AVX2", // AVX functions + FMA3: "FMA3", // Intel FMA 3 + FMA4: "FMA4", // Bulldozer FMA4 functions + XOP: "XOP", // Bulldozer XOP functions + F16C: "F16C", // Half-precision floating-point conversion + BMI1: "BMI1", // Bit Manipulation Instruction Set 1 + BMI2: "BMI2", // Bit Manipulation Instruction Set 2 + TBM: "TBM", // AMD Trailing Bit Manipulation + LZCNT: "LZCNT", // LZCNT instruction + POPCNT: "POPCNT", // POPCNT instruction + AESNI: "AESNI", // Advanced Encryption Standard New Instructions + CLMUL: "CLMUL", // Carry-less Multiplication + HTT: "HTT", // Hyperthreading (enabled) + HLE: "HLE", // Hardware Lock Elision + RTM: "RTM", // Restricted Transactional Memory + RDRAND: "RDRAND", // RDRAND instruction is available + RDSEED: "RDSEED", // RDSEED instruction is available + ADX: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + SHA: "SHA", // Intel SHA Extensions + AVX512F: "AVX512F", // AVX-512 Foundation + AVX512DQ: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions + AVX512IFMA: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions + AVX512PF: "AVX512PF", // AVX-512 Prefetch Instructions + AVX512ER: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions + AVX512CD: "AVX512CD", // AVX-512 Conflict Detection Instructions + AVX512BW: "AVX512BW", // AVX-512 Byte and Word Instructions + AVX512VL: "AVX512VL", // AVX-512 Vector Length Extensions + AVX512VBMI: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions + MPX: "MPX", // Intel MPX (Memory Protection Extensions) + ERMS: "ERMS", // Enhanced REP MOVSB/STOSB + RDTSCP: "RDTSCP", // RDTSCP Instruction + CX16: "CX16", // CMPXCHG16B Instruction + SGX: "SGX", // Software Guard Extensions + + // Performance indicators + SSE2SLOW: "SSE2SLOW", // SSE2 supported, but usually not faster + SSE3SLOW: "SSE3SLOW", // SSE3 supported, but usually not faster + ATOM: "ATOM", // Atom processor, some SSSE3 instructions are slower + +} + +// CPUInfo contains information about the detected system CPU. +type CPUInfo struct { + BrandName string // Brand name reported by the CPU + VendorID Vendor // Comparable CPU vendor ID + Features Flags // Features of the CPU + PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. + ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable. + LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable. + Family int // CPU family number + Model int // CPU model number + CacheLine int // Cache line size in bytes. Will be 0 if undetectable. + Cache struct { + L1I int // L1 Instruction Cache (per core or shared). Will be -1 if undetected + L1D int // L1 Data Cache (per core or shared). Will be -1 if undetected + L2 int // L2 Cache (per core or shared). Will be -1 if undetected + L3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected + } + SGX SGXSupport + maxFunc uint32 + maxExFunc uint32 +} + +var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) +var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) +var xgetbv func(index uint32) (eax, edx uint32) +var rdtscpAsm func() (eax, ebx, ecx, edx uint32) + +// CPU contains information about the CPU as detected on startup, +// or when Detect last was called. +// +// Use this as the primary entry point to you data, +// this way queries are +var CPU CPUInfo + +func init() { + initCPU() + Detect() +} + +// Detect will re-detect current CPU info. +// This will replace the content of the exported CPU variable. +// +// Unless you expect the CPU to change while you are running your program +// you should not need to call this function. +// If you call this, you must ensure that no other goroutine is accessing the +// exported CPU variable. +func Detect() { + CPU.maxFunc = maxFunctionID() + CPU.maxExFunc = maxExtendedFunction() + CPU.BrandName = brandName() + CPU.CacheLine = cacheLine() + CPU.Family, CPU.Model = familyModel() + CPU.Features = support() + CPU.SGX = sgx(CPU.Features&SGX != 0) + CPU.ThreadsPerCore = threadsPerCore() + CPU.LogicalCores = logicalCores() + CPU.PhysicalCores = physicalCores() + CPU.VendorID = vendorID() + CPU.cacheSize() +} + +// Generated here: http://play.golang.org/p/BxFH2Gdc0G + +// Cmov indicates support of CMOV instructions +func (c CPUInfo) Cmov() bool { + return c.Features&CMOV != 0 +} + +// Amd3dnow indicates support of AMD 3DNOW! instructions +func (c CPUInfo) Amd3dnow() bool { + return c.Features&AMD3DNOW != 0 +} + +// Amd3dnowExt indicates support of AMD 3DNOW! Extended instructions +func (c CPUInfo) Amd3dnowExt() bool { + return c.Features&AMD3DNOWEXT != 0 +} + +// MMX indicates support of MMX instructions +func (c CPUInfo) MMX() bool { + return c.Features&MMX != 0 +} + +// MMXExt indicates support of MMXEXT instructions +// (SSE integer functions or AMD MMX ext) +func (c CPUInfo) MMXExt() bool { + return c.Features&MMXEXT != 0 +} + +// SSE indicates support of SSE instructions +func (c CPUInfo) SSE() bool { + return c.Features&SSE != 0 +} + +// SSE2 indicates support of SSE 2 instructions +func (c CPUInfo) SSE2() bool { + return c.Features&SSE2 != 0 +} + +// SSE3 indicates support of SSE 3 instructions +func (c CPUInfo) SSE3() bool { + return c.Features&SSE3 != 0 +} + +// SSSE3 indicates support of SSSE 3 instructions +func (c CPUInfo) SSSE3() bool { + return c.Features&SSSE3 != 0 +} + +// SSE4 indicates support of SSE 4 (also called SSE 4.1) instructions +func (c CPUInfo) SSE4() bool { + return c.Features&SSE4 != 0 +} + +// SSE42 indicates support of SSE4.2 instructions +func (c CPUInfo) SSE42() bool { + return c.Features&SSE42 != 0 +} + +// AVX indicates support of AVX instructions +// and operating system support of AVX instructions +func (c CPUInfo) AVX() bool { + return c.Features&AVX != 0 +} + +// AVX2 indicates support of AVX2 instructions +func (c CPUInfo) AVX2() bool { + return c.Features&AVX2 != 0 +} + +// FMA3 indicates support of FMA3 instructions +func (c CPUInfo) FMA3() bool { + return c.Features&FMA3 != 0 +} + +// FMA4 indicates support of FMA4 instructions +func (c CPUInfo) FMA4() bool { + return c.Features&FMA4 != 0 +} + +// XOP indicates support of XOP instructions +func (c CPUInfo) XOP() bool { + return c.Features&XOP != 0 +} + +// F16C indicates support of F16C instructions +func (c CPUInfo) F16C() bool { + return c.Features&F16C != 0 +} + +// BMI1 indicates support of BMI1 instructions +func (c CPUInfo) BMI1() bool { + return c.Features&BMI1 != 0 +} + +// BMI2 indicates support of BMI2 instructions +func (c CPUInfo) BMI2() bool { + return c.Features&BMI2 != 0 +} + +// TBM indicates support of TBM instructions +// (AMD Trailing Bit Manipulation) +func (c CPUInfo) TBM() bool { + return c.Features&TBM != 0 +} + +// Lzcnt indicates support of LZCNT instruction +func (c CPUInfo) Lzcnt() bool { + return c.Features&LZCNT != 0 +} + +// Popcnt indicates support of POPCNT instruction +func (c CPUInfo) Popcnt() bool { + return c.Features&POPCNT != 0 +} + +// HTT indicates the processor has Hyperthreading enabled +func (c CPUInfo) HTT() bool { + return c.Features&HTT != 0 +} + +// SSE2Slow indicates that SSE2 may be slow on this processor +func (c CPUInfo) SSE2Slow() bool { + return c.Features&SSE2SLOW != 0 +} + +// SSE3Slow indicates that SSE3 may be slow on this processor +func (c CPUInfo) SSE3Slow() bool { + return c.Features&SSE3SLOW != 0 +} + +// AesNi indicates support of AES-NI instructions +// (Advanced Encryption Standard New Instructions) +func (c CPUInfo) AesNi() bool { + return c.Features&AESNI != 0 +} + +// Clmul indicates support of CLMUL instructions +// (Carry-less Multiplication) +func (c CPUInfo) Clmul() bool { + return c.Features&CLMUL != 0 +} + +// NX indicates support of NX (No-Execute) bit +func (c CPUInfo) NX() bool { + return c.Features&NX != 0 +} + +// SSE4A indicates support of AMD Barcelona microarchitecture SSE4a instructions +func (c CPUInfo) SSE4A() bool { + return c.Features&SSE4A != 0 +} + +// HLE indicates support of Hardware Lock Elision +func (c CPUInfo) HLE() bool { + return c.Features&HLE != 0 +} + +// RTM indicates support of Restricted Transactional Memory +func (c CPUInfo) RTM() bool { + return c.Features&RTM != 0 +} + +// Rdrand indicates support of RDRAND instruction is available +func (c CPUInfo) Rdrand() bool { + return c.Features&RDRAND != 0 +} + +// Rdseed indicates support of RDSEED instruction is available +func (c CPUInfo) Rdseed() bool { + return c.Features&RDSEED != 0 +} + +// ADX indicates support of Intel ADX (Multi-Precision Add-Carry Instruction Extensions) +func (c CPUInfo) ADX() bool { + return c.Features&ADX != 0 +} + +// SHA indicates support of Intel SHA Extensions +func (c CPUInfo) SHA() bool { + return c.Features&SHA != 0 +} + +// AVX512F indicates support of AVX-512 Foundation +func (c CPUInfo) AVX512F() bool { + return c.Features&AVX512F != 0 +} + +// AVX512DQ indicates support of AVX-512 Doubleword and Quadword Instructions +func (c CPUInfo) AVX512DQ() bool { + return c.Features&AVX512DQ != 0 +} + +// AVX512IFMA indicates support of AVX-512 Integer Fused Multiply-Add Instructions +func (c CPUInfo) AVX512IFMA() bool { + return c.Features&AVX512IFMA != 0 +} + +// AVX512PF indicates support of AVX-512 Prefetch Instructions +func (c CPUInfo) AVX512PF() bool { + return c.Features&AVX512PF != 0 +} + +// AVX512ER indicates support of AVX-512 Exponential and Reciprocal Instructions +func (c CPUInfo) AVX512ER() bool { + return c.Features&AVX512ER != 0 +} + +// AVX512CD indicates support of AVX-512 Conflict Detection Instructions +func (c CPUInfo) AVX512CD() bool { + return c.Features&AVX512CD != 0 +} + +// AVX512BW indicates support of AVX-512 Byte and Word Instructions +func (c CPUInfo) AVX512BW() bool { + return c.Features&AVX512BW != 0 +} + +// AVX512VL indicates support of AVX-512 Vector Length Extensions +func (c CPUInfo) AVX512VL() bool { + return c.Features&AVX512VL != 0 +} + +// AVX512VBMI indicates support of AVX-512 Vector Bit Manipulation Instructions +func (c CPUInfo) AVX512VBMI() bool { + return c.Features&AVX512VBMI != 0 +} + +// MPX indicates support of Intel MPX (Memory Protection Extensions) +func (c CPUInfo) MPX() bool { + return c.Features&MPX != 0 +} + +// ERMS indicates support of Enhanced REP MOVSB/STOSB +func (c CPUInfo) ERMS() bool { + return c.Features&ERMS != 0 +} + +func (c CPUInfo) RDTSCP() bool { + return c.Features&RDTSCP != 0 +} + +func (c CPUInfo) CX16() bool { + return c.Features&CX16 != 0 +} + +// Atom indicates an Atom processor +func (c CPUInfo) Atom() bool { + return c.Features&ATOM != 0 +} + +// Intel returns true if vendor is recognized as Intel +func (c CPUInfo) Intel() bool { + return c.VendorID == Intel +} + +// AMD returns true if vendor is recognized as AMD +func (c CPUInfo) AMD() bool { + return c.VendorID == AMD +} + +// Transmeta returns true if vendor is recognized as Transmeta +func (c CPUInfo) Transmeta() bool { + return c.VendorID == Transmeta +} + +// NSC returns true if vendor is recognized as National Semiconductor +func (c CPUInfo) NSC() bool { + return c.VendorID == NSC +} + +// VIA returns true if vendor is recognized as VIA +func (c CPUInfo) VIA() bool { + return c.VendorID == VIA +} + +// RTCounter returns the 64-bit time-stamp counter +// Uses the RDTSCP instruction. The value 0 is returned +// if the CPU does not support the instruction. +func (c CPUInfo) RTCounter() uint64 { + if !c.RDTSCP() { + return 0 + } + a, _, _, d := rdtscpAsm() + return uint64(a) | (uint64(d) << 32) +} + +// Ia32TscAux returns the IA32_TSC_AUX part of the RDTSCP. +// This variable is OS dependent, but on Linux contains information +// about the current cpu/core the code is running on. +// If the RDTSCP instruction isn't supported on the CPU, the value 0 is returned. +func (c CPUInfo) Ia32TscAux() uint32 { + if !c.RDTSCP() { + return 0 + } + _, _, ecx, _ := rdtscpAsm() + return ecx +} + +// LogicalCPU will return the Logical CPU the code is currently executing on. +// This is likely to change when the OS re-schedules the running thread +// to another CPU. +// If the current core cannot be detected, -1 will be returned. +func (c CPUInfo) LogicalCPU() int { + if c.maxFunc < 1 { + return -1 + } + _, ebx, _, _ := cpuid(1) + return int(ebx >> 24) +} + +// VM Will return true if the cpu id indicates we are in +// a virtual machine. This is only a hint, and will very likely +// have many false negatives. +func (c CPUInfo) VM() bool { + switch c.VendorID { + case MSVM, KVM, VMware, XenHVM: + return true + } + return false +} + +// Flags contains detected cpu features and caracteristics +type Flags uint64 + +// String returns a string representation of the detected +// CPU features. +func (f Flags) String() string { + return strings.Join(f.Strings(), ",") +} + +// Strings returns and array of the detected features. +func (f Flags) Strings() []string { + s := support() + r := make([]string, 0, 20) + for i := uint(0); i < 64; i++ { + key := Flags(1 << i) + val := flagNames[key] + if s&key != 0 { + r = append(r, val) + } + } + return r +} + +func maxExtendedFunction() uint32 { + eax, _, _, _ := cpuid(0x80000000) + return eax +} + +func maxFunctionID() uint32 { + a, _, _, _ := cpuid(0) + return a +} + +func brandName() string { + if maxExtendedFunction() >= 0x80000004 { + v := make([]uint32, 0, 48) + for i := uint32(0); i < 3; i++ { + a, b, c, d := cpuid(0x80000002 + i) + v = append(v, a, b, c, d) + } + return strings.Trim(string(valAsString(v...)), " ") + } + return "unknown" +} + +func threadsPerCore() int { + mfi := maxFunctionID() + if mfi < 0x4 || vendorID() != Intel { + return 1 + } + + if mfi < 0xb { + _, b, _, d := cpuid(1) + if (d & (1 << 28)) != 0 { + // v will contain logical core count + v := (b >> 16) & 255 + if v > 1 { + a4, _, _, _ := cpuid(4) + // physical cores + v2 := (a4 >> 26) + 1 + if v2 > 0 { + return int(v) / int(v2) + } + } + } + return 1 + } + _, b, _, _ := cpuidex(0xb, 0) + if b&0xffff == 0 { + return 1 + } + return int(b & 0xffff) +} + +func logicalCores() int { + mfi := maxFunctionID() + switch vendorID() { + case Intel: + // Use this on old Intel processors + if mfi < 0xb { + if mfi < 1 { + return 0 + } + // CPUID.1:EBX[23:16] represents the maximum number of addressable IDs (initial APIC ID) + // that can be assigned to logical processors in a physical package. + // The value may not be the same as the number of logical processors that are present in the hardware of a physical package. + _, ebx, _, _ := cpuid(1) + logical := (ebx >> 16) & 0xff + return int(logical) + } + _, b, _, _ := cpuidex(0xb, 1) + return int(b & 0xffff) + case AMD: + _, b, _, _ := cpuid(1) + return int((b >> 16) & 0xff) + default: + return 0 + } +} + +func familyModel() (int, int) { + if maxFunctionID() < 0x1 { + return 0, 0 + } + eax, _, _, _ := cpuid(1) + family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff) + model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0) + return int(family), int(model) +} + +func physicalCores() int { + switch vendorID() { + case Intel: + return logicalCores() / threadsPerCore() + case AMD: + if maxExtendedFunction() >= 0x80000008 { + _, _, c, _ := cpuid(0x80000008) + return int(c&0xff) + 1 + } + } + return 0 +} + +// Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID +var vendorMapping = map[string]Vendor{ + "AMDisbetter!": AMD, + "AuthenticAMD": AMD, + "CentaurHauls": VIA, + "GenuineIntel": Intel, + "TransmetaCPU": Transmeta, + "GenuineTMx86": Transmeta, + "Geode by NSC": NSC, + "VIA VIA VIA ": VIA, + "KVMKVMKVMKVM": KVM, + "Microsoft Hv": MSVM, + "VMwareVMware": VMware, + "XenVMMXenVMM": XenHVM, +} + +func vendorID() Vendor { + _, b, c, d := cpuid(0) + v := valAsString(b, d, c) + vend, ok := vendorMapping[string(v)] + if !ok { + return Other + } + return vend +} + +func cacheLine() int { + if maxFunctionID() < 0x1 { + return 0 + } + + _, ebx, _, _ := cpuid(1) + cache := (ebx & 0xff00) >> 5 // cflush size + if cache == 0 && maxExtendedFunction() >= 0x80000006 { + _, _, ecx, _ := cpuid(0x80000006) + cache = ecx & 0xff // cacheline size + } + // TODO: Read from Cache and TLB Information + return int(cache) +} + +func (c *CPUInfo) cacheSize() { + c.Cache.L1D = -1 + c.Cache.L1I = -1 + c.Cache.L2 = -1 + c.Cache.L3 = -1 + vendor := vendorID() + switch vendor { + case Intel: + if maxFunctionID() < 4 { + return + } + for i := uint32(0); ; i++ { + eax, ebx, ecx, _ := cpuidex(4, i) + cacheType := eax & 15 + if cacheType == 0 { + break + } + cacheLevel := (eax >> 5) & 7 + coherency := int(ebx&0xfff) + 1 + partitions := int((ebx>>12)&0x3ff) + 1 + associativity := int((ebx>>22)&0x3ff) + 1 + sets := int(ecx) + 1 + size := associativity * partitions * coherency * sets + switch cacheLevel { + case 1: + if cacheType == 1 { + // 1 = Data Cache + c.Cache.L1D = size + } else if cacheType == 2 { + // 2 = Instruction Cache + c.Cache.L1I = size + } else { + if c.Cache.L1D < 0 { + c.Cache.L1I = size + } + if c.Cache.L1I < 0 { + c.Cache.L1I = size + } + } + case 2: + c.Cache.L2 = size + case 3: + c.Cache.L3 = size + } + } + case AMD: + // Untested. + if maxExtendedFunction() < 0x80000005 { + return + } + _, _, ecx, edx := cpuid(0x80000005) + c.Cache.L1D = int(((ecx >> 24) & 0xFF) * 1024) + c.Cache.L1I = int(((edx >> 24) & 0xFF) * 1024) + + if maxExtendedFunction() < 0x80000006 { + return + } + _, _, ecx, _ = cpuid(0x80000006) + c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024) + } + + return +} + +type SGXSupport struct { + Available bool + SGX1Supported bool + SGX2Supported bool + MaxEnclaveSizeNot64 int64 + MaxEnclaveSize64 int64 +} + +func sgx(available bool) (rval SGXSupport) { + rval.Available = available + + if !available { + return + } + + a, _, _, d := cpuidex(0x12, 0) + rval.SGX1Supported = a&0x01 != 0 + rval.SGX2Supported = a&0x02 != 0 + rval.MaxEnclaveSizeNot64 = 1 << (d & 0xFF) // pow 2 + rval.MaxEnclaveSize64 = 1 << ((d >> 8) & 0xFF) // pow 2 + + return +} + +func support() Flags { + mfi := maxFunctionID() + vend := vendorID() + if mfi < 0x1 { + return 0 + } + rval := uint64(0) + _, _, c, d := cpuid(1) + if (d & (1 << 15)) != 0 { + rval |= CMOV + } + if (d & (1 << 23)) != 0 { + rval |= MMX + } + if (d & (1 << 25)) != 0 { + rval |= MMXEXT + } + if (d & (1 << 25)) != 0 { + rval |= SSE + } + if (d & (1 << 26)) != 0 { + rval |= SSE2 + } + if (c & 1) != 0 { + rval |= SSE3 + } + if (c & 0x00000200) != 0 { + rval |= SSSE3 + } + if (c & 0x00080000) != 0 { + rval |= SSE4 + } + if (c & 0x00100000) != 0 { + rval |= SSE42 + } + if (c & (1 << 25)) != 0 { + rval |= AESNI + } + if (c & (1 << 1)) != 0 { + rval |= CLMUL + } + if c&(1<<23) != 0 { + rval |= POPCNT + } + if c&(1<<30) != 0 { + rval |= RDRAND + } + if c&(1<<29) != 0 { + rval |= F16C + } + if c&(1<<13) != 0 { + rval |= CX16 + } + if vend == Intel && (d&(1<<28)) != 0 && mfi >= 4 { + if threadsPerCore() > 1 { + rval |= HTT + } + } + + // Check XGETBV, OXSAVE and AVX bits + if c&(1<<26) != 0 && c&(1<<27) != 0 && c&(1<<28) != 0 { + // Check for OS support + eax, _ := xgetbv(0) + if (eax & 0x6) == 0x6 { + rval |= AVX + if (c & 0x00001000) != 0 { + rval |= FMA3 + } + } + } + + // Check AVX2, AVX2 requires OS support, but BMI1/2 don't. + if mfi >= 7 { + _, ebx, ecx, _ := cpuidex(7, 0) + if (rval&AVX) != 0 && (ebx&0x00000020) != 0 { + rval |= AVX2 + } + if (ebx & 0x00000008) != 0 { + rval |= BMI1 + if (ebx & 0x00000100) != 0 { + rval |= BMI2 + } + } + if ebx&(1<<2) != 0 { + rval |= SGX + } + if ebx&(1<<4) != 0 { + rval |= HLE + } + if ebx&(1<<9) != 0 { + rval |= ERMS + } + if ebx&(1<<11) != 0 { + rval |= RTM + } + if ebx&(1<<14) != 0 { + rval |= MPX + } + if ebx&(1<<18) != 0 { + rval |= RDSEED + } + if ebx&(1<<19) != 0 { + rval |= ADX + } + if ebx&(1<<29) != 0 { + rval |= SHA + } + + // Only detect AVX-512 features if XGETBV is supported + if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) { + // Check for OS support + eax, _ := xgetbv(0) + + // Verify that XCR0[7:5] = ‘111b’ (OPMASK state, upper 256-bit of ZMM0-ZMM15 and + // ZMM16-ZMM31 state are enabled by OS) + /// and that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). + if (eax>>5)&7 == 7 && (eax>>1)&3 == 3 { + if ebx&(1<<16) != 0 { + rval |= AVX512F + } + if ebx&(1<<17) != 0 { + rval |= AVX512DQ + } + if ebx&(1<<21) != 0 { + rval |= AVX512IFMA + } + if ebx&(1<<26) != 0 { + rval |= AVX512PF + } + if ebx&(1<<27) != 0 { + rval |= AVX512ER + } + if ebx&(1<<28) != 0 { + rval |= AVX512CD + } + if ebx&(1<<30) != 0 { + rval |= AVX512BW + } + if ebx&(1<<31) != 0 { + rval |= AVX512VL + } + // ecx + if ecx&(1<<1) != 0 { + rval |= AVX512VBMI + } + } + } + } + + if maxExtendedFunction() >= 0x80000001 { + _, _, c, d := cpuid(0x80000001) + if (c & (1 << 5)) != 0 { + rval |= LZCNT + rval |= POPCNT + } + if (d & (1 << 31)) != 0 { + rval |= AMD3DNOW + } + if (d & (1 << 30)) != 0 { + rval |= AMD3DNOWEXT + } + if (d & (1 << 23)) != 0 { + rval |= MMX + } + if (d & (1 << 22)) != 0 { + rval |= MMXEXT + } + if (c & (1 << 6)) != 0 { + rval |= SSE4A + } + if d&(1<<20) != 0 { + rval |= NX + } + if d&(1<<27) != 0 { + rval |= RDTSCP + } + + /* Allow for selectively disabling SSE2 functions on AMD processors + with SSE2 support but not SSE4a. This includes Athlon64, some + Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster + than SSE2 often enough to utilize this special-case flag. + AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case + so that SSE2 is used unless explicitly disabled by checking + AV_CPU_FLAG_SSE2SLOW. */ + if vendorID() != Intel && + rval&SSE2 != 0 && (c&0x00000040) == 0 { + rval |= SSE2SLOW + } + + /* XOP and FMA4 use the AVX instruction coding scheme, so they can't be + * used unless the OS has AVX support. */ + if (rval & AVX) != 0 { + if (c & 0x00000800) != 0 { + rval |= XOP + } + if (c & 0x00010000) != 0 { + rval |= FMA4 + } + } + + if vendorID() == Intel { + family, model := familyModel() + if family == 6 && (model == 9 || model == 13 || model == 14) { + /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and + * 6/14 (core1 "yonah") theoretically support sse2, but it's + * usually slower than mmx. */ + if (rval & SSE2) != 0 { + rval |= SSE2SLOW + } + if (rval & SSE3) != 0 { + rval |= SSE3SLOW + } + } + /* The Atom processor has SSSE3 support, which is useful in many cases, + * but sometimes the SSSE3 version is slower than the SSE2 equivalent + * on the Atom, but is generally faster on other processors supporting + * SSSE3. This flag allows for selectively disabling certain SSSE3 + * functions on the Atom. */ + if family == 6 && model == 28 { + rval |= ATOM + } + } + } + return Flags(rval) +} + +func valAsString(values ...uint32) []byte { + r := make([]byte, 4*len(values)) + for i, v := range values { + dst := r[i*4:] + dst[0] = byte(v & 0xff) + dst[1] = byte((v >> 8) & 0xff) + dst[2] = byte((v >> 16) & 0xff) + dst[3] = byte((v >> 24) & 0xff) + switch { + case dst[0] == 0: + return r[:i*4] + case dst[1] == 0: + return r[:i*4+1] + case dst[2] == 0: + return r[:i*4+2] + case dst[3] == 0: + return r[:i*4+3] + } + } + return r +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_386.s b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_386.s new file mode 100644 index 00000000000..9947f7b6f6a --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_386.s @@ -0,0 +1,40 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuid(SB), 7, $0 + XORL CX, CX + MOVL op+0(FP), AX + CPUID + MOVL AX, eax+4(FP) + MOVL BX, ebx+8(FP) + MOVL CX, ecx+12(FP) + MOVL DX, edx+16(FP) + RET + +// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuidex(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 + +// func xgetbv(index uint32) (eax, edx uint32) +TEXT ·asmXgetbv(SB), 7, $0 + MOVL index+0(FP), CX + BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV + MOVL AX, eax+4(FP) + MOVL DX, edx+8(FP) + RET + +// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) +TEXT ·asmRdtscpAsm(SB), 7, $0 + BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP + MOVL AX, eax+0(FP) + MOVL BX, ebx+4(FP) + MOVL CX, ecx+8(FP) + MOVL DX, edx+12(FP) + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_amd64.s b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_amd64.s new file mode 100644 index 00000000000..68a7c9d8808 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/cpuid_amd64.s @@ -0,0 +1,40 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuid(SB), 7, $0 + XORQ CX, CX + 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 asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuidex(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 + +// func asmXgetbv(index uint32) (eax, edx uint32) +TEXT ·asmXgetbv(SB), 7, $0 + MOVL index+0(FP), CX + BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV + MOVL AX, eax+8(FP) + MOVL DX, edx+12(FP) + RET + +// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) +TEXT ·asmRdtscpAsm(SB), 7, $0 + BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP + MOVL AX, eax+0(FP) + MOVL BX, ebx+4(FP) + MOVL CX, ecx+8(FP) + MOVL DX, edx+12(FP) + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_intel.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_intel.go new file mode 100644 index 00000000000..d5475ebfaa8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_intel.go @@ -0,0 +1,17 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// +build 386 amd64 + +package cpuid + +func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +func asmXgetbv(index uint32) (eax, edx uint32) +func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) + +func initCPU() { + cpuid = asmCpuid + cpuidex = asmCpuidex + xgetbv = asmXgetbv + rdtscpAsm = asmRdtscpAsm +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_ref.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_ref.go new file mode 100644 index 00000000000..6f9231b13bf --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/detect_ref.go @@ -0,0 +1,23 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// +build !amd64,!386 + +package cpuid + +func initCPU() { + cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } + + cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } + + xgetbv = func(index uint32) (eax, edx uint32) { + return 0, 0 + } + + rdtscpAsm = func() (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/generate.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/generate.go new file mode 100644 index 00000000000..c060b8165e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/generate.go @@ -0,0 +1,3 @@ +package cpuid + +//go:generate go run private-gen.go diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private-gen.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/private-gen.go new file mode 100644 index 00000000000..437333d2922 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private-gen.go @@ -0,0 +1,476 @@ +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io" + "io/ioutil" + "log" + "os" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +var inFiles = []string{"cpuid.go", "cpuid_test.go"} +var copyFiles = []string{"cpuid_amd64.s", "cpuid_386.s", "detect_ref.go", "detect_intel.go"} +var fileSet = token.NewFileSet() +var reWrites = []rewrite{ + initRewrite("CPUInfo -> cpuInfo"), + initRewrite("Vendor -> vendor"), + initRewrite("Flags -> flags"), + initRewrite("Detect -> detect"), + initRewrite("CPU -> cpu"), +} +var excludeNames = map[string]bool{"string": true, "join": true, "trim": true, + // cpuid_test.go + "t": true, "println": true, "logf": true, "log": true, "fatalf": true, "fatal": true, +} + +var excludePrefixes = []string{"test", "benchmark"} + +func main() { + Package := "private" + parserMode := parser.ParseComments + exported := make(map[string]rewrite) + for _, file := range inFiles { + in, err := os.Open(file) + if err != nil { + log.Fatalf("opening input", err) + } + + src, err := ioutil.ReadAll(in) + if err != nil { + log.Fatalf("reading input", err) + } + + astfile, err := parser.ParseFile(fileSet, file, src, parserMode) + if err != nil { + log.Fatalf("parsing input", err) + } + + for _, rw := range reWrites { + astfile = rw(astfile) + } + + // Inspect the AST and print all identifiers and literals. + var startDecl token.Pos + var endDecl token.Pos + ast.Inspect(astfile, func(n ast.Node) bool { + var s string + switch x := n.(type) { + case *ast.Ident: + if x.IsExported() { + t := strings.ToLower(x.Name) + for _, pre := range excludePrefixes { + if strings.HasPrefix(t, pre) { + return true + } + } + if excludeNames[t] != true { + //if x.Pos() > startDecl && x.Pos() < endDecl { + exported[x.Name] = initRewrite(x.Name + " -> " + t) + } + } + + case *ast.GenDecl: + if x.Tok == token.CONST && x.Lparen > 0 { + startDecl = x.Lparen + endDecl = x.Rparen + // fmt.Printf("Decl:%s -> %s\n", fileSet.Position(startDecl), fileSet.Position(endDecl)) + } + } + if s != "" { + fmt.Printf("%s:\t%s\n", fileSet.Position(n.Pos()), s) + } + return true + }) + + for _, rw := range exported { + astfile = rw(astfile) + } + + var buf bytes.Buffer + + printer.Fprint(&buf, fileSet, astfile) + + // Remove package documentation and insert information + s := buf.String() + ind := strings.Index(buf.String(), "\npackage cpuid") + s = s[ind:] + s = "// Generated, DO NOT EDIT,\n" + + "// but copy it to your own project and rename the package.\n" + + "// See more at http://github.com/klauspost/cpuid\n" + + s + + outputName := Package + string(os.PathSeparator) + file + + err = ioutil.WriteFile(outputName, []byte(s), 0644) + if err != nil { + log.Fatalf("writing output: %s", err) + } + log.Println("Generated", outputName) + } + + for _, file := range copyFiles { + dst := "" + if strings.HasPrefix(file, "cpuid") { + dst = Package + string(os.PathSeparator) + file + } else { + dst = Package + string(os.PathSeparator) + "cpuid_" + file + } + err := copyFile(file, dst) + if err != nil { + log.Fatalf("copying file: %s", err) + } + log.Println("Copied", dst) + } +} + +// CopyFile copies a file from src to dst. If src and dst files exist, and are +// the same, then return success. Copy the file contents from src to dst. +func copyFile(src, dst string) (err error) { + sfi, err := os.Stat(src) + if err != nil { + return + } + if !sfi.Mode().IsRegular() { + // cannot copy non-regular files (e.g., directories, + // symlinks, devices, etc.) + return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String()) + } + dfi, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return + } + } else { + if !(dfi.Mode().IsRegular()) { + return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return + } + } + err = copyFileContents(src, dst) + return +} + +// copyFileContents copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. +func copyFileContents(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + cerr := out.Close() + if err == nil { + err = cerr + } + }() + if _, err = io.Copy(out, in); err != nil { + return + } + err = out.Sync() + return +} + +type rewrite func(*ast.File) *ast.File + +// Mostly copied from gofmt +func initRewrite(rewriteRule string) rewrite { + f := strings.Split(rewriteRule, "->") + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n") + os.Exit(2) + } + pattern := parseExpr(f[0], "pattern") + replace := parseExpr(f[1], "replacement") + return func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) } +} + +// parseExpr parses s as an expression. +// It might make sense to expand this to allow statement patterns, +// but there are problems with preserving formatting and also +// with what a wildcard for a statement looks like. +func parseExpr(s, what string) ast.Expr { + x, err := parser.ParseExpr(s) + if err != nil { + fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err) + os.Exit(2) + } + return x +} + +// Keep this function for debugging. +/* +func dump(msg string, val reflect.Value) { + fmt.Printf("%s:\n", msg) + ast.Print(fileSet, val.Interface()) + fmt.Println() +} +*/ + +// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. +func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { + cmap := ast.NewCommentMap(fileSet, p, p.Comments) + m := make(map[string]reflect.Value) + pat := reflect.ValueOf(pattern) + repl := reflect.ValueOf(replace) + + var rewriteVal func(val reflect.Value) reflect.Value + rewriteVal = func(val reflect.Value) reflect.Value { + // don't bother if val is invalid to start with + if !val.IsValid() { + return reflect.Value{} + } + for k := range m { + delete(m, k) + } + val = apply(rewriteVal, val) + if match(m, pat, val) { + val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) + } + return val + } + + r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File) + r.Comments = cmap.Filter(r).Comments() // recreate comments list + return r +} + +// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y. +func set(x, y reflect.Value) { + // don't bother if x cannot be set or y is invalid + if !x.CanSet() || !y.IsValid() { + return + } + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && + (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) { + // x cannot be set to y - ignore this rewrite + return + } + panic(x) + } + }() + x.Set(y) +} + +// Values/types for special cases. +var ( + objectPtrNil = reflect.ValueOf((*ast.Object)(nil)) + scopePtrNil = reflect.ValueOf((*ast.Scope)(nil)) + + identType = reflect.TypeOf((*ast.Ident)(nil)) + objectPtrType = reflect.TypeOf((*ast.Object)(nil)) + positionType = reflect.TypeOf(token.NoPos) + callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) + scopePtrType = reflect.TypeOf((*ast.Scope)(nil)) +) + +// apply replaces each AST field x in val with f(x), returning val. +// To avoid extra conversions, f operates on the reflect.Value form. +func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value { + if !val.IsValid() { + return reflect.Value{} + } + + // *ast.Objects introduce cycles and are likely incorrect after + // rewrite; don't follow them but replace with nil instead + if val.Type() == objectPtrType { + return objectPtrNil + } + + // similarly for scopes: they are likely incorrect after a rewrite; + // replace them with nil + if val.Type() == scopePtrType { + return scopePtrNil + } + + switch v := reflect.Indirect(val); v.Kind() { + case reflect.Slice: + for i := 0; i < v.Len(); i++ { + e := v.Index(i) + set(e, f(e)) + } + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + e := v.Field(i) + set(e, f(e)) + } + case reflect.Interface: + e := v.Elem() + set(v, f(e)) + } + return val +} + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + +// match returns true if pattern matches val, +// recording wildcard submatches in m. +// If m == nil, match checks whether pattern == val. +func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { + // Wildcard matches any expression. If it appears multiple + // times in the pattern, it must match the same expression + // each time. + if m != nil && pattern.IsValid() && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) && val.IsValid() { + // wildcards only match valid (non-nil) expressions. + if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() { + if old, ok := m[name]; ok { + return match(nil, old, val) + } + m[name] = val + return true + } + } + } + + // Otherwise, pattern and val must match recursively. + if !pattern.IsValid() || !val.IsValid() { + return !pattern.IsValid() && !val.IsValid() + } + if pattern.Type() != val.Type() { + return false + } + + // Special cases. + switch pattern.Type() { + case identType: + // For identifiers, only the names need to match + // (and none of the other *ast.Object information). + // This is a common case, handle it all here instead + // of recursing down any further via reflection. + p := pattern.Interface().(*ast.Ident) + v := val.Interface().(*ast.Ident) + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name + case objectPtrType, positionType: + // object pointers and token positions always match + return true + case callExprType: + // For calls, the Ellipsis fields (token.Position) must + // match since that is how f(x) and f(x...) are different. + // Check them here but fall through for the remaining fields. + p := pattern.Interface().(*ast.CallExpr) + v := val.Interface().(*ast.CallExpr) + if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() { + return false + } + } + + p := reflect.Indirect(pattern) + v := reflect.Indirect(val) + if !p.IsValid() || !v.IsValid() { + return !p.IsValid() && !v.IsValid() + } + + switch p.Kind() { + case reflect.Slice: + if p.Len() != v.Len() { + return false + } + for i := 0; i < p.Len(); i++ { + if !match(m, p.Index(i), v.Index(i)) { + return false + } + } + return true + + case reflect.Struct: + for i := 0; i < p.NumField(); i++ { + if !match(m, p.Field(i), v.Field(i)) { + return false + } + } + return true + + case reflect.Interface: + return match(m, p.Elem(), v.Elem()) + } + + // Handle token integers, etc. + return p.Interface() == v.Interface() +} + +// subst returns a copy of pattern with values from m substituted in place +// of wildcards and pos used as the position of tokens from the pattern. +// if m == nil, subst returns a copy of pattern and doesn't change the line +// number information. +func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value { + if !pattern.IsValid() { + return reflect.Value{} + } + + // Wildcard gets replaced with map value. + if m != nil && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) { + if old, ok := m[name]; ok { + return subst(nil, old, reflect.Value{}) + } + } + } + + if pos.IsValid() && pattern.Type() == positionType { + // use new position only if old position was valid in the first place + if old := pattern.Interface().(token.Pos); !old.IsValid() { + return pattern + } + return pos + } + + // Otherwise copy. + switch p := pattern; p.Kind() { + case reflect.Slice: + v := reflect.MakeSlice(p.Type(), p.Len(), p.Len()) + for i := 0; i < p.Len(); i++ { + v.Index(i).Set(subst(m, p.Index(i), pos)) + } + return v + + case reflect.Struct: + v := reflect.New(p.Type()).Elem() + for i := 0; i < p.NumField(); i++ { + v.Field(i).Set(subst(m, p.Field(i), pos)) + } + return v + + case reflect.Ptr: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos).Addr()) + } + return v + + case reflect.Interface: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos)) + } + return v + } + + return pattern +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/README.md b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/README.md new file mode 100644 index 00000000000..57a68f88274 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/README.md @@ -0,0 +1,6 @@ +# cpuid private + +This is a specially converted of the cpuid package, so it can be included in +a package without exporting anything. + +Package home: https://github.com/klauspost/cpuid diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid.go new file mode 100644 index 00000000000..be99cb0b70d --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid.go @@ -0,0 +1,987 @@ +// Generated, DO NOT EDIT, +// but copy it to your own project and rename the package. +// See more at http://github.com/klauspost/cpuid + +package cpuid + +import ( + "strings" +) + +// Vendor is a representation of a CPU vendor. +type vendor int + +const ( + other vendor = iota + intel + amd + via + transmeta + nsc + kvm // Kernel-based Virtual Machine + msvm // Microsoft Hyper-V or Windows Virtual PC + vmware + xenhvm +) + +const ( + cmov = 1 << iota // i686 CMOV + nx // NX (No-Execute) bit + amd3dnow // AMD 3DNOW + amd3dnowext // AMD 3DNowExt + mmx // standard MMX + mmxext // SSE integer functions or AMD MMX ext + sse // SSE functions + sse2 // P4 SSE functions + sse3 // Prescott SSE3 functions + ssse3 // Conroe SSSE3 functions + sse4 // Penryn SSE4.1 functions + sse4a // AMD Barcelona microarchitecture SSE4a instructions + sse42 // Nehalem SSE4.2 functions + avx // AVX functions + avx2 // AVX2 functions + fma3 // Intel FMA 3 + fma4 // Bulldozer FMA4 functions + xop // Bulldozer XOP functions + f16c // Half-precision floating-point conversion + bmi1 // Bit Manipulation Instruction Set 1 + bmi2 // Bit Manipulation Instruction Set 2 + tbm // AMD Trailing Bit Manipulation + lzcnt // LZCNT instruction + popcnt // POPCNT instruction + aesni // Advanced Encryption Standard New Instructions + clmul // Carry-less Multiplication + htt // Hyperthreading (enabled) + hle // Hardware Lock Elision + rtm // Restricted Transactional Memory + rdrand // RDRAND instruction is available + rdseed // RDSEED instruction is available + adx // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + sha // Intel SHA Extensions + avx512f // AVX-512 Foundation + avx512dq // AVX-512 Doubleword and Quadword Instructions + avx512ifma // AVX-512 Integer Fused Multiply-Add Instructions + avx512pf // AVX-512 Prefetch Instructions + avx512er // AVX-512 Exponential and Reciprocal Instructions + avx512cd // AVX-512 Conflict Detection Instructions + avx512bw // AVX-512 Byte and Word Instructions + avx512vl // AVX-512 Vector Length Extensions + avx512vbmi // AVX-512 Vector Bit Manipulation Instructions + mpx // Intel MPX (Memory Protection Extensions) + erms // Enhanced REP MOVSB/STOSB + rdtscp // RDTSCP Instruction + cx16 // CMPXCHG16B Instruction + + // Performance indicators + sse2slow // SSE2 is supported, but usually not faster + sse3slow // SSE3 is supported, but usually not faster + atom // Atom processor, some SSSE3 instructions are slower +) + +var flagNames = map[flags]string{ + cmov: "CMOV", // i686 CMOV + nx: "NX", // NX (No-Execute) bit + amd3dnow: "AMD3DNOW", // AMD 3DNOW + amd3dnowext: "AMD3DNOWEXT", // AMD 3DNowExt + mmx: "MMX", // Standard MMX + mmxext: "MMXEXT", // SSE integer functions or AMD MMX ext + sse: "SSE", // SSE functions + sse2: "SSE2", // P4 SSE2 functions + sse3: "SSE3", // Prescott SSE3 functions + ssse3: "SSSE3", // Conroe SSSE3 functions + sse4: "SSE4.1", // Penryn SSE4.1 functions + sse4a: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions + sse42: "SSE4.2", // Nehalem SSE4.2 functions + avx: "AVX", // AVX functions + avx2: "AVX2", // AVX functions + fma3: "FMA3", // Intel FMA 3 + fma4: "FMA4", // Bulldozer FMA4 functions + xop: "XOP", // Bulldozer XOP functions + f16c: "F16C", // Half-precision floating-point conversion + bmi1: "BMI1", // Bit Manipulation Instruction Set 1 + bmi2: "BMI2", // Bit Manipulation Instruction Set 2 + tbm: "TBM", // AMD Trailing Bit Manipulation + lzcnt: "LZCNT", // LZCNT instruction + popcnt: "POPCNT", // POPCNT instruction + aesni: "AESNI", // Advanced Encryption Standard New Instructions + clmul: "CLMUL", // Carry-less Multiplication + htt: "HTT", // Hyperthreading (enabled) + hle: "HLE", // Hardware Lock Elision + rtm: "RTM", // Restricted Transactional Memory + rdrand: "RDRAND", // RDRAND instruction is available + rdseed: "RDSEED", // RDSEED instruction is available + adx: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) + sha: "SHA", // Intel SHA Extensions + avx512f: "AVX512F", // AVX-512 Foundation + avx512dq: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions + avx512ifma: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions + avx512pf: "AVX512PF", // AVX-512 Prefetch Instructions + avx512er: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions + avx512cd: "AVX512CD", // AVX-512 Conflict Detection Instructions + avx512bw: "AVX512BW", // AVX-512 Byte and Word Instructions + avx512vl: "AVX512VL", // AVX-512 Vector Length Extensions + avx512vbmi: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions + mpx: "MPX", // Intel MPX (Memory Protection Extensions) + erms: "ERMS", // Enhanced REP MOVSB/STOSB + rdtscp: "RDTSCP", // RDTSCP Instruction + cx16: "CX16", // CMPXCHG16B Instruction + + // Performance indicators + sse2slow: "SSE2SLOW", // SSE2 supported, but usually not faster + sse3slow: "SSE3SLOW", // SSE3 supported, but usually not faster + atom: "ATOM", // Atom processor, some SSSE3 instructions are slower + +} + +// CPUInfo contains information about the detected system CPU. +type cpuInfo struct { + brandname string // Brand name reported by the CPU + vendorid vendor // Comparable CPU vendor ID + features flags // Features of the CPU + physicalcores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. + threadspercore int // Number of threads per physical core. Will be 1 if undetectable. + logicalcores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable. + family int // CPU family number + model int // CPU model number + cacheline int // Cache line size in bytes. Will be 0 if undetectable. + cache struct { + l1i int // L1 Instruction Cache (per core or shared). Will be -1 if undetected + l1d int // L1 Data Cache (per core or shared). Will be -1 if undetected + l2 int // L2 Cache (per core or shared). Will be -1 if undetected + l3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected + } + maxFunc uint32 + maxExFunc uint32 +} + +var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) +var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) +var xgetbv func(index uint32) (eax, edx uint32) +var rdtscpAsm func() (eax, ebx, ecx, edx uint32) + +// CPU contains information about the CPU as detected on startup, +// or when Detect last was called. +// +// Use this as the primary entry point to you data, +// this way queries are +var cpu cpuInfo + +func init() { + initCPU() + detect() +} + +// Detect will re-detect current CPU info. +// This will replace the content of the exported CPU variable. +// +// Unless you expect the CPU to change while you are running your program +// you should not need to call this function. +// If you call this, you must ensure that no other goroutine is accessing the +// exported CPU variable. +func detect() { + cpu.maxFunc = maxFunctionID() + cpu.maxExFunc = maxExtendedFunction() + cpu.brandname = brandName() + cpu.cacheline = cacheLine() + cpu.family, cpu.model = familyModel() + cpu.features = support() + cpu.threadspercore = threadsPerCore() + cpu.logicalcores = logicalCores() + cpu.physicalcores = physicalCores() + cpu.vendorid = vendorID() + cpu.cacheSize() +} + +// Generated here: http://play.golang.org/p/BxFH2Gdc0G + +// Cmov indicates support of CMOV instructions +func (c cpuInfo) cmov() bool { + return c.features&cmov != 0 +} + +// Amd3dnow indicates support of AMD 3DNOW! instructions +func (c cpuInfo) amd3dnow() bool { + return c.features&amd3dnow != 0 +} + +// Amd3dnowExt indicates support of AMD 3DNOW! Extended instructions +func (c cpuInfo) amd3dnowext() bool { + return c.features&amd3dnowext != 0 +} + +// MMX indicates support of MMX instructions +func (c cpuInfo) mmx() bool { + return c.features&mmx != 0 +} + +// MMXExt indicates support of MMXEXT instructions +// (SSE integer functions or AMD MMX ext) +func (c cpuInfo) mmxext() bool { + return c.features&mmxext != 0 +} + +// SSE indicates support of SSE instructions +func (c cpuInfo) sse() bool { + return c.features&sse != 0 +} + +// SSE2 indicates support of SSE 2 instructions +func (c cpuInfo) sse2() bool { + return c.features&sse2 != 0 +} + +// SSE3 indicates support of SSE 3 instructions +func (c cpuInfo) sse3() bool { + return c.features&sse3 != 0 +} + +// SSSE3 indicates support of SSSE 3 instructions +func (c cpuInfo) ssse3() bool { + return c.features&ssse3 != 0 +} + +// SSE4 indicates support of SSE 4 (also called SSE 4.1) instructions +func (c cpuInfo) sse4() bool { + return c.features&sse4 != 0 +} + +// SSE42 indicates support of SSE4.2 instructions +func (c cpuInfo) sse42() bool { + return c.features&sse42 != 0 +} + +// AVX indicates support of AVX instructions +// and operating system support of AVX instructions +func (c cpuInfo) avx() bool { + return c.features&avx != 0 +} + +// AVX2 indicates support of AVX2 instructions +func (c cpuInfo) avx2() bool { + return c.features&avx2 != 0 +} + +// FMA3 indicates support of FMA3 instructions +func (c cpuInfo) fma3() bool { + return c.features&fma3 != 0 +} + +// FMA4 indicates support of FMA4 instructions +func (c cpuInfo) fma4() bool { + return c.features&fma4 != 0 +} + +// XOP indicates support of XOP instructions +func (c cpuInfo) xop() bool { + return c.features&xop != 0 +} + +// F16C indicates support of F16C instructions +func (c cpuInfo) f16c() bool { + return c.features&f16c != 0 +} + +// BMI1 indicates support of BMI1 instructions +func (c cpuInfo) bmi1() bool { + return c.features&bmi1 != 0 +} + +// BMI2 indicates support of BMI2 instructions +func (c cpuInfo) bmi2() bool { + return c.features&bmi2 != 0 +} + +// TBM indicates support of TBM instructions +// (AMD Trailing Bit Manipulation) +func (c cpuInfo) tbm() bool { + return c.features&tbm != 0 +} + +// Lzcnt indicates support of LZCNT instruction +func (c cpuInfo) lzcnt() bool { + return c.features&lzcnt != 0 +} + +// Popcnt indicates support of POPCNT instruction +func (c cpuInfo) popcnt() bool { + return c.features&popcnt != 0 +} + +// HTT indicates the processor has Hyperthreading enabled +func (c cpuInfo) htt() bool { + return c.features&htt != 0 +} + +// SSE2Slow indicates that SSE2 may be slow on this processor +func (c cpuInfo) sse2slow() bool { + return c.features&sse2slow != 0 +} + +// SSE3Slow indicates that SSE3 may be slow on this processor +func (c cpuInfo) sse3slow() bool { + return c.features&sse3slow != 0 +} + +// AesNi indicates support of AES-NI instructions +// (Advanced Encryption Standard New Instructions) +func (c cpuInfo) aesni() bool { + return c.features&aesni != 0 +} + +// Clmul indicates support of CLMUL instructions +// (Carry-less Multiplication) +func (c cpuInfo) clmul() bool { + return c.features&clmul != 0 +} + +// NX indicates support of NX (No-Execute) bit +func (c cpuInfo) nx() bool { + return c.features&nx != 0 +} + +// SSE4A indicates support of AMD Barcelona microarchitecture SSE4a instructions +func (c cpuInfo) sse4a() bool { + return c.features&sse4a != 0 +} + +// HLE indicates support of Hardware Lock Elision +func (c cpuInfo) hle() bool { + return c.features&hle != 0 +} + +// RTM indicates support of Restricted Transactional Memory +func (c cpuInfo) rtm() bool { + return c.features&rtm != 0 +} + +// Rdrand indicates support of RDRAND instruction is available +func (c cpuInfo) rdrand() bool { + return c.features&rdrand != 0 +} + +// Rdseed indicates support of RDSEED instruction is available +func (c cpuInfo) rdseed() bool { + return c.features&rdseed != 0 +} + +// ADX indicates support of Intel ADX (Multi-Precision Add-Carry Instruction Extensions) +func (c cpuInfo) adx() bool { + return c.features&adx != 0 +} + +// SHA indicates support of Intel SHA Extensions +func (c cpuInfo) sha() bool { + return c.features&sha != 0 +} + +// AVX512F indicates support of AVX-512 Foundation +func (c cpuInfo) avx512f() bool { + return c.features&avx512f != 0 +} + +// AVX512DQ indicates support of AVX-512 Doubleword and Quadword Instructions +func (c cpuInfo) avx512dq() bool { + return c.features&avx512dq != 0 +} + +// AVX512IFMA indicates support of AVX-512 Integer Fused Multiply-Add Instructions +func (c cpuInfo) avx512ifma() bool { + return c.features&avx512ifma != 0 +} + +// AVX512PF indicates support of AVX-512 Prefetch Instructions +func (c cpuInfo) avx512pf() bool { + return c.features&avx512pf != 0 +} + +// AVX512ER indicates support of AVX-512 Exponential and Reciprocal Instructions +func (c cpuInfo) avx512er() bool { + return c.features&avx512er != 0 +} + +// AVX512CD indicates support of AVX-512 Conflict Detection Instructions +func (c cpuInfo) avx512cd() bool { + return c.features&avx512cd != 0 +} + +// AVX512BW indicates support of AVX-512 Byte and Word Instructions +func (c cpuInfo) avx512bw() bool { + return c.features&avx512bw != 0 +} + +// AVX512VL indicates support of AVX-512 Vector Length Extensions +func (c cpuInfo) avx512vl() bool { + return c.features&avx512vl != 0 +} + +// AVX512VBMI indicates support of AVX-512 Vector Bit Manipulation Instructions +func (c cpuInfo) avx512vbmi() bool { + return c.features&avx512vbmi != 0 +} + +// MPX indicates support of Intel MPX (Memory Protection Extensions) +func (c cpuInfo) mpx() bool { + return c.features&mpx != 0 +} + +// ERMS indicates support of Enhanced REP MOVSB/STOSB +func (c cpuInfo) erms() bool { + return c.features&erms != 0 +} + +func (c cpuInfo) rdtscp() bool { + return c.features&rdtscp != 0 +} + +func (c cpuInfo) cx16() bool { + return c.features&cx16 != 0 +} + +// Atom indicates an Atom processor +func (c cpuInfo) atom() bool { + return c.features&atom != 0 +} + +// Intel returns true if vendor is recognized as Intel +func (c cpuInfo) intel() bool { + return c.vendorid == intel +} + +// AMD returns true if vendor is recognized as AMD +func (c cpuInfo) amd() bool { + return c.vendorid == amd +} + +// Transmeta returns true if vendor is recognized as Transmeta +func (c cpuInfo) transmeta() bool { + return c.vendorid == transmeta +} + +// NSC returns true if vendor is recognized as National Semiconductor +func (c cpuInfo) nsc() bool { + return c.vendorid == nsc +} + +// VIA returns true if vendor is recognized as VIA +func (c cpuInfo) via() bool { + return c.vendorid == via +} + +// RTCounter returns the 64-bit time-stamp counter +// Uses the RDTSCP instruction. The value 0 is returned +// if the CPU does not support the instruction. +func (c cpuInfo) rtcounter() uint64 { + if !c.rdtscp() { + return 0 + } + a, _, _, d := rdtscpAsm() + return uint64(a) | (uint64(d) << 32) +} + +// Ia32TscAux returns the IA32_TSC_AUX part of the RDTSCP. +// This variable is OS dependent, but on Linux contains information +// about the current cpu/core the code is running on. +// If the RDTSCP instruction isn't supported on the CPU, the value 0 is returned. +func (c cpuInfo) ia32tscaux() uint32 { + if !c.rdtscp() { + return 0 + } + _, _, ecx, _ := rdtscpAsm() + return ecx +} + +// LogicalCPU will return the Logical CPU the code is currently executing on. +// This is likely to change when the OS re-schedules the running thread +// to another CPU. +// If the current core cannot be detected, -1 will be returned. +func (c cpuInfo) logicalcpu() int { + if c.maxFunc < 1 { + return -1 + } + _, ebx, _, _ := cpuid(1) + return int(ebx >> 24) +} + +// VM Will return true if the cpu id indicates we are in +// a virtual machine. This is only a hint, and will very likely +// have many false negatives. +func (c cpuInfo) vm() bool { + switch c.vendorid { + case msvm, kvm, vmware, xenhvm: + return true + } + return false +} + +// Flags contains detected cpu features and caracteristics +type flags uint64 + +// String returns a string representation of the detected +// CPU features. +func (f flags) String() string { + return strings.Join(f.strings(), ",") +} + +// Strings returns and array of the detected features. +func (f flags) strings() []string { + s := support() + r := make([]string, 0, 20) + for i := uint(0); i < 64; i++ { + key := flags(1 << i) + val := flagNames[key] + if s&key != 0 { + r = append(r, val) + } + } + return r +} + +func maxExtendedFunction() uint32 { + eax, _, _, _ := cpuid(0x80000000) + return eax +} + +func maxFunctionID() uint32 { + a, _, _, _ := cpuid(0) + return a +} + +func brandName() string { + if maxExtendedFunction() >= 0x80000004 { + v := make([]uint32, 0, 48) + for i := uint32(0); i < 3; i++ { + a, b, c, d := cpuid(0x80000002 + i) + v = append(v, a, b, c, d) + } + return strings.Trim(string(valAsString(v...)), " ") + } + return "unknown" +} + +func threadsPerCore() int { + mfi := maxFunctionID() + if mfi < 0x4 || vendorID() != intel { + return 1 + } + + if mfi < 0xb { + _, b, _, d := cpuid(1) + if (d & (1 << 28)) != 0 { + // v will contain logical core count + v := (b >> 16) & 255 + if v > 1 { + a4, _, _, _ := cpuid(4) + // physical cores + v2 := (a4 >> 26) + 1 + if v2 > 0 { + return int(v) / int(v2) + } + } + } + return 1 + } + _, b, _, _ := cpuidex(0xb, 0) + if b&0xffff == 0 { + return 1 + } + return int(b & 0xffff) +} + +func logicalCores() int { + mfi := maxFunctionID() + switch vendorID() { + case intel: + // Use this on old Intel processors + if mfi < 0xb { + if mfi < 1 { + return 0 + } + // CPUID.1:EBX[23:16] represents the maximum number of addressable IDs (initial APIC ID) + // that can be assigned to logical processors in a physical package. + // The value may not be the same as the number of logical processors that are present in the hardware of a physical package. + _, ebx, _, _ := cpuid(1) + logical := (ebx >> 16) & 0xff + return int(logical) + } + _, b, _, _ := cpuidex(0xb, 1) + return int(b & 0xffff) + case amd: + _, b, _, _ := cpuid(1) + return int((b >> 16) & 0xff) + default: + return 0 + } +} + +func familyModel() (int, int) { + if maxFunctionID() < 0x1 { + return 0, 0 + } + eax, _, _, _ := cpuid(1) + family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff) + model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0) + return int(family), int(model) +} + +func physicalCores() int { + switch vendorID() { + case intel: + return logicalCores() / threadsPerCore() + case amd: + if maxExtendedFunction() >= 0x80000008 { + _, _, c, _ := cpuid(0x80000008) + return int(c&0xff) + 1 + } + } + return 0 +} + +// Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID +var vendorMapping = map[string]vendor{ + "AMDisbetter!": amd, + "AuthenticAMD": amd, + "CentaurHauls": via, + "GenuineIntel": intel, + "TransmetaCPU": transmeta, + "GenuineTMx86": transmeta, + "Geode by NSC": nsc, + "VIA VIA VIA ": via, + "KVMKVMKVMKVM": kvm, + "Microsoft Hv": msvm, + "VMwareVMware": vmware, + "XenVMMXenVMM": xenhvm, +} + +func vendorID() vendor { + _, b, c, d := cpuid(0) + v := valAsString(b, d, c) + vend, ok := vendorMapping[string(v)] + if !ok { + return other + } + return vend +} + +func cacheLine() int { + if maxFunctionID() < 0x1 { + return 0 + } + + _, ebx, _, _ := cpuid(1) + cache := (ebx & 0xff00) >> 5 // cflush size + if cache == 0 && maxExtendedFunction() >= 0x80000006 { + _, _, ecx, _ := cpuid(0x80000006) + cache = ecx & 0xff // cacheline size + } + // TODO: Read from Cache and TLB Information + return int(cache) +} + +func (c *cpuInfo) cacheSize() { + c.cache.l1d = -1 + c.cache.l1i = -1 + c.cache.l2 = -1 + c.cache.l3 = -1 + vendor := vendorID() + switch vendor { + case intel: + if maxFunctionID() < 4 { + return + } + for i := uint32(0); ; i++ { + eax, ebx, ecx, _ := cpuidex(4, i) + cacheType := eax & 15 + if cacheType == 0 { + break + } + cacheLevel := (eax >> 5) & 7 + coherency := int(ebx&0xfff) + 1 + partitions := int((ebx>>12)&0x3ff) + 1 + associativity := int((ebx>>22)&0x3ff) + 1 + sets := int(ecx) + 1 + size := associativity * partitions * coherency * sets + switch cacheLevel { + case 1: + if cacheType == 1 { + // 1 = Data Cache + c.cache.l1d = size + } else if cacheType == 2 { + // 2 = Instruction Cache + c.cache.l1i = size + } else { + if c.cache.l1d < 0 { + c.cache.l1i = size + } + if c.cache.l1i < 0 { + c.cache.l1i = size + } + } + case 2: + c.cache.l2 = size + case 3: + c.cache.l3 = size + } + } + case amd: + // Untested. + if maxExtendedFunction() < 0x80000005 { + return + } + _, _, ecx, edx := cpuid(0x80000005) + c.cache.l1d = int(((ecx >> 24) & 0xFF) * 1024) + c.cache.l1i = int(((edx >> 24) & 0xFF) * 1024) + + if maxExtendedFunction() < 0x80000006 { + return + } + _, _, ecx, _ = cpuid(0x80000006) + c.cache.l2 = int(((ecx >> 16) & 0xFFFF) * 1024) + } + + return +} + +func support() flags { + mfi := maxFunctionID() + vend := vendorID() + if mfi < 0x1 { + return 0 + } + rval := uint64(0) + _, _, c, d := cpuid(1) + if (d & (1 << 15)) != 0 { + rval |= cmov + } + if (d & (1 << 23)) != 0 { + rval |= mmx + } + if (d & (1 << 25)) != 0 { + rval |= mmxext + } + if (d & (1 << 25)) != 0 { + rval |= sse + } + if (d & (1 << 26)) != 0 { + rval |= sse2 + } + if (c & 1) != 0 { + rval |= sse3 + } + if (c & 0x00000200) != 0 { + rval |= ssse3 + } + if (c & 0x00080000) != 0 { + rval |= sse4 + } + if (c & 0x00100000) != 0 { + rval |= sse42 + } + if (c & (1 << 25)) != 0 { + rval |= aesni + } + if (c & (1 << 1)) != 0 { + rval |= clmul + } + if c&(1<<23) != 0 { + rval |= popcnt + } + if c&(1<<30) != 0 { + rval |= rdrand + } + if c&(1<<29) != 0 { + rval |= f16c + } + if c&(1<<13) != 0 { + rval |= cx16 + } + if vend == intel && (d&(1<<28)) != 0 && mfi >= 4 { + if threadsPerCore() > 1 { + rval |= htt + } + } + + // Check XGETBV, OXSAVE and AVX bits + if c&(1<<26) != 0 && c&(1<<27) != 0 && c&(1<<28) != 0 { + // Check for OS support + eax, _ := xgetbv(0) + if (eax & 0x6) == 0x6 { + rval |= avx + if (c & 0x00001000) != 0 { + rval |= fma3 + } + } + } + + // Check AVX2, AVX2 requires OS support, but BMI1/2 don't. + if mfi >= 7 { + _, ebx, ecx, _ := cpuidex(7, 0) + if (rval&avx) != 0 && (ebx&0x00000020) != 0 { + rval |= avx2 + } + if (ebx & 0x00000008) != 0 { + rval |= bmi1 + if (ebx & 0x00000100) != 0 { + rval |= bmi2 + } + } + if ebx&(1<<4) != 0 { + rval |= hle + } + if ebx&(1<<9) != 0 { + rval |= erms + } + if ebx&(1<<11) != 0 { + rval |= rtm + } + if ebx&(1<<14) != 0 { + rval |= mpx + } + if ebx&(1<<18) != 0 { + rval |= rdseed + } + if ebx&(1<<19) != 0 { + rval |= adx + } + if ebx&(1<<29) != 0 { + rval |= sha + } + + // Only detect AVX-512 features if XGETBV is supported + if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) { + // Check for OS support + eax, _ := xgetbv(0) + + // Verify that XCR0[7:5] = ‘111b’ (OPMASK state, upper 256-bit of ZMM0-ZMM15 and + // ZMM16-ZMM31 state are enabled by OS) + /// and that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). + if (eax>>5)&7 == 7 && (eax>>1)&3 == 3 { + if ebx&(1<<16) != 0 { + rval |= avx512f + } + if ebx&(1<<17) != 0 { + rval |= avx512dq + } + if ebx&(1<<21) != 0 { + rval |= avx512ifma + } + if ebx&(1<<26) != 0 { + rval |= avx512pf + } + if ebx&(1<<27) != 0 { + rval |= avx512er + } + if ebx&(1<<28) != 0 { + rval |= avx512cd + } + if ebx&(1<<30) != 0 { + rval |= avx512bw + } + if ebx&(1<<31) != 0 { + rval |= avx512vl + } + // ecx + if ecx&(1<<1) != 0 { + rval |= avx512vbmi + } + } + } + } + + if maxExtendedFunction() >= 0x80000001 { + _, _, c, d := cpuid(0x80000001) + if (c & (1 << 5)) != 0 { + rval |= lzcnt + rval |= popcnt + } + if (d & (1 << 31)) != 0 { + rval |= amd3dnow + } + if (d & (1 << 30)) != 0 { + rval |= amd3dnowext + } + if (d & (1 << 23)) != 0 { + rval |= mmx + } + if (d & (1 << 22)) != 0 { + rval |= mmxext + } + if (c & (1 << 6)) != 0 { + rval |= sse4a + } + if d&(1<<20) != 0 { + rval |= nx + } + if d&(1<<27) != 0 { + rval |= rdtscp + } + + /* Allow for selectively disabling SSE2 functions on AMD processors + with SSE2 support but not SSE4a. This includes Athlon64, some + Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster + than SSE2 often enough to utilize this special-case flag. + AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case + so that SSE2 is used unless explicitly disabled by checking + AV_CPU_FLAG_SSE2SLOW. */ + if vendorID() != intel && + rval&sse2 != 0 && (c&0x00000040) == 0 { + rval |= sse2slow + } + + /* XOP and FMA4 use the AVX instruction coding scheme, so they can't be + * used unless the OS has AVX support. */ + if (rval & avx) != 0 { + if (c & 0x00000800) != 0 { + rval |= xop + } + if (c & 0x00010000) != 0 { + rval |= fma4 + } + } + + if vendorID() == intel { + family, model := familyModel() + if family == 6 && (model == 9 || model == 13 || model == 14) { + /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and + * 6/14 (core1 "yonah") theoretically support sse2, but it's + * usually slower than mmx. */ + if (rval & sse2) != 0 { + rval |= sse2slow + } + if (rval & sse3) != 0 { + rval |= sse3slow + } + } + /* The Atom processor has SSSE3 support, which is useful in many cases, + * but sometimes the SSSE3 version is slower than the SSE2 equivalent + * on the Atom, but is generally faster on other processors supporting + * SSSE3. This flag allows for selectively disabling certain SSSE3 + * functions on the Atom. */ + if family == 6 && model == 28 { + rval |= atom + } + } + } + return flags(rval) +} + +func valAsString(values ...uint32) []byte { + r := make([]byte, 4*len(values)) + for i, v := range values { + dst := r[i*4:] + dst[0] = byte(v & 0xff) + dst[1] = byte((v >> 8) & 0xff) + dst[2] = byte((v >> 16) & 0xff) + dst[3] = byte((v >> 24) & 0xff) + switch { + case dst[0] == 0: + return r[:i*4] + case dst[1] == 0: + return r[:i*4+1] + case dst[2] == 0: + return r[:i*4+2] + case dst[3] == 0: + return r[:i*4+3] + } + } + return r +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_386.s b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_386.s new file mode 100644 index 00000000000..9947f7b6f6a --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_386.s @@ -0,0 +1,40 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuid(SB), 7, $0 + XORL CX, CX + MOVL op+0(FP), AX + CPUID + MOVL AX, eax+4(FP) + MOVL BX, ebx+8(FP) + MOVL CX, ecx+12(FP) + MOVL DX, edx+16(FP) + RET + +// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuidex(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 + +// func xgetbv(index uint32) (eax, edx uint32) +TEXT ·asmXgetbv(SB), 7, $0 + MOVL index+0(FP), CX + BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV + MOVL AX, eax+4(FP) + MOVL DX, edx+8(FP) + RET + +// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) +TEXT ·asmRdtscpAsm(SB), 7, $0 + BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP + MOVL AX, eax+0(FP) + MOVL BX, ebx+4(FP) + MOVL CX, ecx+8(FP) + MOVL DX, edx+12(FP) + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_amd64.s b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_amd64.s new file mode 100644 index 00000000000..68a7c9d8808 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_amd64.s @@ -0,0 +1,40 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuid(SB), 7, $0 + XORQ CX, CX + 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 asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +TEXT ·asmCpuidex(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 + +// func asmXgetbv(index uint32) (eax, edx uint32) +TEXT ·asmXgetbv(SB), 7, $0 + MOVL index+0(FP), CX + BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV + MOVL AX, eax+8(FP) + MOVL DX, edx+12(FP) + RET + +// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) +TEXT ·asmRdtscpAsm(SB), 7, $0 + BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP + MOVL AX, eax+0(FP) + MOVL BX, ebx+4(FP) + MOVL CX, ecx+8(FP) + MOVL DX, edx+12(FP) + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_intel.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_intel.go new file mode 100644 index 00000000000..d5475ebfaa8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_intel.go @@ -0,0 +1,17 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// +build 386 amd64 + +package cpuid + +func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) +func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) +func asmXgetbv(index uint32) (eax, edx uint32) +func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) + +func initCPU() { + cpuid = asmCpuid + cpuidex = asmCpuidex + xgetbv = asmXgetbv + rdtscpAsm = asmRdtscpAsm +} diff --git a/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_ref.go b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_ref.go new file mode 100644 index 00000000000..6f9231b13bf --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/cpuid/private/cpuid_detect_ref.go @@ -0,0 +1,23 @@ +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// +build !amd64,!386 + +package cpuid + +func initCPU() { + cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } + + cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } + + xgetbv = func(index uint32) (eax, edx uint32) { + return 0, 0 + } + + rdtscpAsm = func() (eax, ebx, ecx, edx uint32) { + return 0, 0, 0, 0 + } +} diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/.gitignore b/Godeps/_workspace/src/github.com/klauspost/crc32/.gitignore new file mode 100644 index 00000000000..daf913b1b34 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/.travis.yml b/Godeps/_workspace/src/github.com/klauspost/crc32/.travis.yml new file mode 100644 index 00000000000..c62e25f5a5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: + - go test -v . + - go test -v -race . diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/LICENSE b/Godeps/_workspace/src/github.com/klauspost/crc32/LICENSE new file mode 100644 index 00000000000..4fd5963e39c --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2015 Klaus Post + +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. diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/README.md b/Godeps/_workspace/src/github.com/klauspost/crc32/README.md new file mode 100644 index 00000000000..440541c7ff3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/README.md @@ -0,0 +1,84 @@ +# crc32 +CRC32 hash with x64 optimizations + +This package is a drop-in replacement for the standard library `hash/crc32` package, that features SSE 4.2 optimizations on x64 platforms, for a 10x speedup. + +[![Build Status](https://travis-ci.org/klauspost/crc32.svg?branch=master)](https://travis-ci.org/klauspost/crc32) + +# usage + +Install using `go get github.com/klauspost/crc32`. This library is based on Go 1.5 code and requires Go 1.3 or newer. + +Replace `import "hash/crc32"` with `import "github.com/klauspost/crc32"` and you are good to go. + +# changes + +* Dec 4, 2015: Uses the "slice-by-8" trick more extensively, which gives a 1.5 to 2.5x speedup if assembler is unavailable. + + +# performance + +For IEEE tables (the most common), there is approximately a factor 10 speedup with "CLMUL" (Carryless multiplication) instruction: +``` +benchmark old ns/op new ns/op delta +BenchmarkCrc32KB 99955 10258 -89.74% + +benchmark old MB/s new MB/s speedup +BenchmarkCrc32KB 327.83 3194.20 9.74x +``` + +For other tables and "CLMUL" capable machines the performance is the same as the standard library. + +Here are some detailed benchmarks, comparing to go 1.5 standard library with and without assembler enabled. + +``` +Std: Standard Go 1.5 library +Crc: Indicates IEEE type CRC. +40B: Size of each slice encoded. +NoAsm: Assembler was disabled (ie. not an AMD64 or SSE 4.2+ capable machine). +Castagnoli: Castagnoli CRC type. + +BenchmarkStdCrc40B-4 10000000 158 ns/op 252.88 MB/s +BenchmarkCrc40BNoAsm-4 20000000 105 ns/op 377.38 MB/s (slice8) +BenchmarkCrc40B-4 20000000 105 ns/op 378.77 MB/s (slice8) + +BenchmarkStdCrc1KB-4 500000 3604 ns/op 284.10 MB/s +BenchmarkCrc1KBNoAsm-4 1000000 1463 ns/op 699.79 MB/s (slice8) +BenchmarkCrc1KB-4 3000000 396 ns/op 2583.69 MB/s (asm) + +BenchmarkStdCrc8KB-4 200000 11417 ns/op 717.48 MB/s (slice8) +BenchmarkCrc8KBNoAsm-4 200000 11317 ns/op 723.85 MB/s (slice8) +BenchmarkCrc8KB-4 500000 2919 ns/op 2805.73 MB/s (asm) + +BenchmarkStdCrc32KB-4 30000 45749 ns/op 716.24 MB/s (slice8) +BenchmarkCrc32KBNoAsm-4 30000 45109 ns/op 726.42 MB/s (slice8) +BenchmarkCrc32KB-4 100000 11497 ns/op 2850.09 MB/s (asm) + +BenchmarkStdNoAsmCastagnol40B-4 10000000 161 ns/op 246.94 MB/s +BenchmarkStdCastagnoli40B-4 50000000 28.4 ns/op 1410.69 MB/s (asm) +BenchmarkCastagnoli40BNoAsm-4 20000000 100 ns/op 398.01 MB/s (slice8) +BenchmarkCastagnoli40B-4 50000000 28.2 ns/op 1419.54 MB/s (asm) + +BenchmarkStdNoAsmCastagnoli1KB-4 500000 3622 ns/op 282.67 MB/s +BenchmarkStdCastagnoli1KB-4 10000000 144 ns/op 7099.78 MB/s (asm) +BenchmarkCastagnoli1KBNoAsm-4 1000000 1475 ns/op 694.14 MB/s (slice8) +BenchmarkCastagnoli1KB-4 10000000 146 ns/op 6993.35 MB/s (asm) + +BenchmarkStdNoAsmCastagnoli8KB-4 50000 28781 ns/op 284.63 MB/s +BenchmarkStdCastagnoli8KB-4 1000000 1029 ns/op 7957.89 MB/s (asm) +BenchmarkCastagnoli8KBNoAsm-4 200000 11410 ns/op 717.94 MB/s (slice8) +BenchmarkCastagnoli8KB-4 1000000 1000 ns/op 8188.71 MB/s (asm) + +BenchmarkStdNoAsmCastagnoli32KB-4 10000 115426 ns/op 283.89 MB/s +BenchmarkStdCastagnoli32KB-4 300000 4065 ns/op 8059.13 MB/s (asm) +BenchmarkCastagnoli32KBNoAsm-4 30000 45171 ns/op 725.41 MB/s (slice8) +BenchmarkCastagnoli32KB-4 500000 4077 ns/op 8035.89 MB/s (asm) +``` + +The IEEE assembler optimizations has been submitted and will be part of the Go 1.6 standard library. + +However, the improved use of slice-by-8 has not, but will probably be submitted for Go 1.7. + +# license + +Standard Go license. Changes are Copyright (c) 2015 Klaus Post under same conditions. diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32.go b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32.go new file mode 100644 index 00000000000..b584e410350 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32.go @@ -0,0 +1,182 @@ +// 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. + +// Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32, +// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for +// information. +// +// Polynomials are represented in LSB-first form also known as reversed representation. +// +// See http://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks#Reversed_representations_and_reciprocal_polynomials +// for information. +package crc32 + +import ( + "hash" + "sync" +) + +// The size of a CRC-32 checksum in bytes. +const Size = 4 + +// Predefined polynomials. +const ( + // IEEE is by far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... + IEEE = 0xedb88320 + + // Castagnoli's polynomial, used in iSCSI. + // Has better error detection characteristics than IEEE. + // http://dx.doi.org/10.1109/26.231911 + Castagnoli = 0x82f63b78 + + // Koopman's polynomial. + // Also has better error detection characteristics than IEEE. + // http://dx.doi.org/10.1109/DSN.2002.1028931 + Koopman = 0xeb31d82e +) + +// Table is a 256-word table representing the polynomial for efficient processing. +type Table [256]uint32 + +// castagnoliTable points to a lazily initialized Table for the Castagnoli +// polynomial. MakeTable will always return this value when asked to make a +// Castagnoli table so we can compare against it to find when the caller is +// using this polynomial. +var castagnoliTable *Table +var castagnoliTable8 *slicing8Table +var castagnoliOnce sync.Once + +func castagnoliInit() { + castagnoliTable = makeTable(Castagnoli) + castagnoliTable8 = makeTable8(Castagnoli) +} + +// IEEETable is the table for the IEEE polynomial. +var IEEETable = makeTable(IEEE) + +// slicing8Table is array of 8 Tables +type slicing8Table [8]Table + +// iEEETable8 is the slicing8Table for IEEE +var iEEETable8 *slicing8Table +var iEEETable8Once sync.Once + +// MakeTable returns the Table constructed from the specified polynomial. +func MakeTable(poly uint32) *Table { + switch poly { + case IEEE: + return IEEETable + case Castagnoli: + castagnoliOnce.Do(castagnoliInit) + return castagnoliTable + } + return makeTable(poly) +} + +// makeTable returns the Table constructed from the specified polynomial. +func makeTable(poly uint32) *Table { + t := new(Table) + for i := 0; i < 256; i++ { + crc := uint32(i) + for j := 0; j < 8; j++ { + if crc&1 == 1 { + crc = (crc >> 1) ^ poly + } else { + crc >>= 1 + } + } + t[i] = crc + } + return t +} + +// makeTable8 returns slicing8Table constructed from the specified polynomial. +func makeTable8(poly uint32) *slicing8Table { + t := new(slicing8Table) + t[0] = *makeTable(poly) + for i := 0; i < 256; i++ { + crc := t[0][i] + for j := 1; j < 8; j++ { + crc = t[0][crc&0xFF] ^ (crc >> 8) + t[j][i] = crc + } + } + return t +} + +// digest represents the partial evaluation of a checksum. +type digest struct { + crc uint32 + tab *Table +} + +// New creates a new hash.Hash32 computing the CRC-32 checksum +// using the polynomial represented by the Table. +func New(tab *Table) hash.Hash32 { return &digest{0, tab} } + +// NewIEEE creates a new hash.Hash32 computing the CRC-32 checksum +// using the IEEE polynomial. +func NewIEEE() hash.Hash32 { return New(IEEETable) } + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return 1 } + +func (d *digest) Reset() { d.crc = 0 } + +func update(crc uint32, tab *Table, p []byte) uint32 { + crc = ^crc + for _, v := range p { + crc = tab[byte(crc)^v] ^ (crc >> 8) + } + return ^crc +} + +// updateSlicingBy8 updates CRC using Slicing-by-8 +func updateSlicingBy8(crc uint32, tab *slicing8Table, p []byte) uint32 { + crc = ^crc + for len(p) > 8 { + crc ^= uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 + crc = tab[0][p[7]] ^ tab[1][p[6]] ^ tab[2][p[5]] ^ tab[3][p[4]] ^ + tab[4][crc>>24] ^ tab[5][(crc>>16)&0xFF] ^ + tab[6][(crc>>8)&0xFF] ^ tab[7][crc&0xFF] + p = p[8:] + } + crc = ^crc + if len(p) == 0 { + return crc + } + return update(crc, &tab[0], p) +} + +// Update returns the result of adding the bytes in p to the crc. +func Update(crc uint32, tab *Table, p []byte) uint32 { + if tab == castagnoliTable { + return updateCastagnoli(crc, p) + } else if tab == IEEETable { + return updateIEEE(crc, p) + } + return update(crc, tab, p) +} + +func (d *digest) Write(p []byte) (n int, err error) { + d.crc = Update(d.crc, d.tab, p) + return len(p), nil +} + +func (d *digest) Sum32() uint32 { return d.crc } + +func (d *digest) Sum(in []byte) []byte { + s := d.Sum32() + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) +} + +// Checksum returns the CRC-32 checksum of data +// using the polynomial represented by the Table. +func Checksum(data []byte, tab *Table) uint32 { return Update(0, tab, data) } + +// ChecksumIEEE returns the CRC-32 checksum of data +// using the IEEE polynomial. +func ChecksumIEEE(data []byte) uint32 { return updateIEEE(0, data) } diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.go b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.go new file mode 100644 index 00000000000..669fe17114e --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.go @@ -0,0 +1,62 @@ +//+build !appengine,!gccgo + +// Copyright 2011 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. + +package crc32 + +// This file contains the code to call the SSE 4.2 version of the Castagnoli +// and IEEE CRC. + +// haveSSE41/haveSSE42/haveCLMUL are defined in crc_amd64.s and uses +// CPUID to test for SSE 4.1, 4.2 and CLMUL support. +func haveSSE41() bool +func haveSSE42() bool +func haveCLMUL() bool + +// castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32 +// instruction. +// go:noescape +func castagnoliSSE42(crc uint32, p []byte) uint32 + +// ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ +// instruction as well as SSE 4.1. +// go:noescape +func ieeeCLMUL(crc uint32, p []byte) uint32 + +var sse42 = haveSSE42() +var useFastIEEE = haveCLMUL() && haveSSE41() + +func updateCastagnoli(crc uint32, p []byte) uint32 { + if sse42 { + return castagnoliSSE42(crc, p) + } + // only use slicing-by-8 when input is >= 16 Bytes + if len(p) >= 16 { + return updateSlicingBy8(crc, castagnoliTable8, p) + } + return update(crc, castagnoliTable, p) +} + +func updateIEEE(crc uint32, p []byte) uint32 { + if useFastIEEE && len(p) >= 64 { + left := len(p) & 15 + do := len(p) - left + crc := ^ieeeCLMUL(^crc, p[:do]) + if left > 0 { + crc = update(crc, IEEETable, p[do:]) + } + return crc + } + + // only use slicing-by-8 when input is >= 16 Bytes + if len(p) >= 16 { + iEEETable8Once.Do(func() { + iEEETable8 = makeTable8(IEEE) + }) + return updateSlicingBy8(crc, iEEETable8, p) + } + + return update(crc, IEEETable, p) +} diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.s b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.s new file mode 100644 index 00000000000..1b1cd8b6740 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64.s @@ -0,0 +1,237 @@ +//+build gc + +// Copyright 2011 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. + +#define NOSPLIT 4 +#define RODATA 8 + +// func castagnoliSSE42(crc uint32, p []byte) uint32 +TEXT ·castagnoliSSE42(SB), NOSPLIT, $0 + MOVL crc+0(FP), AX // CRC value + MOVQ p+8(FP), SI // data pointer + MOVQ p_len+16(FP), CX // len(p) + + NOTL AX + + // If there's less than 8 bytes to process, we do it byte-by-byte. + CMPQ CX, $8 + JL cleanup + + // Process individual bytes until the input is 8-byte aligned. +startup: + MOVQ SI, BX + ANDQ $7, BX + JZ aligned + + CRC32B (SI), AX + DECQ CX + INCQ SI + JMP startup + +aligned: + // The input is now 8-byte aligned and we can process 8-byte chunks. + CMPQ CX, $8 + JL cleanup + + CRC32Q (SI), AX + ADDQ $8, SI + SUBQ $8, CX + JMP aligned + +cleanup: + // We may have some bytes left over that we process one at a time. + CMPQ CX, $0 + JE done + + CRC32B (SI), AX + INCQ SI + DECQ CX + JMP cleanup + +done: + NOTL AX + MOVL AX, ret+32(FP) + RET + +// func haveSSE42() bool +TEXT ·haveSSE42(SB), NOSPLIT, $0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $20, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// func haveCLMUL() bool +TEXT ·haveCLMUL(SB), NOSPLIT, $0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $1, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// func haveSSE41() bool +TEXT ·haveSSE41(SB), NOSPLIT, $0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $19, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// CRC32 polynomial data +// +// These constants are lifted from the +// Linux kernel, since they avoid the costly +// PSHUFB 16 byte reversal proposed in the +// original Intel paper. +DATA r2r1kp<>+0(SB)/8, $0x154442bd4 +DATA r2r1kp<>+8(SB)/8, $0x1c6e41596 +DATA r4r3kp<>+0(SB)/8, $0x1751997d0 +DATA r4r3kp<>+8(SB)/8, $0x0ccaa009e +DATA rupolykp<>+0(SB)/8, $0x1db710641 +DATA rupolykp<>+8(SB)/8, $0x1f7011641 +DATA r5kp<>+0(SB)/8, $0x163cd6124 + +GLOBL r2r1kp<>(SB), RODATA, $16 +GLOBL r4r3kp<>(SB), RODATA, $16 +GLOBL rupolykp<>(SB), RODATA, $16 +GLOBL r5kp<>(SB), RODATA, $8 + +// Based on http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf +// len(p) must be at least 64, and must be a multiple of 16. + +// func ieeeCLMUL(crc uint32, p []byte) uint32 +TEXT ·ieeeCLMUL(SB), NOSPLIT, $0 + MOVL crc+0(FP), X0 // Initial CRC value + MOVQ p+8(FP), SI // data pointer + MOVQ p_len+16(FP), CX // len(p) + + MOVOU (SI), X1 + MOVOU 16(SI), X2 + MOVOU 32(SI), X3 + MOVOU 48(SI), X4 + PXOR X0, X1 + ADDQ $64, SI // buf+=64 + SUBQ $64, CX // len-=64 + CMPQ CX, $64 // Less than 64 bytes left + JB remain64 + + MOVOU r2r1kp<>+0(SB), X0 + +loopback64: + MOVOA X1, X5 + MOVOA X2, X6 + MOVOA X3, X7 + MOVOA X4, X8 + + PCLMULQDQ $0, X0, X1 + PCLMULQDQ $0, X0, X2 + PCLMULQDQ $0, X0, X3 + PCLMULQDQ $0, X0, X4 + + // Load next early + MOVOU (SI), X11 + MOVOU 16(SI), X12 + MOVOU 32(SI), X13 + MOVOU 48(SI), X14 + + PCLMULQDQ $0x11, X0, X5 + PCLMULQDQ $0x11, X0, X6 + PCLMULQDQ $0x11, X0, X7 + PCLMULQDQ $0x11, X0, X8 + + PXOR X5, X1 + PXOR X6, X2 + PXOR X7, X3 + PXOR X8, X4 + + PXOR X11, X1 + PXOR X12, X2 + PXOR X13, X3 + PXOR X14, X4 + + ADDQ $0x40, DI + ADDQ $64, SI // buf+=64 + SUBQ $64, CX // len-=64 + CMPQ CX, $64 // Less than 64 bytes left? + JGE loopback64 + + // Fold result into a single register (X1) +remain64: + MOVOU r4r3kp<>+0(SB), X0 + + MOVOA X1, X5 + PCLMULQDQ $0, X0, X1 + PCLMULQDQ $0x11, X0, X5 + PXOR X5, X1 + PXOR X2, X1 + + MOVOA X1, X5 + PCLMULQDQ $0, X0, X1 + PCLMULQDQ $0x11, X0, X5 + PXOR X5, X1 + PXOR X3, X1 + + MOVOA X1, X5 + PCLMULQDQ $0, X0, X1 + PCLMULQDQ $0x11, X0, X5 + PXOR X5, X1 + PXOR X4, X1 + + // More than 16 bytes left? + CMPQ CX, $16 + JB finish + + // Encode 16 bytes +remain16: + MOVOU (SI), X10 + MOVOA X1, X5 + PCLMULQDQ $0, X0, X1 + PCLMULQDQ $0x11, X0, X5 + PXOR X5, X1 + PXOR X10, X1 + SUBQ $16, CX + ADDQ $16, SI + CMPQ CX, $16 + JGE remain16 + +finish: + // Fold final result into 32 bits and return it + PCMPEQB X3, X3 + PCLMULQDQ $1, X1, X0 + PSRLDQ $8, X1 + PXOR X0, X1 + + MOVOA X1, X2 + MOVQ r5kp<>+0(SB), X0 + + // Creates 32 bit mask. Note that we don't care about upper half. + PSRLQ $32, X3 + + PSRLDQ $4, X2 + PAND X3, X1 + PCLMULQDQ $0, X0, X1 + PXOR X2, X1 + + MOVOU rupolykp<>+0(SB), X0 + + MOVOA X1, X2 + PAND X3, X1 + PCLMULQDQ $0x10, X0, X1 + PAND X3, X1 + PCLMULQDQ $0, X0, X1 + PXOR X2, X1 + + // PEXTRD $1, X1, AX (SSE 4.1) + BYTE $0x66; BYTE $0x0f; BYTE $0x3a + BYTE $0x16; BYTE $0xc8; BYTE $0x01 + MOVL AX, ret+32(FP) + + RET diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.go b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.go new file mode 100644 index 00000000000..64a356feca3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.go @@ -0,0 +1,39 @@ +//+build !appengine,!gccgo + +// Copyright 2011 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. + +package crc32 + +// This file contains the code to call the SSE 4.2 version of the Castagnoli +// CRC. + +// haveSSE42 is defined in crc_amd64p32.s and uses CPUID to test for 4.2 +// support. +func haveSSE42() bool + +// castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32 +// instruction. +func castagnoliSSE42(crc uint32, p []byte) uint32 + +var sse42 = haveSSE42() + +func updateCastagnoli(crc uint32, p []byte) uint32 { + if sse42 { + return castagnoliSSE42(crc, p) + } + return update(crc, castagnoliTable, p) +} + +func updateIEEE(crc uint32, p []byte) uint32 { + // only use slicing-by-8 when input is >= 4KB + if len(p) >= 4096 { + iEEETable8Once.Do(func() { + iEEETable8 = makeTable8(IEEE) + }) + return updateSlicingBy8(crc, iEEETable8, p) + } + + return update(crc, IEEETable, p) +} diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.s b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.s new file mode 100644 index 00000000000..65944c5ac77 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_amd64p32.s @@ -0,0 +1,67 @@ +//+build gc + +// Copyright 2011 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. + +#define NOSPLIT 4 +#define RODATA 8 + +// func castagnoliSSE42(crc uint32, p []byte) uint32 +TEXT ·castagnoliSSE42(SB), NOSPLIT, $0 + MOVL crc+0(FP), AX // CRC value + MOVL p+4(FP), SI // data pointer + MOVL p_len+8(FP), CX // len(p) + + NOTL AX + + // If there's less than 8 bytes to process, we do it byte-by-byte. + CMPQ CX, $8 + JL cleanup + + // Process individual bytes until the input is 8-byte aligned. +startup: + MOVQ SI, BX + ANDQ $7, BX + JZ aligned + + CRC32B (SI), AX + DECQ CX + INCQ SI + JMP startup + +aligned: + // The input is now 8-byte aligned and we can process 8-byte chunks. + CMPQ CX, $8 + JL cleanup + + CRC32Q (SI), AX + ADDQ $8, SI + SUBQ $8, CX + JMP aligned + +cleanup: + // We may have some bytes left over that we process one at a time. + CMPQ CX, $0 + JE done + + CRC32B (SI), AX + INCQ SI + DECQ CX + JMP cleanup + +done: + NOTL AX + MOVL AX, ret+16(FP) + RET + +// func haveSSE42() bool +TEXT ·haveSSE42(SB), NOSPLIT, $0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $20, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + diff --git a/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_generic.go b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_generic.go new file mode 100644 index 00000000000..43f5d1dd911 --- /dev/null +++ b/Godeps/_workspace/src/github.com/klauspost/crc32/crc32_generic.go @@ -0,0 +1,28 @@ +// Copyright 2011 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. + +// +build 386 arm arm64 ppc64 ppc64le appengine gccgo + +package crc32 + +// The file contains the generic version of updateCastagnoli which does +// slicing-by-8, or uses the fallback for very small sizes. +func updateCastagnoli(crc uint32, p []byte) uint32 { + // only use slicing-by-8 when input is >= 16 Bytes + if len(p) >= 16 { + return updateSlicingBy8(crc, castagnoliTable8, p) + } + return update(crc, castagnoliTable, p) +} + +func updateIEEE(crc uint32, p []byte) uint32 { + // only use slicing-by-8 when input is >= 16 Bytes + if len(p) >= 16 { + iEEETable8Once.Do(func() { + iEEETable8 = makeTable8(IEEE) + }) + return updateSlicingBy8(crc, iEEETable8, p) + } + return update(crc, IEEETable, p) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/README.md b/Godeps/_workspace/src/github.com/macaron-contrib/binding/README.md deleted file mode 100644 index ad375653600..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/README.md +++ /dev/null @@ -1,21 +0,0 @@ -binding [![Build Status](https://drone.io/github.com/macaron-contrib/binding/status.png)](https://drone.io/github.com/macaron-contrib/binding/latest) [![](http://gocover.io/_badge/github.com/macaron-contrib/binding)](http://gocover.io/github.com/macaron-contrib/binding) -======= - -Middlware binding provides request data binding and validation for [Macaron](https://github.com/Unknwon/macaron). - -### Installation - - go get github.com/macaron-contrib/binding - -## Getting Help - -- [API Reference](https://gowalker.org/github.com/macaron-contrib/binding) -- [Documentation](http://macaron.gogs.io/docs/middlewares/binding) - -## Credits - -This package is forked from [martini-contrib/binding](https://github.com/martini-contrib/binding) with modifications. - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/bind_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/bind_test.go deleted file mode 100644 index fb224fafc05..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/bind_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Bind(t *testing.T) { - Convey("Bind test", t, func() { - Convey("Bind form", func() { - for _, testCase := range formTestCases { - performFormTest(t, Bind, testCase) - } - }) - - Convey("Bind JSON", func() { - for _, testCase := range jsonTestCases { - performJsonTest(t, Bind, testCase) - } - }) - - Convey("Bind multipart form", func() { - for _, testCase := range multipartFormTestCases { - performMultipartFormTest(t, Bind, testCase) - } - }) - - Convey("Bind with file", func() { - for _, testCase := range fileTestCases { - performFileTest(t, Bind, testCase) - performFileTest(t, BindIgnErr, testCase) - } - }) - }) -} - -func Test_Version(t *testing.T) { - Convey("Get package version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/common_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/common_test.go deleted file mode 100644 index 83af92ff182..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/common_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "mime/multipart" - - "github.com/Unknwon/macaron" -) - -// These types are mostly contrived examples, but they're used -// across many test cases. The idea is to cover all the scenarios -// that this binding package might encounter in actual use. -type ( - // For basic test cases with a required field - Post struct { - Title string `form:"title" json:"title" binding:"Required"` - Content string `form:"content" json:"content"` - } - - // To be used as a nested struct (with a required field) - Person struct { - Name string `form:"name" json:"name" binding:"Required"` - Email string `form:"email" json:"email"` - } - - // For advanced test cases: multiple values, embedded - // and nested structs, an ignored field, and single - // and multiple file uploads - BlogPost struct { - Post - Id int `binding:"Required"` // JSON not specified here for test coverage - Ignored string `form:"-" json:"-"` - Ratings []int `form:"rating" json:"ratings"` - Author Person `json:"author"` - Coauthor *Person `json:"coauthor"` - HeaderImage *multipart.FileHeader - Pictures []*multipart.FileHeader `form:"picture"` - unexported string `form:"unexported"` - } - - EmbedPerson struct { - *Person - } - - SadForm struct { - AlphaDash string `form:"AlphaDash" binding:"AlphaDash"` - AlphaDashDot string `form:"AlphaDashDot" binding:"AlphaDashDot"` - MinSize string `form:"MinSize" binding:"MinSize(5)"` - MinSizeSlice []string `form:"MinSizeSlice" binding:"MinSize(5)"` - MaxSize string `form:"MaxSize" binding:"MaxSize(1)"` - MaxSizeSlice []string `form:"MaxSizeSlice" binding:"MaxSize(1)"` - Range int `form:"Range" binding:"Range(1,2)"` - RangeInvalid int `form:"RangeInvalid" binding:"Range(1)"` - Email string `binding:"Email"` - Url string `form:"Url" binding:"Url"` - UrlEmpty string `form:"UrlEmpty" binding:"Url"` - In string `form:"In" binding:"Default(0);In(1,2,3)"` - InInvalid string `form:"InInvalid" binding:"In(1,2,3)"` - NotIn string `form:"NotIn" binding:"NotIn(1,2,3)"` - Include string `form:"Include" binding:"Include(a)"` - Exclude string `form:"Exclude" binding:"Exclude(a)"` - } - - CustomErrorHandle struct { - Rule `binding:"CustomRule"` - } - - // The common function signature of the handlers going under test. - handlerFunc func(interface{}, ...interface{}) macaron.Handler - - // Used for testing mapping an interface to the context - // If used (withInterface = true in the testCases), a modeler - // should be mapped to the context as well as BlogPost, meaning - // you can receive a modeler in your application instead of a - // concrete BlogPost. - modeler interface { - Model() string - } -) - -func (p Post) Validate(ctx *macaron.Context, errs Errors) Errors { - if len(p.Title) < 10 { - errs = append(errs, Error{ - FieldNames: []string{"title"}, - Classification: "LengthError", - Message: "Life is too short", - }) - } - return errs -} - -func (p Post) Model() string { - return p.Title -} - -func (_ CustomErrorHandle) Error(_ *macaron.Context, _ Errors) {} - -const ( - testRoute = "/test" - formContentType = "application/x-www-form-urlencoded" -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errorhandler_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/errorhandler_test.go deleted file mode 100644 index 8b20657201c..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errorhandler_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -var errorTestCases = []errorTestCase{ - { - description: "No errors", - errors: Errors{}, - expected: errorTestResult{ - statusCode: http.StatusOK, - }, - }, - { - description: "Deserialization error", - errors: Errors{ - { - Classification: ERR_DESERIALIZATION, - Message: "Some parser error here", - }, - }, - expected: errorTestResult{ - statusCode: http.StatusBadRequest, - contentType: _JSON_CONTENT_TYPE, - body: `[{"classification":"DeserializationError","message":"Some parser error here"}]`, - }, - }, - { - description: "Content-Type error", - errors: Errors{ - { - Classification: ERR_CONTENT_TYPE, - Message: "Empty Content-Type", - }, - }, - expected: errorTestResult{ - statusCode: http.StatusUnsupportedMediaType, - contentType: _JSON_CONTENT_TYPE, - body: `[{"classification":"ContentTypeError","message":"Empty Content-Type"}]`, - }, - }, - { - description: "Requirement error", - errors: Errors{ - { - FieldNames: []string{"some_field"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - }, - expected: errorTestResult{ - statusCode: STATUS_UNPROCESSABLE_ENTITY, - contentType: _JSON_CONTENT_TYPE, - body: `[{"fieldNames":["some_field"],"classification":"RequiredError","message":"Required"}]`, - }, - }, - { - description: "Bad header error", - errors: Errors{ - { - Classification: "HeaderError", - Message: "The X-Something header must be specified", - }, - }, - expected: errorTestResult{ - statusCode: STATUS_UNPROCESSABLE_ENTITY, - contentType: _JSON_CONTENT_TYPE, - body: `[{"classification":"HeaderError","message":"The X-Something header must be specified"}]`, - }, - }, - { - description: "Custom field error", - errors: Errors{ - { - FieldNames: []string{"month", "year"}, - Classification: "DateError", - Message: "The month and year must be in the future", - }, - }, - expected: errorTestResult{ - statusCode: STATUS_UNPROCESSABLE_ENTITY, - contentType: _JSON_CONTENT_TYPE, - body: `[{"fieldNames":["month","year"],"classification":"DateError","message":"The month and year must be in the future"}]`, - }, - }, - { - description: "Multiple errors", - errors: Errors{ - { - FieldNames: []string{"foo"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - { - FieldNames: []string{"foo"}, - Classification: "LengthError", - Message: "The length of the 'foo' field is too short", - }, - }, - expected: errorTestResult{ - statusCode: STATUS_UNPROCESSABLE_ENTITY, - contentType: _JSON_CONTENT_TYPE, - body: `[{"fieldNames":["foo"],"classification":"RequiredError","message":"Required"},{"fieldNames":["foo"],"classification":"LengthError","message":"The length of the 'foo' field is too short"}]`, - }, - }, -} - -func Test_ErrorHandler(t *testing.T) { - Convey("Error handler", t, func() { - for _, testCase := range errorTestCases { - performErrorTest(t, testCase) - } - }) -} - -func performErrorTest(t *testing.T, testCase errorTestCase) { - resp := httptest.NewRecorder() - - errorHandler(testCase.errors, resp) - - So(resp.Code, ShouldEqual, testCase.expected.statusCode) - So(resp.Header().Get("Content-Type"), ShouldEqual, testCase.expected.contentType) - - actualBody, err := ioutil.ReadAll(resp.Body) - So(err, ShouldBeNil) - So(string(actualBody), ShouldEqual, testCase.expected.body) -} - -type ( - errorTestCase struct { - description string - errors Errors - expected errorTestResult - } - - errorTestResult struct { - statusCode int - contentType string - body string - } -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errors_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/errors_test.go deleted file mode 100644 index 8537c0133ac..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/errors_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "fmt" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_ErrorsAdd(t *testing.T) { - Convey("Add new error", t, func() { - var actual Errors - expected := Errors{ - Error{ - FieldNames: []string{"Field1", "Field2"}, - Classification: "ErrorClass", - Message: "Some message", - }, - } - - actual.Add(expected[0].FieldNames, expected[0].Classification, expected[0].Message) - - So(len(actual), ShouldEqual, 1) - So(fmt.Sprintf("%#v", actual), ShouldEqual, fmt.Sprintf("%#v", expected)) - }) -} - -func Test_ErrorsLen(t *testing.T) { - Convey("Get number of errors", t, func() { - So(errorsTestSet.Len(), ShouldEqual, len(errorsTestSet)) - }) -} - -func Test_ErrorsHas(t *testing.T) { - Convey("Check error class", t, func() { - So(errorsTestSet.Has("ClassA"), ShouldBeTrue) - So(errorsTestSet.Has("ClassQ"), ShouldBeFalse) - }) -} - -func Test_ErrorGetters(t *testing.T) { - Convey("Get error detail", t, func() { - err := Error{ - FieldNames: []string{"field1", "field2"}, - Classification: "ErrorClass", - Message: "The message", - } - - fieldsActual := err.Fields() - - So(len(fieldsActual), ShouldEqual, 2) - So(fieldsActual[0], ShouldEqual, "field1") - So(fieldsActual[1], ShouldEqual, "field2") - - So(err.Kind(), ShouldEqual, "ErrorClass") - So(err.Error(), ShouldEqual, "The message") - }) -} - -/* -func TestErrorsWithClass(t *testing.T) { - expected := Errors{ - errorsTestSet[0], - errorsTestSet[3], - } - actualStr := fmt.Sprintf("%#v", errorsTestSet.WithClass("ClassA")) - expectedStr := fmt.Sprintf("%#v", expected) - if actualStr != expectedStr { - t.Errorf("Expected:\n%s\nbut got:\n%s", expectedStr, actualStr) - } -} -*/ - -var errorsTestSet = Errors{ - Error{ - FieldNames: []string{}, - Classification: "ClassA", - Message: "Foobar", - }, - Error{ - FieldNames: []string{}, - Classification: "ClassB", - Message: "Foo", - }, - Error{ - FieldNames: []string{"field1", "field2"}, - Classification: "ClassB", - Message: "Foobar", - }, - Error{ - FieldNames: []string{"field2"}, - Classification: "ClassA", - Message: "Foobar", - }, - Error{ - FieldNames: []string{"field2"}, - Classification: "ClassB", - Message: "Foobar", - }, -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/file_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/file_test.go deleted file mode 100644 index f6bab1792eb..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/file_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "bytes" - "mime/multipart" - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -var fileTestCases = []fileTestCase{ - { - description: "Single file", - singleFile: &fileInfo{ - fileName: "message.txt", - data: "All your binding are belong to us", - }, - }, - { - description: "Multiple files", - multipleFiles: []*fileInfo{ - &fileInfo{ - fileName: "cool-gopher-fact.txt", - data: "Did you know? https://plus.google.com/+MatthewHolt/posts/GmVfd6TPJ51", - }, - &fileInfo{ - fileName: "gophercon2014.txt", - data: "@bradfitz has a Go time machine: https://twitter.com/mholt6/status/459463953395875840", - }, - }, - }, - { - description: "Single file and multiple files", - singleFile: &fileInfo{ - fileName: "social media.txt", - data: "Hey, you should follow @mholt6 (Twitter) or +MatthewHolt (Google+)", - }, - multipleFiles: []*fileInfo{ - &fileInfo{ - fileName: "thank you!", - data: "Also, thanks to all the contributors of this package!", - }, - &fileInfo{ - fileName: "btw...", - data: "This tool translates JSON into Go structs: http://mholt.github.io/json-to-go/", - }, - }, - }, -} - -func Test_FileUploads(t *testing.T) { - Convey("Test file upload", t, func() { - for _, testCase := range fileTestCases { - performFileTest(t, MultipartForm, testCase) - } - }) -} - -func performFileTest(t *testing.T, binder handlerFunc, testCase fileTestCase) { - httpRecorder := httptest.NewRecorder() - m := macaron.Classic() - - fileTestHandler := func(actual BlogPost, errs Errors) { - assertFileAsExpected(t, testCase, actual.HeaderImage, testCase.singleFile) - So(len(testCase.multipleFiles), ShouldEqual, len(actual.Pictures)) - - for i, expectedFile := range testCase.multipleFiles { - if i >= len(actual.Pictures) { - break - } - assertFileAsExpected(t, testCase, actual.Pictures[i], expectedFile) - } - } - - m.Post(testRoute, binder(BlogPost{}), func(actual BlogPost, errs Errors) { - fileTestHandler(actual, errs) - }) - - m.ServeHTTP(httpRecorder, buildRequestWithFile(testCase)) - - switch httpRecorder.Code { - case http.StatusNotFound: - panic("Routing is messed up in test fixture (got 404): check methods and paths") - case http.StatusInternalServerError: - panic("Something bad happened on '" + testCase.description + "'") - } -} - -func assertFileAsExpected(t *testing.T, testCase fileTestCase, actual *multipart.FileHeader, expected *fileInfo) { - if expected == nil && actual == nil { - return - } - - if expected != nil && actual == nil { - So(actual, ShouldNotBeNil) - return - } else if expected == nil && actual != nil { - So(actual, ShouldBeNil) - return - } - - So(actual.Filename, ShouldEqual, expected.fileName) - So(unpackFileHeaderData(actual), ShouldEqual, expected.data) -} - -func buildRequestWithFile(testCase fileTestCase) *http.Request { - b := &bytes.Buffer{} - w := multipart.NewWriter(b) - - if testCase.singleFile != nil { - formFileSingle, err := w.CreateFormFile("header_image", testCase.singleFile.fileName) - if err != nil { - panic("Could not create FormFile (single file): " + err.Error()) - } - formFileSingle.Write([]byte(testCase.singleFile.data)) - } - - for _, file := range testCase.multipleFiles { - formFileMultiple, err := w.CreateFormFile("picture", file.fileName) - if err != nil { - panic("Could not create FormFile (multiple files): " + err.Error()) - } - formFileMultiple.Write([]byte(file.data)) - } - - err := w.Close() - if err != nil { - panic("Could not close multipart writer: " + err.Error()) - } - - req, err := http.NewRequest("POST", testRoute, b) - if err != nil { - panic("Could not create file upload request: " + err.Error()) - } - - req.Header.Set("Content-Type", w.FormDataContentType()) - - return req -} - -func unpackFileHeaderData(fh *multipart.FileHeader) string { - if fh == nil { - return "" - } - - f, err := fh.Open() - if err != nil { - panic("Could not open file header:" + err.Error()) - } - defer f.Close() - - var fb bytes.Buffer - _, err = fb.ReadFrom(f) - if err != nil { - panic("Could not read from file header:" + err.Error()) - } - - return fb.String() -} - -type ( - fileTestCase struct { - description string - input BlogPost - singleFile *fileInfo - multipleFiles []*fileInfo - } - - fileInfo struct { - fileName string - data string - } -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/form_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/form_test.go deleted file mode 100644 index 8e2480908ff..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/form_test.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "fmt" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -var formTestCases = []formTestCase{ - { - description: "Happy path", - shouldSucceed: true, - payload: `title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet`, - contentType: formContentType, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Happy path with interface", - shouldSucceed: true, - withInterface: true, - payload: `title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet`, - contentType: formContentType, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Empty payload", - shouldSucceed: false, - payload: ``, - contentType: formContentType, - expected: Post{}, - }, - { - description: "Empty content type", - shouldSucceed: false, - payload: `title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet`, - contentType: ``, - expected: Post{}, - }, - { - description: "Malformed form body", - shouldSucceed: false, - payload: `title=%2`, - contentType: formContentType, - expected: Post{}, - }, - { - description: "With nested and embedded structs", - shouldSucceed: true, - payload: `title=Glorious+Post+Title&id=1&name=Matt+Holt`, - contentType: formContentType, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Required embedded struct field not specified", - shouldSucceed: false, - payload: `id=1&name=Matt+Holt`, - contentType: formContentType, - expected: BlogPost{Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Required nested struct field not specified", - shouldSucceed: false, - payload: `title=Glorious+Post+Title&id=1`, - contentType: formContentType, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1}, - }, - { - description: "Multiple values into slice", - shouldSucceed: true, - payload: `title=Glorious+Post+Title&id=1&name=Matt+Holt&rating=4&rating=3&rating=5`, - contentType: formContentType, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}, Ratings: []int{4, 3, 5}}, - }, - { - description: "Unexported field", - shouldSucceed: true, - payload: `title=Glorious+Post+Title&id=1&name=Matt+Holt&unexported=foo`, - contentType: formContentType, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Query string POST", - shouldSucceed: true, - payload: `title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet`, - contentType: formContentType, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Query string with Content-Type (POST request)", - shouldSucceed: true, - queryString: "?title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet", - payload: ``, - contentType: formContentType, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Query string without Content-Type (GET request)", - shouldSucceed: true, - method: "GET", - queryString: "?title=Glorious+Post+Title&content=Lorem+ipsum+dolor+sit+amet", - payload: ``, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Embed struct pointer", - shouldSucceed: true, - deepEqual: true, - method: "GET", - queryString: "?name=Glorious+Post+Title&email=Lorem+ipsum+dolor+sit+amet", - payload: ``, - expected: EmbedPerson{&Person{Name: "Glorious Post Title", Email: "Lorem ipsum dolor sit amet"}}, - }, - { - description: "Embed struct pointer remain nil if not binded", - shouldSucceed: true, - deepEqual: true, - method: "GET", - queryString: "?", - payload: ``, - expected: EmbedPerson{nil}, - }, - { - description: "Custom error handler", - shouldSucceed: true, - deepEqual: true, - method: "GET", - queryString: "?", - payload: ``, - expected: CustomErrorHandle{}, - }, -} - -func init() { - AddRule(&Rule{ - func(rule string) bool { - return rule == "CustomRule" - }, - func(_ Errors, _ string, _ interface{}) bool { - return false - }, - }) - SetNameMapper(nameMapper) -} - -func Test_Form(t *testing.T) { - Convey("Test form", t, func() { - for _, testCase := range formTestCases { - performFormTest(t, Form, testCase) - } - }) -} - -func performFormTest(t *testing.T, binder handlerFunc, testCase formTestCase) { - resp := httptest.NewRecorder() - m := macaron.Classic() - - formTestHandler := func(actual interface{}, errs Errors) { - if testCase.shouldSucceed && len(errs) > 0 { - So(len(errs), ShouldEqual, 0) - } else if !testCase.shouldSucceed && len(errs) == 0 { - So(len(errs), ShouldNotEqual, 0) - } - expString := fmt.Sprintf("%+v", testCase.expected) - actString := fmt.Sprintf("%+v", actual) - if actString != expString && !(testCase.deepEqual && reflect.DeepEqual(testCase.expected, actual)) { - So(actString, ShouldEqual, expString) - } - } - - switch testCase.expected.(type) { - case Post: - if testCase.withInterface { - m.Post(testRoute, binder(Post{}, (*modeler)(nil)), func(actual Post, iface modeler, errs Errors) { - So(actual.Title, ShouldEqual, iface.Model()) - formTestHandler(actual, errs) - }) - } else { - m.Post(testRoute, binder(Post{}), func(actual Post, errs Errors) { - formTestHandler(actual, errs) - }) - m.Get(testRoute, binder(Post{}), func(actual Post, errs Errors) { - formTestHandler(actual, errs) - }) - } - - case BlogPost: - if testCase.withInterface { - m.Post(testRoute, binder(BlogPost{}, (*modeler)(nil)), func(actual BlogPost, iface modeler, errs Errors) { - So(actual.Title, ShouldEqual, iface.Model()) - formTestHandler(actual, errs) - }) - } else { - m.Post(testRoute, binder(BlogPost{}), func(actual BlogPost, errs Errors) { - formTestHandler(actual, errs) - }) - } - - case EmbedPerson: - m.Post(testRoute, binder(EmbedPerson{}), func(actual EmbedPerson, errs Errors) { - formTestHandler(actual, errs) - }) - m.Get(testRoute, binder(EmbedPerson{}), func(actual EmbedPerson, errs Errors) { - formTestHandler(actual, errs) - }) - case CustomErrorHandle: - m.Get(testRoute, binder(CustomErrorHandle{}), func(actual CustomErrorHandle, errs Errors) { - formTestHandler(actual, errs) - }) - } - - if len(testCase.method) == 0 { - testCase.method = "POST" - } - - req, err := http.NewRequest(testCase.method, testRoute+testCase.queryString, strings.NewReader(testCase.payload)) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", testCase.contentType) - - m.ServeHTTP(resp, req) - - switch resp.Code { - case http.StatusNotFound: - panic("Routing is messed up in test fixture (got 404): check methods and paths") - case http.StatusInternalServerError: - panic("Something bad happened on '" + testCase.description + "'") - } -} - -type ( - formTestCase struct { - description string - shouldSucceed bool - deepEqual bool - withInterface bool - queryString string - payload string - contentType string - expected interface{} - method string - } -) - -type defaultForm struct { - Default string `binding:"Default(hello world)"` -} - -func Test_Default(t *testing.T) { - Convey("Test default value", t, func() { - m := macaron.Classic() - m.Get("/", Bind(defaultForm{}), func(f defaultForm) { - So(f.Default, ShouldEqual, "hello world") - }) - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - - m.ServeHTTP(resp, req) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/json_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/json_test.go deleted file mode 100644 index 1321f0bd14c..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/json_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -var jsonTestCases = []jsonTestCase{ - { - description: "Happy path", - shouldSucceedOnJson: true, - payload: `{"title": "Glorious Post Title", "content": "Lorem ipsum dolor sit amet"}`, - contentType: _JSON_CONTENT_TYPE, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Happy path with interface", - shouldSucceedOnJson: true, - withInterface: true, - payload: `{"title": "Glorious Post Title", "content": "Lorem ipsum dolor sit amet"}`, - contentType: _JSON_CONTENT_TYPE, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Nil payload", - shouldSucceedOnJson: false, - payload: `-nil-`, - contentType: _JSON_CONTENT_TYPE, - expected: Post{}, - }, - { - description: "Empty payload", - shouldSucceedOnJson: false, - payload: ``, - contentType: _JSON_CONTENT_TYPE, - expected: Post{}, - }, - { - description: "Empty content type", - shouldSucceedOnJson: true, - shouldFailOnBind: true, - payload: `{"title": "Glorious Post Title", "content": "Lorem ipsum dolor sit amet"}`, - contentType: ``, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Unsupported content type", - shouldSucceedOnJson: true, - shouldFailOnBind: true, - payload: `{"title": "Glorious Post Title", "content": "Lorem ipsum dolor sit amet"}`, - contentType: `BoGuS`, - expected: Post{Title: "Glorious Post Title", Content: "Lorem ipsum dolor sit amet"}, - }, - { - description: "Malformed JSON", - shouldSucceedOnJson: false, - payload: `{"title":"foo"`, - contentType: _JSON_CONTENT_TYPE, - expected: Post{}, - }, - { - description: "Deserialization with nested and embedded struct", - shouldSucceedOnJson: true, - payload: `{"title":"Glorious Post Title", "id":1, "author":{"name":"Matt Holt"}}`, - contentType: _JSON_CONTENT_TYPE, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Deserialization with nested and embedded struct with interface", - shouldSucceedOnJson: true, - withInterface: true, - payload: `{"title":"Glorious Post Title", "id":1, "author":{"name":"Matt Holt"}}`, - contentType: _JSON_CONTENT_TYPE, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Required nested struct field not specified", - shouldSucceedOnJson: false, - payload: `{"title":"Glorious Post Title", "id":1, "author":{}}`, - contentType: _JSON_CONTENT_TYPE, - expected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1}, - }, - { - description: "Required embedded struct field not specified", - shouldSucceedOnJson: false, - payload: `{"id":1, "author":{"name":"Matt Holt"}}`, - contentType: _JSON_CONTENT_TYPE, - expected: BlogPost{Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Slice of Posts", - shouldSucceedOnJson: true, - payload: `[{"title": "First Post"}, {"title": "Second Post"}]`, - contentType: _JSON_CONTENT_TYPE, - expected: []Post{Post{Title: "First Post"}, Post{Title: "Second Post"}}, - }, -} - -func Test_Json(t *testing.T) { - Convey("Test JSON", t, func() { - for _, testCase := range jsonTestCases { - performJsonTest(t, Json, testCase) - } - }) -} - -func performJsonTest(t *testing.T, binder handlerFunc, testCase jsonTestCase) { - var payload io.Reader - httpRecorder := httptest.NewRecorder() - m := macaron.Classic() - - jsonTestHandler := func(actual interface{}, errs Errors) { - if testCase.shouldSucceedOnJson && len(errs) > 0 { - So(len(errs), ShouldEqual, 0) - } else if !testCase.shouldSucceedOnJson && len(errs) == 0 { - So(len(errs), ShouldNotEqual, 0) - } - So(fmt.Sprintf("%+v", actual), ShouldEqual, fmt.Sprintf("%+v", testCase.expected)) - } - - switch testCase.expected.(type) { - case []Post: - if testCase.withInterface { - m.Post(testRoute, binder([]Post{}, (*modeler)(nil)), func(actual []Post, iface modeler, errs Errors) { - - for _, a := range actual { - So(a.Title, ShouldEqual, iface.Model()) - jsonTestHandler(a, errs) - } - }) - } else { - m.Post(testRoute, binder([]Post{}), func(actual []Post, errs Errors) { - jsonTestHandler(actual, errs) - }) - } - - case Post: - if testCase.withInterface { - m.Post(testRoute, binder(Post{}, (*modeler)(nil)), func(actual Post, iface modeler, errs Errors) { - So(actual.Title, ShouldEqual, iface.Model()) - jsonTestHandler(actual, errs) - }) - } else { - m.Post(testRoute, binder(Post{}), func(actual Post, errs Errors) { - jsonTestHandler(actual, errs) - }) - } - - case BlogPost: - if testCase.withInterface { - m.Post(testRoute, binder(BlogPost{}, (*modeler)(nil)), func(actual BlogPost, iface modeler, errs Errors) { - So(actual.Title, ShouldEqual, iface.Model()) - jsonTestHandler(actual, errs) - }) - } else { - m.Post(testRoute, binder(BlogPost{}), func(actual BlogPost, errs Errors) { - jsonTestHandler(actual, errs) - }) - } - } - - if testCase.payload == "-nil-" { - payload = nil - } else { - payload = strings.NewReader(testCase.payload) - } - - req, err := http.NewRequest("POST", testRoute, payload) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", testCase.contentType) - - m.ServeHTTP(httpRecorder, req) - - switch httpRecorder.Code { - case http.StatusNotFound: - panic("Routing is messed up in test fixture (got 404): check method and path") - case http.StatusInternalServerError: - panic("Something bad happened on '" + testCase.description + "'") - default: - if testCase.shouldSucceedOnJson && - httpRecorder.Code != http.StatusOK && - !testCase.shouldFailOnBind { - So(httpRecorder.Code, ShouldEqual, http.StatusOK) - } - } -} - -type ( - jsonTestCase struct { - description string - withInterface bool - shouldSucceedOnJson bool - shouldFailOnBind bool - payload string - contentType string - expected interface{} - } -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/misc_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/misc_test.go deleted file mode 100644 index 29f72f627d2..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/misc_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -// When binding from Form data, testing the type of data to bind -// and converting a string into that type is tedious, so these tests -// cover all those cases. -func Test_SetWithProperType(t *testing.T) { - Convey("Set with proper type", t, func() { - testInputs := map[string]string{ - "successful": `integer=-1&integer8=-8&integer16=-16&integer32=-32&integer64=-64&uinteger=1&uinteger8=8&uinteger16=16&uinteger32=32&uinteger64=64&boolean_1=true&fl32_1=32.3232&fl64_1=-64.6464646464&str=string`, - "errorful": `integer=&integer8=asdf&integer16=--&integer32=&integer64=dsf&uinteger=&uinteger8=asdf&uinteger16=+&uinteger32= 32 &uinteger64=+%20+&boolean_1=&boolean_2=asdf&fl32_1=asdf&fl32_2=&fl64_1=&fl64_2=asdfstr`, - } - - expectedOutputs := map[string]Everything{ - "successful": Everything{ - Integer: -1, - Integer8: -8, - Integer16: -16, - Integer32: -32, - Integer64: -64, - Uinteger: 1, - Uinteger8: 8, - Uinteger16: 16, - Uinteger32: 32, - Uinteger64: 64, - Boolean_1: true, - Fl32_1: 32.3232, - Fl64_1: -64.6464646464, - Str: "string", - }, - "errorful": Everything{}, - } - - for key, testCase := range testInputs { - httpRecorder := httptest.NewRecorder() - m := macaron.Classic() - - m.Post(testRoute, Form(Everything{}), func(actual Everything, errs Errors) { - So(fmt.Sprintf("%+v", actual), ShouldEqual, fmt.Sprintf("%+v", expectedOutputs[key])) - }) - req, err := http.NewRequest("POST", testRoute, strings.NewReader(testCase)) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", formContentType) - m.ServeHTTP(httpRecorder, req) - } - }) -} - -// Each binder middleware should assert that the struct passed in is not -// a pointer (to avoid race conditions) -func Test_EnsureNotPointer(t *testing.T) { - Convey("Ensure field is not a pointer", t, func() { - shouldPanic := func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - ensureNotPointer(&Post{}) - } - - shouldNotPanic := func() { - defer func() { - So(recover(), ShouldBeNil) - }() - ensureNotPointer(Post{}) - } - - shouldPanic() - shouldNotPanic() - }) -} - -// Used in testing setWithProperType; kind of clunky... -type Everything struct { - Integer int `form:"integer"` - Integer8 int8 `form:"integer8"` - Integer16 int16 `form:"integer16"` - Integer32 int32 `form:"integer32"` - Integer64 int64 `form:"integer64"` - Uinteger uint `form:"uinteger"` - Uinteger8 uint8 `form:"uinteger8"` - Uinteger16 uint16 `form:"uinteger16"` - Uinteger32 uint32 `form:"uinteger32"` - Uinteger64 uint64 `form:"uinteger64"` - Boolean_1 bool `form:"boolean_1"` - Boolean_2 bool `form:"boolean_2"` - Fl32_1 float32 `form:"fl32_1"` - Fl32_2 float32 `form:"fl32_2"` - Fl64_1 float64 `form:"fl64_1"` - Fl64_2 float64 `form:"fl64_2"` - Str string `form:"str"` -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/multipart_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/multipart_test.go deleted file mode 100644 index fef10523593..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/multipart_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "bytes" - "fmt" - "mime/multipart" - "net/http" - "net/http/httptest" - "strconv" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -var multipartFormTestCases = []multipartFormTestCase{ - { - description: "Happy multipart form path", - shouldSucceed: true, - inputAndExpected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "FormValue called before req.MultipartReader(); see https://github.com/martini-contrib/csrf/issues/6", - shouldSucceed: true, - callFormValueBefore: true, - inputAndExpected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Empty payload", - shouldSucceed: false, - inputAndExpected: BlogPost{}, - }, - { - description: "Missing required field (Id)", - shouldSucceed: false, - inputAndExpected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Required embedded struct field not specified", - shouldSucceed: false, - inputAndExpected: BlogPost{Id: 1, Author: Person{Name: "Matt Holt"}}, - }, - { - description: "Required nested struct field not specified", - shouldSucceed: false, - inputAndExpected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1}, - }, - { - description: "Multiple values", - shouldSucceed: true, - inputAndExpected: BlogPost{Post: Post{Title: "Glorious Post Title"}, Id: 1, Author: Person{Name: "Matt Holt"}, Ratings: []int{3, 5, 4}}, - }, - { - description: "Bad multipart encoding", - shouldSucceed: false, - malformEncoding: true, - }, -} - -func Test_MultipartForm(t *testing.T) { - Convey("Test multipart form", t, func() { - for _, testCase := range multipartFormTestCases { - performMultipartFormTest(t, MultipartForm, testCase) - } - }) -} - -func performMultipartFormTest(t *testing.T, binder handlerFunc, testCase multipartFormTestCase) { - httpRecorder := httptest.NewRecorder() - m := macaron.Classic() - - m.Post(testRoute, binder(BlogPost{}), func(actual BlogPost, errs Errors) { - if testCase.shouldSucceed && len(errs) > 0 { - So(len(errs), ShouldEqual, 0) - } else if !testCase.shouldSucceed && len(errs) == 0 { - So(len(errs), ShouldNotEqual, 0) - } - So(fmt.Sprintf("%+v", actual), ShouldEqual, fmt.Sprintf("%+v", testCase.inputAndExpected)) - }) - - multipartPayload, mpWriter := makeMultipartPayload(testCase) - - req, err := http.NewRequest("POST", testRoute, multipartPayload) - if err != nil { - panic(err) - } - - req.Header.Add("Content-Type", mpWriter.FormDataContentType()) - - err = mpWriter.Close() - if err != nil { - panic(err) - } - - if testCase.callFormValueBefore { - req.FormValue("foo") - } - - m.ServeHTTP(httpRecorder, req) - - switch httpRecorder.Code { - case http.StatusNotFound: - panic("Routing is messed up in test fixture (got 404): check methods and paths") - case http.StatusInternalServerError: - panic("Something bad happened on '" + testCase.description + "'") - } -} - -// Writes the input from a test case into a buffer using the multipart writer. -func makeMultipartPayload(testCase multipartFormTestCase) (*bytes.Buffer, *multipart.Writer) { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - if testCase.malformEncoding { - // TODO: Break the multipart form parser which is apparently impervious!! - // (Get it to return an error. Trying to get 100% test coverage.) - body.Write([]byte(`--` + writer.Boundary() + `\nContent-Disposition: form-data; name="foo"\n\n--` + writer.Boundary() + `--`)) - return body, writer - } else { - writer.WriteField("title", testCase.inputAndExpected.Title) - writer.WriteField("content", testCase.inputAndExpected.Content) - writer.WriteField("id", strconv.Itoa(testCase.inputAndExpected.Id)) - writer.WriteField("ignored", testCase.inputAndExpected.Ignored) - for _, value := range testCase.inputAndExpected.Ratings { - writer.WriteField("rating", strconv.Itoa(value)) - } - writer.WriteField("name", testCase.inputAndExpected.Author.Name) - writer.WriteField("email", testCase.inputAndExpected.Author.Email) - return body, writer - } -} - -type ( - multipartFormTestCase struct { - description string - shouldSucceed bool - inputAndExpected BlogPost - malformEncoding bool - callFormValueBefore bool - } -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/binding/validate_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/binding/validate_test.go deleted file mode 100644 index 66d31e159e4..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/binding/validate_test.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 martini-contrib/binding Authors -// Copyright 2014 Unknwon -// -// 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 binding - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -var validationTestCases = []validationTestCase{ - { - description: "No errors", - data: BlogPost{ - Id: 1, - Post: Post{ - Title: "Behold The Title!", - Content: "And some content", - }, - Author: Person{ - Name: "Matt Holt", - }, - }, - expectedErrors: Errors{}, - }, - { - description: "ID required", - data: BlogPost{ - Post: Post{ - Title: "Behold The Title!", - Content: "And some content", - }, - Author: Person{ - Name: "Matt Holt", - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"id"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - }, - }, - { - description: "Embedded struct field required", - data: BlogPost{ - Id: 1, - Post: Post{ - Content: "Content given, but title is required", - }, - Author: Person{ - Name: "Matt Holt", - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"title"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - Error{ - FieldNames: []string{"title"}, - Classification: "LengthError", - Message: "Life is too short", - }, - }, - }, - { - description: "Nested struct field required", - data: BlogPost{ - Id: 1, - Post: Post{ - Title: "Behold The Title!", - Content: "And some content", - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"name"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - }, - }, - { - description: "Required field missing in nested struct pointer", - data: BlogPost{ - Id: 1, - Post: Post{ - Title: "Behold The Title!", - Content: "And some content", - }, - Author: Person{ - Name: "Matt Holt", - }, - Coauthor: &Person{}, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"name"}, - Classification: ERR_REQUIRED, - Message: "Required", - }, - }, - }, - { - description: "All required fields specified in nested struct pointer", - data: BlogPost{ - Id: 1, - Post: Post{ - Title: "Behold The Title!", - Content: "And some content", - }, - Author: Person{ - Name: "Matt Holt", - }, - Coauthor: &Person{ - Name: "Jeremy Saenz", - }, - }, - expectedErrors: Errors{}, - }, - { - description: "Custom validation should put an error", - data: BlogPost{ - Id: 1, - Post: Post{ - Title: "Too short", - Content: "And some content", - }, - Author: Person{ - Name: "Matt Holt", - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"title"}, - Classification: "LengthError", - Message: "Life is too short", - }, - }, - }, - { - description: "List Validation", - data: []BlogPost{ - BlogPost{ - Id: 1, - Post: Post{ - Title: "First Post", - Content: "And some content", - }, - Author: Person{ - Name: "Leeor Aharon", - }, - }, - BlogPost{ - Id: 2, - Post: Post{ - Title: "Second Post", - Content: "And some content", - }, - Author: Person{ - Name: "Leeor Aharon", - }, - }, - }, - expectedErrors: Errors{}, - }, - { - description: "List Validation w/ Errors", - data: []BlogPost{ - BlogPost{ - Id: 1, - Post: Post{ - Title: "First Post", - Content: "And some content", - }, - Author: Person{ - Name: "Leeor Aharon", - }, - }, - BlogPost{ - Id: 2, - Post: Post{ - Title: "Too Short", - Content: "And some content", - }, - Author: Person{ - Name: "Leeor Aharon", - }, - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"title"}, - Classification: "LengthError", - Message: "Life is too short", - }, - }, - }, - { - description: "List of invalid custom validations", - data: []SadForm{ - SadForm{ - AlphaDash: ",", - AlphaDashDot: ",", - MinSize: ",", - MinSizeSlice: []string{",", ","}, - MaxSize: ",,", - MaxSizeSlice: []string{",", ","}, - Range: 3, - Email: ",", - Url: ",", - UrlEmpty: "", - InInvalid: "4", - NotIn: "1", - Include: "def", - Exclude: "abc", - }, - }, - expectedErrors: Errors{ - Error{ - FieldNames: []string{"AlphaDash"}, - Classification: "AlphaDashError", - Message: "AlphaDash", - }, - Error{ - FieldNames: []string{"AlphaDashDot"}, - Classification: "AlphaDashDot", - Message: "AlphaDashDot", - }, - Error{ - FieldNames: []string{"MinSize"}, - Classification: "MinSize", - Message: "MinSize", - }, - Error{ - FieldNames: []string{"MinSize"}, - Classification: "MinSize", - Message: "MinSize", - }, - Error{ - FieldNames: []string{"MaxSize"}, - Classification: "MaxSize", - Message: "MaxSize", - }, - Error{ - FieldNames: []string{"MaxSize"}, - Classification: "MaxSize", - Message: "MaxSize", - }, - Error{ - FieldNames: []string{"Range"}, - Classification: "Range", - Message: "Range", - }, - Error{ - FieldNames: []string{"Email"}, - Classification: "Email", - Message: "Email", - }, - Error{ - FieldNames: []string{"Url"}, - Classification: "Url", - Message: "Url", - }, - Error{ - FieldNames: []string{"Default"}, - Classification: "Default", - Message: "Default", - }, - Error{ - FieldNames: []string{"InInvalid"}, - Classification: "In", - Message: "In", - }, - Error{ - FieldNames: []string{"NotIn"}, - Classification: "NotIn", - Message: "NotIn", - }, - Error{ - FieldNames: []string{"Include"}, - Classification: "Include", - Message: "Include", - }, - Error{ - FieldNames: []string{"Exclude"}, - Classification: "Exclude", - Message: "Exclude", - }, - }, - }, - { - description: "List of valid custom validations", - data: []SadForm{ - SadForm{ - AlphaDash: "123-456", - AlphaDashDot: "123.456", - MinSize: "12345", - MinSizeSlice: []string{"1", "2", "3", "4", "5"}, - MaxSize: "1", - MaxSizeSlice: []string{"1"}, - Range: 2, - In: "1", - InInvalid: "1", - Email: "123@456.com", - Url: "http://123.456", - Include: "abc", - }, - }, - }, -} - -func Test_Validation(t *testing.T) { - Convey("Test validation", t, func() { - for _, testCase := range validationTestCases { - performValidationTest(t, testCase) - } - }) -} - -func performValidationTest(t *testing.T, testCase validationTestCase) { - httpRecorder := httptest.NewRecorder() - m := macaron.Classic() - - m.Post(testRoute, Validate(testCase.data), func(actual Errors) { - So(fmt.Sprintf("%+v", actual), ShouldEqual, fmt.Sprintf("%+v", testCase.expectedErrors)) - }) - - req, err := http.NewRequest("POST", testRoute, nil) - if err != nil { - panic(err) - } - - m.ServeHTTP(httpRecorder, req) - - switch httpRecorder.Code { - case http.StatusNotFound: - panic("Routing is messed up in test fixture (got 404): check methods and paths") - case http.StatusInternalServerError: - panic("Something bad happened on '" + testCase.description + "'") - } -} - -type ( - validationTestCase struct { - description string - data interface{} - expectedErrors Errors - } -) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md b/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md deleted file mode 100644 index 01de811eacc..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md +++ /dev/null @@ -1,21 +0,0 @@ -session [![Build Status](https://drone.io/github.com/macaron-contrib/session/status.png)](https://drone.io/github.com/macaron-contrib/session/latest) [![](http://gocover.io/_badge/github.com/macaron-contrib/session)](http://gocover.io/github.com/macaron-contrib/session) -======= - -Middleware session provides session management for [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase, Ledis and Nodb. - -### Installation - - go get github.com/macaron-contrib/session - -## Getting Help - -- [API Reference](https://gowalker.org/github.com/macaron-contrib/session) -- [Documentation](http://macaron.gogs.io/docs/middlewares/session) - -## Credits - -This package is forked from [beego/session](https://github.com/astaxie/beego/tree/master/session) with reconstruction(over 80%). - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/file_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/file_test.go deleted file mode 100644 index 9c83555ab4e..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/file_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "os" - "path" - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_FileProvider(t *testing.T) { - Convey("Test file session provider", t, func() { - dir := path.Join(os.TempDir(), "data/sessions") - os.RemoveAll(dir) - testProvider(Options{ - Provider: "file", - ProviderConfig: dir, - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go deleted file mode 100644 index dac42a364b7..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_LedisProvider(t *testing.T) { - Convey("Test ledis session provider", t, func() { - opt := session.Options{ - Provider: "ledis", - ProviderConfig: "data_dir=./tmp.db", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - Convey("Regenrate empty session", func() { - m.Get("/empty", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/empty", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") - m.ServeHTTP(resp, req) - }) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go deleted file mode 100644 index beb272d52b1..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_MemcacheProvider(t *testing.T) { - Convey("Test memcache session provider", t, func() { - opt := session.Options{ - Provider: "memcache", - ProviderConfig: "127.0.0.1:9090", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - }) - - Convey("Regenrate empty session", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") - m.ServeHTTP(resp, req) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/memory_test.go deleted file mode 100644 index 41659bb6a1e..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func Test_MemProvider(t *testing.T) { - Convey("Test memory session provider", t, func() { - testProvider(Options{}) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go deleted file mode 100644 index 15b3996a228..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_MysqlProvider(t *testing.T) { - Convey("Test mysql session provider", t, func() { - opt := session.Options{ - Provider: "mysql", - ProviderConfig: "root:@tcp(localhost:3306)/macaron?charset=utf8", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - So(raw.Release(), ShouldBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - }) - - Convey("Regenrate empty session", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf48; Path=/;") - m.ServeHTTP(resp, req) - }) - - Convey("GC session", func() { - m := macaron.New() - opt2 := opt - opt2.Gclifetime = 1 - m.Use(session.Sessioner(opt2)) - - m.Get("/", func(sess session.Store) { - sess.Set("uname", "unknwon") - So(sess.ID(), ShouldNotBeEmpty) - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Flush(), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - time.Sleep(2 * time.Second) - sess.GC() - So(sess.Count(), ShouldEqual, 0) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go deleted file mode 100644 index c86ba98ded5..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_LedisProvider(t *testing.T) { - Convey("Test nodb session provider", t, func() { - opt := session.Options{ - Provider: "nodb", - ProviderConfig: "./tmp.db", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - Convey("Regenrate empty session", func() { - m.Get("/empty", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - }) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/empty", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") - m.ServeHTTP(resp, req) - }) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go deleted file mode 100644 index ea212c729f6..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_PostgresProvider(t *testing.T) { - Convey("Test postgres session provider", t, func() { - opt := session.Options{ - Provider: "postgres", - ProviderConfig: "user=jiahuachen dbname=macaron port=5432 sslmode=disable", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - So(raw.Release(), ShouldBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - }) - - Convey("Regenrate empty session", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf48; Path=/;") - m.ServeHTTP(resp, req) - }) - - Convey("GC session", func() { - m := macaron.New() - opt2 := opt - opt2.Gclifetime = 1 - m.Use(session.Sessioner(opt2)) - - m.Get("/", func(sess session.Store) { - sess.Set("uname", "unknwon") - So(sess.ID(), ShouldNotBeEmpty) - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Flush(), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - time.Sleep(2 * time.Second) - sess.GC() - So(sess.Count(), ShouldEqual, 0) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go deleted file mode 100644 index 9fd8e6518f8..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" - - "github.com/macaron-contrib/session" -) - -func Test_RedisProvider(t *testing.T) { - Convey("Test redis session provider", t, func() { - opt := session.Options{ - Provider: "redis", - ProviderConfig: "addr=:6379", - } - - Convey("Basic operation", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess session.Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - }) - - Convey("Regenrate empty session", func() { - m := macaron.New() - m.Use(session.Sessioner(opt)) - m.Get("/", func(ctx *macaron.Context, sess session.Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") - m.ServeHTTP(resp, req) - }) - }) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go deleted file mode 100644 index 82efc277c61..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 session - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Unknwon/macaron" - . "github.com/smartystreets/goconvey/convey" -) - -func Test_Version(t *testing.T) { - Convey("Check package version", t, func() { - So(Version(), ShouldEqual, _VERSION) - }) -} - -func Test_Sessioner(t *testing.T) { - Convey("Use session middleware", t, func() { - m := macaron.New() - m.Use(Sessioner()) - m.Get("/", func() {}) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) - - Convey("Register invalid provider", t, func() { - Convey("Provider not exists", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - - m := macaron.New() - m.Use(Sessioner(Options{ - Provider: "fake", - })) - }) - - Convey("Provider value is nil", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - - Register("fake", nil) - }) - - Convey("Register twice", func() { - defer func() { - So(recover(), ShouldNotBeNil) - }() - - Register("memory", &MemProvider{}) - }) - }) -} - -func testProvider(opt Options) { - Convey("Basic operation", func() { - m := macaron.New() - m.Use(Sessioner(opt)) - - m.Get("/", func(ctx *macaron.Context, sess Store) { - sess.Set("uname", "unknwon") - }) - m.Get("/reg", func(ctx *macaron.Context, sess Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := raw.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - }) - m.Get("/get", func(ctx *macaron.Context, sess Store) { - sid := sess.ID() - So(sid, ShouldNotBeEmpty) - - raw, err := sess.Read(sid) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Delete("uname"), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - So(sess.Destory(ctx), ShouldBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - cookie := resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/reg", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - - cookie = resp.Header().Get("Set-Cookie") - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", cookie) - m.ServeHTTP(resp, req) - }) - - Convey("Regenrate empty session", func() { - m := macaron.New() - m.Use(Sessioner(opt)) - m.Get("/", func(ctx *macaron.Context, sess Store) { - raw, err := sess.RegenerateId(ctx) - So(err, ShouldBeNil) - So(raw, ShouldNotBeNil) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") - m.ServeHTTP(resp, req) - }) - - Convey("GC session", func() { - m := macaron.New() - opt2 := opt - opt2.Gclifetime = 1 - m.Use(Sessioner(opt2)) - - m.Get("/", func(sess Store) { - sess.Set("uname", "unknwon") - So(sess.ID(), ShouldNotBeEmpty) - uname := sess.Get("uname") - So(uname, ShouldNotBeNil) - So(uname, ShouldEqual, "unknwon") - - So(sess.Flush(), ShouldBeNil) - So(sess.Get("uname"), ShouldBeNil) - - time.Sleep(2 * time.Second) - sess.GC() - So(sess.Count(), ShouldEqual, 0) - }) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - }) -} - -func Test_Flash(t *testing.T) { - Convey("Test flash", t, func() { - m := macaron.New() - m.Use(Sessioner()) - m.Get("/set", func(f *Flash) string { - f.Success("success") - f.Error("error") - f.Warning("warning") - f.Info("info") - return "" - }) - m.Get("/get", func() {}) - - resp := httptest.NewRecorder() - req, err := http.NewRequest("GET", "/set", nil) - So(err, ShouldBeNil) - m.ServeHTTP(resp, req) - - resp = httptest.NewRecorder() - req, err = http.NewRequest("GET", "/get", nil) - So(err, ShouldBeNil) - req.Header.Set("Cookie", "macaron_flash=error%3Derror%26info%3Dinfo%26success%3Dsuccess%26warning%3Dwarning; Path=/") - m.ServeHTTP(resp, req) - }) -} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/.gitignore b/Godeps/_workspace/src/gopkg.in/macaron.v1/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/.gitignore rename to Godeps/_workspace/src/gopkg.in/macaron.v1/.gitignore diff --git a/Godeps/_workspace/src/gopkg.in/macaron.v1/.travis.yml b/Godeps/_workspace/src/gopkg.in/macaron.v1/.travis.yml new file mode 100644 index 00000000000..6eeb32a4e38 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/.travis.yml @@ -0,0 +1,13 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE b/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE new file mode 100644 index 00000000000..37ec93a14fd --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/LICENSE @@ -0,0 +1,191 @@ +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +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 +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. diff --git a/Godeps/_workspace/src/gopkg.in/macaron.v1/README.md b/Godeps/_workspace/src/gopkg.in/macaron.v1/README.md new file mode 100644 index 00000000000..704ecf34077 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/README.md @@ -0,0 +1,94 @@ +Macaron [![Build Status](https://travis-ci.org/go-macaron/macaron.svg?branch=v1)](https://travis-ci.org/go-macaron/macaron) [![](http://gocover.io/_badge/github.com/go-macaron/macaron)](http://gocover.io/github.com/go-macaron/macaron) +======================= + +![Macaron Logo](https://raw.githubusercontent.com/go-macaron/macaron/v1/macaronlogo.png) + +Package macaron is a high productive and modular web framework in Go. + +##### Current version: 0.8.0 + +## Getting Started + +The minimum requirement of Go is **1.3**. + +To install Macaron: + + go get gopkg.in/macaron.v1 + +The very basic usage of Macaron: + +```go +package main + +import "gopkg.in/macaron.v1" + +func main() { + m := macaron.Classic() + m.Get("/", func() string { + return "Hello world!" + }) + m.Run() +} +``` + +## Features + +- Powerful routing with suburl. +- Flexible routes combinations. +- Unlimited nested group routers. +- Directly integrate with existing services. +- Dynamically change template files at runtime. +- Allow to use in-memory template and static files. +- Easy to plugin/unplugin features with modular design. +- Handy dependency injection powered by [inject](https://github.com/codegangsta/inject). +- Better router layer and less reflection make faster speed. + +## Middlewares + +Middlewares allow you easily plugin/unplugin features for your Macaron applications. + +There are already many [middlewares](https://github.com/go-macaron) to simplify your work: + +- render - Go template engine +- static - Serves static files +- [gzip](https://github.com/go-macaron/gzip) - Gzip compression to all responses +- [binding](https://github.com/go-macaron/binding) - Request data binding and validation +- [i18n](https://github.com/go-macaron/i18n) - Internationalization and Localization +- [cache](https://github.com/go-macaron/cache) - Cache manager +- [session](https://github.com/go-macaron/session) - Session manager +- [csrf](https://github.com/go-macaron/csrf) - Generates and validates csrf tokens +- [captcha](https://github.com/go-macaron/captcha) - Captcha service +- [pongo2](https://github.com/go-macaron/pongo2) - Pongo2 template engine support +- [sockets](https://github.com/go-macaron/sockets) - WebSockets channels binding +- [bindata](https://github.com/go-macaron/bindata) - Embed binary data as static and template files +- [toolbox](https://github.com/go-macaron/toolbox) - Health check, pprof, profile and statistic services +- [oauth2](https://github.com/go-macaron/oauth2) - OAuth 2.0 backend +- [switcher](https://github.com/go-macaron/switcher) - Multiple-site support +- [method](https://github.com/go-macaron/method) - HTTP method override +- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions +- [renders](https://github.com/go-macaron/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option) + +## Use Cases + +- [Gogs](http://gogs.io): A painless self-hosted Git Service +- [Peach](http://peachdocs.org): A modern web documentation server +- [Go Walker](https://gowalker.org): Go online API documentation +- [Switch](http://gopm.io): Gopm registry +- [YouGam](http://yougam.com): Online Forum +- [Critical Stack Intel](https://intel.criticalstack.com/): A 100% free intel marketplace from Critical Stack, Inc. + +## Getting Help + +- [API Reference](https://gowalker.org/gopkg.in/macaron.v1) +- [Documentation](http://go-macaron.com) +- [FAQs](http://go-macaron.com/docs/faqs) +- [![Join the chat at https://gitter.im/Unknwon/macaron](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-macaron/macaron?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Credits + +- Basic design of [Martini](https://github.com/go-martini/martini). +- Logo is modified by [@insionng](https://github.com/insionng) based on [Tribal Dragon](http://xtremeyamazaki.deviantart.com/art/Tribal-Dragon-27005087). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/context.go similarity index 92% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/context.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/context.go index abf7ac90257..bb6caa41414 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/context.go @@ -1,4 +1,4 @@ -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -33,7 +33,7 @@ import ( "github.com/Unknwon/com" - "github.com/Unknwon/macaron/inject" + "github.com/go-macaron/inject" ) // Locale reprents a localization interface. @@ -176,11 +176,27 @@ func (ctx *Context) Redirect(location string, status ...int) { http.Redirect(ctx.Resp, ctx.Req.Request, location, code) } -// Query querys form parameter. -func (ctx *Context) Query(name string) string { - if ctx.Req.Form == nil { +// Maximum amount of memory to use when parsing a multipart form. +// Set this to whatever value you prefer; default is 10 MB. +var MaxMemory = int64(1024 * 1024 * 10) + +func (ctx *Context) parseForm() { + if ctx.Req.Form != nil { + return + } + + contentType := ctx.Req.Header.Get(_CONTENT_TYPE) + if (ctx.Req.Method == "POST" || ctx.Req.Method == "PUT") && + len(contentType) > 0 && strings.Contains(contentType, "multipart/form-data") { + ctx.Req.ParseMultipartForm(MaxMemory) + } else { ctx.Req.ParseForm() } +} + +// Query querys form parameter. +func (ctx *Context) Query(name string) string { + ctx.parseForm() return ctx.Req.Form.Get(name) } @@ -191,9 +207,7 @@ func (ctx *Context) QueryTrim(name string) string { // QueryStrings returns a list of results by given query name. func (ctx *Context) QueryStrings(name string) []string { - if ctx.Req.Form == nil { - ctx.Req.ParseForm() - } + ctx.parseForm() vals, ok := ctx.Req.Form[name] if !ok { @@ -229,7 +243,7 @@ func (ctx *Context) Params(name string) string { if len(name) == 0 { return "" } - if name[0] != '*' && name[0] != ':' { + if len(name) > 1 && name[0] != ':' { name = ":" + name } return ctx.params[name] @@ -273,6 +287,24 @@ func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, return ctx.Req.FormFile(name) } +// SaveToFile reads a file from request by field name and saves to given path. +func (ctx *Context) SaveToFile(name, savePath string) error { + fr, _, err := ctx.GetFile(name) + if err != nil { + return err + } + defer fr.Close() + + fw, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer fw.Close() + + _, err = io.Copy(fw, fr) + return err +} + // SetCookie sets given cookie value to response header. // FIXME: IE support? http://golanghome.com/post/620#reply2 func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/admin/index.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/admin/index.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/admin/index.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/admin/index.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/another_layout.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/another_layout.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/another_layout.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/another_layout.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/content.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/content.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/content.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/content.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/current_layout.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/current_layout.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/current_layout.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/current_layout.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/delims.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/delims.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/delims.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/delims.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/hello.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/hello.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/hello.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/hello.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/hypertext.html b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/hypertext.html similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/hypertext.html rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/hypertext.html diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/layout.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/layout.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic/layout.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic/layout.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic2/hello.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic2/hello.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic2/hello.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic2/hello.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic2/hello2.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic2/hello2.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/basic2/hello2.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/basic2/hello2.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/custom_funcs/index.tmpl b/Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/custom_funcs/index.tmpl similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/fixtures/custom_funcs/index.tmpl rename to Godeps/_workspace/src/gopkg.in/macaron.v1/fixtures/custom_funcs/index.tmpl diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/logger.go similarity index 97% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/logger.go index 6e0b25c9b1f..109b7b3330d 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/logger.go @@ -1,5 +1,5 @@ // Copyright 2013 Martini Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/macaron.go similarity index 92% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/macaron.go index adbe9e3692d..94fd3f0ce20 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/macaron.go @@ -1,4 +1,6 @@ -// Copyright 2014 Unknwon +// +build go1.3 + +// Copyright 2014 The Macaron Authors // // 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 @@ -12,7 +14,7 @@ // License for the specific language governing permissions and limitations // under the License. -// Package macaron is a high productive and modular design web framework in Go. +// Package macaron is a high productive and modular web framework in Go. package macaron import ( @@ -26,10 +28,10 @@ import ( "github.com/Unknwon/com" "gopkg.in/ini.v1" - "github.com/Unknwon/macaron/inject" + "github.com/go-macaron/inject" ) -const _VERSION = "0.5.4.0318" +const _VERSION = "0.8.0.1013" func Version() string { return _VERSION @@ -64,7 +66,8 @@ type Macaron struct { handlers []Handler action Handler - urlPrefix string // For suburl support. + hasURLPrefix bool + urlPrefix string // For suburl support. *Router logger *log.Logger @@ -83,11 +86,10 @@ func NewWithLogger(out io.Writer) *Macaron { m.Router.m = m m.Map(m.logger) m.Map(defaultReturnHandler()) - m.notFound = func(resp http.ResponseWriter, req *http.Request) { - c := m.createContext(resp, req) - c.handlers = append(c.handlers, http.NotFound) - c.run() - } + m.NotFound(http.NotFound) + m.InternalServerError(func(rw http.ResponseWriter, err error) { + http.Error(rw, err.Error(), 500) + }) return m } @@ -162,7 +164,9 @@ func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Cont // Useful if you want to control your own HTTP server. // Be aware that none of middleware will run without registering any router. func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix) + if m.hasURLPrefix { + req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix) + } for _, h := range m.befores { if h(rw, req) { return @@ -203,7 +207,7 @@ func (m *Macaron) Run(args ...interface{}) { } addr := host + ":" + com.ToStr(port) - logger := m.Injector.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger) + logger := m.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger) logger.Printf("listening on %s (%s)\n", addr, Env) logger.Fatalln(http.ListenAndServe(addr, m)) } @@ -211,6 +215,7 @@ func (m *Macaron) Run(args ...interface{}) { // SetURLPrefix sets URL prefix of router layer, so that it support suburl. func (m *Macaron) SetURLPrefix(prefix string) { m.urlPrefix = prefix + m.hasURLPrefix = len(m.urlPrefix) > 0 } // ____ ____ .__ ___. .__ diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaronlogo.png b/Godeps/_workspace/src/gopkg.in/macaron.v1/macaronlogo.png similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/macaronlogo.png rename to Godeps/_workspace/src/gopkg.in/macaron.v1/macaronlogo.png diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/recovery.go similarity index 98% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/recovery.go index 6ff1659e8ad..ea3bdac045b 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/recovery.go @@ -1,5 +1,5 @@ // Copyright 2013 Martini Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -23,7 +23,7 @@ import ( "net/http" "runtime" - "github.com/Unknwon/macaron/inject" + "github.com/go-macaron/inject" ) const ( diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/render.go similarity index 87% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/render.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/render.go index b0558c95742..3706e4fe414 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/render.go @@ -1,6 +1,5 @@ // Copyright 2013 Martini Authors -// Copyright 2013 oxtoacart -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -22,7 +21,6 @@ import ( "encoding/xml" "fmt" "html/template" - "io" "io/ioutil" "net/http" "os" @@ -35,52 +33,23 @@ import ( "github.com/Unknwon/com" ) -// BufferPool implements a pool of bytes.Buffers in the form of a bounded channel. -type BufferPool struct { - c chan *bytes.Buffer -} - -// NewBufferPool creates a new BufferPool bounded to the given size. -func NewBufferPool(size int) (bp *BufferPool) { - return &BufferPool{ - c: make(chan *bytes.Buffer, size), - } -} - -// Get gets a Buffer from the BufferPool, or creates a new one if none are available -// in the pool. -func (bp *BufferPool) Get() (b *bytes.Buffer) { - select { - case b = <-bp.c: - // reuse existing buffer - default: - // create new buffer - b = bytes.NewBuffer([]byte{}) - } - return -} - -// Put returns the given Buffer to the BufferPool. -func (bp *BufferPool) Put(b *bytes.Buffer) { - b.Reset() - bp.c <- b -} - const ( - ContentType = "Content-Type" - ContentLength = "Content-Length" - ContentBinary = "application/octet-stream" - ContentJSON = "application/json" - ContentHTML = "text/html" - CONTENT_PLAIN = "text/plain" - ContentXHTML = "application/xhtml+xml" - ContentXML = "text/xml" - defaultCharset = "UTF-8" + _CONTENT_TYPE = "Content-Type" + _CONTENT_LENGTH = "Content-Length" + _CONTENT_BINARY = "application/octet-stream" + _CONTENT_JSON = "application/json" + _CONTENT_HTML = "text/html" + _CONTENT_PLAIN = "text/plain" + _CONTENT_XHTML = "application/xhtml+xml" + _CONTENT_XML = "text/xml" + _DEFAULT_CHARSET = "UTF-8" ) var ( // Provides a temporary buffer to execute templates into and catch errors. - bufpool = NewBufferPool(64) + bufpool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, + } // Included helper functions for use when rendering html helperFuncs = template.FuncMap{ @@ -149,12 +118,12 @@ type ( Render interface { http.ResponseWriter - RW() http.ResponseWriter + SetResponseWriter(http.ResponseWriter) JSON(int, interface{}) JSONString(interface{}) (string, error) - RawData(int, []byte) - RenderData(int, []byte) + RawData(int, []byte) // Serve content as binary + PlainText(int, []byte) // Serve content as plain text HTML(int, string, interface{}, ...HTMLOptions) HTMLSet(int, string, string, interface{}, ...HTMLOptions) HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) @@ -244,7 +213,7 @@ func PrepareCharset(charset string) string { return "; charset=" + charset } - return "; charset=" + defaultCharset + return "; charset=" + _DEFAULT_CHARSET } func GetExt(s string) string { @@ -321,7 +290,7 @@ func (ts *templateSet) GetDir(name string) string { return ts.dirs[name] } -func prepareOptions(options []RenderOptions) RenderOptions { +func prepareRenderOptions(options []RenderOptions) RenderOptions { var opt RenderOptions if len(options) > 0 { opt = options[0] @@ -335,7 +304,7 @@ func prepareOptions(options []RenderOptions) RenderOptions { opt.Extensions = []string{".tmpl", ".html"} } if len(opt.HTMLContentType) == 0 { - opt.HTMLContentType = ContentHTML + opt.HTMLContentType = _CONTENT_HTML } return opt @@ -401,11 +370,11 @@ func renderHandler(opt RenderOptions, tplSets []string) Handler { // If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the // MACARON_ENV environment variable to "production". func Renderer(options ...RenderOptions) Handler { - return renderHandler(prepareOptions(options), []string{}) + return renderHandler(prepareRenderOptions(options), []string{}) } func Renderers(options RenderOptions, tplSets ...string) Handler { - return renderHandler(prepareOptions([]RenderOptions{options}), tplSets) + return renderHandler(prepareRenderOptions([]RenderOptions{options}), tplSets) } type TplRender struct { @@ -417,8 +386,8 @@ type TplRender struct { startTime time.Time } -func (r *TplRender) RW() http.ResponseWriter { - return r.ResponseWriter +func (r *TplRender) SetResponseWriter(rw http.ResponseWriter) { + r.ResponseWriter = rw } func (r *TplRender) JSON(status int, v interface{}) { @@ -437,7 +406,7 @@ func (r *TplRender) JSON(status int, v interface{}) { } // json rendered fine, write out the result - r.Header().Set(ContentType, ContentJSON+r.CompiledCharset) + r.Header().Set(_CONTENT_TYPE, _CONTENT_JSON+r.CompiledCharset) r.WriteHeader(status) if len(r.Opt.PrefixJSON) > 0 { r.Write(r.Opt.PrefixJSON) @@ -473,7 +442,7 @@ func (r *TplRender) XML(status int, v interface{}) { } // XML rendered fine, write out the result - r.Header().Set(ContentType, ContentXML+r.CompiledCharset) + r.Header().Set(_CONTENT_TYPE, _CONTENT_XML+r.CompiledCharset) r.WriteHeader(status) if len(r.Opt.PrefixXML) > 0 { r.Write(r.Opt.PrefixXML) @@ -482,23 +451,23 @@ func (r *TplRender) XML(status int, v interface{}) { } func (r *TplRender) data(status int, contentType string, v []byte) { - if r.Header().Get(ContentType) == "" { - r.Header().Set(ContentType, contentType) + if r.Header().Get(_CONTENT_TYPE) == "" { + r.Header().Set(_CONTENT_TYPE, contentType) } r.WriteHeader(status) r.Write(v) } func (r *TplRender) RawData(status int, v []byte) { - r.data(status, ContentBinary, v) + r.data(status, _CONTENT_BINARY, v) } -func (r *TplRender) RenderData(status int, v []byte) { - r.data(status, CONTENT_PLAIN, v) +func (r *TplRender) PlainText(status int, v []byte) { + r.data(status, _CONTENT_PLAIN, v) } func (r *TplRender) execute(t *template.Template, name string, data interface{}) (*bytes.Buffer, error) { - buf := bufpool.Get() + buf := bufpool.Get().(*bytes.Buffer) return buf, t.ExecuteTemplate(buf, name, data) } @@ -551,10 +520,10 @@ func (r *TplRender) renderHTML(status int, setName, tplName string, data interfa return } - r.Header().Set(ContentType, r.Opt.HTMLContentType+r.CompiledCharset) + r.Header().Set(_CONTENT_TYPE, r.Opt.HTMLContentType+r.CompiledCharset) r.WriteHeader(status) - io.Copy(r, out) + out.WriteTo(r) bufpool.Put(out) } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/response_writer.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/response_writer.go similarity index 100% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/response_writer.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/response_writer.go diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/return_handler.go similarity index 74% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/return_handler.go index ea1e0447646..db6eec3e9fa 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/return_handler.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/return_handler.go @@ -1,5 +1,5 @@ // Copyright 2013 Martini Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -19,7 +19,7 @@ import ( "net/http" "reflect" - "github.com/Unknwon/macaron/inject" + "github.com/go-macaron/inject" ) // ReturnHandler is a service that Martini provides that is called @@ -32,6 +32,11 @@ func canDeref(val reflect.Value) bool { return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr } +func isError(val reflect.Value) bool { + _, ok := val.Interface().(error) + return ok +} + func isByteSlice(val reflect.Value) bool { return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 } @@ -39,21 +44,33 @@ func isByteSlice(val reflect.Value) bool { func defaultReturnHandler() ReturnHandler { return func(ctx *Context, vals []reflect.Value) { rv := ctx.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil))) - res := rv.Interface().(http.ResponseWriter) + resp := rv.Interface().(http.ResponseWriter) var respVal reflect.Value if len(vals) > 1 && vals[0].Kind() == reflect.Int { - res.WriteHeader(int(vals[0].Int())) + resp.WriteHeader(int(vals[0].Int())) respVal = vals[1] } else if len(vals) > 0 { respVal = vals[0] + + if isError(respVal) { + err := respVal.Interface().(error) + if err != nil { + ctx.internalServerError(ctx, err) + } + return + } else if canDeref(respVal) { + if respVal.IsNil() { + return // Ignore nil error + } + } } if canDeref(respVal) { respVal = respVal.Elem() } if isByteSlice(respVal) { - res.Write(respVal.Bytes()) + resp.Write(respVal.Bytes()) } else { - res.Write([]byte(respVal.String())) + resp.Write([]byte(respVal.String())) } } } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/router.go similarity index 58% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/router.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/router.go index d2b394540b8..f9b421a3302 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/router.go @@ -1,4 +1,4 @@ -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 @@ -18,8 +18,6 @@ import ( "net/http" "strings" "sync" - - "github.com/Unknwon/com" ) var ( @@ -38,22 +36,22 @@ var ( // routeMap represents a thread-safe map for route tree. type routeMap struct { lock sync.RWMutex - routes map[string]map[string]bool + routes map[string]map[string]*Leaf } // NewRouteMap initializes and returns a new routeMap. func NewRouteMap() *routeMap { rm := &routeMap{ - routes: make(map[string]map[string]bool), + routes: make(map[string]map[string]*Leaf), } for m := range _HTTP_METHODS { - rm.routes[m] = make(map[string]bool) + rm.routes[m] = make(map[string]*Leaf) } return rm } -// isExist returns true if a route has been registered. -func (rm *routeMap) isExist(method, pattern string) bool { +// getLeaf returns Leaf object if a route has been registered. +func (rm *routeMap) getLeaf(method, pattern string) *Leaf { rm.lock.RLock() defer rm.lock.RUnlock() @@ -61,11 +59,11 @@ func (rm *routeMap) isExist(method, pattern string) bool { } // add adds new route to route tree map. -func (rm *routeMap) add(method, pattern string) { +func (rm *routeMap) add(method, pattern string, leaf *Leaf) { rm.lock.Lock() defer rm.lock.Unlock() - rm.routes[method][pattern] = true + rm.routes[method][pattern] = leaf } type group struct { @@ -75,34 +73,61 @@ type group struct { // Router represents a Macaron router layer. type Router struct { - m *Macaron - routers map[string]*Tree + m *Macaron + autoHead bool + routers map[string]*Tree *routeMap + namedRoutes map[string]*Leaf - groups []group - notFound http.HandlerFunc + groups []group + notFound http.HandlerFunc + internalServerError func(*Context, error) } func NewRouter() *Router { return &Router{ - routers: make(map[string]*Tree), - routeMap: NewRouteMap(), + routers: make(map[string]*Tree), + routeMap: NewRouteMap(), + namedRoutes: make(map[string]*Leaf), } } +// SetAutoHead sets the value who determines whether add HEAD method automatically +// when GET method is added. Combo router will not be affected by this value. +func (r *Router) SetAutoHead(v bool) { + r.autoHead = v +} + type Params map[string]string // Handle is a function that can be registered to a route to handle HTTP requests. // Like http.HandlerFunc, but has a third parameter for the values of wildcards (variables). type Handle func(http.ResponseWriter, *http.Request, Params) +// Route represents a wrapper of leaf route and upper level router. +type Route struct { + router *Router + leaf *Leaf +} + +// Name sets name of route. +func (r *Route) Name(name string) { + if len(name) == 0 { + panic("route name cannot be empty") + } else if r.router.namedRoutes[name] != nil { + panic("route with given name already exists") + } + r.router.namedRoutes[name] = r.leaf +} + // handle adds new route to the router tree. -func (r *Router) handle(method, pattern string, handle Handle) { +func (r *Router) handle(method, pattern string, handle Handle) *Route { method = strings.ToUpper(method) + var leaf *Leaf // Prevent duplicate routes. - if r.isExist(method, pattern) { - return + if leaf = r.getLeaf(method, pattern); leaf != nil { + return &Route{r, leaf} } // Validate HTTP methods. @@ -123,18 +148,19 @@ func (r *Router) handle(method, pattern string, handle Handle) { // Add to router tree. for m := range methods { if t, ok := r.routers[m]; ok { - t.AddRouter(pattern, handle) + leaf = t.Add(pattern, handle) } else { t := NewTree() - t.AddRouter(pattern, handle) + leaf = t.Add(pattern, handle) r.routers[m] = t } - r.add(m, pattern) + r.add(m, pattern, leaf) } + return &Route{r, leaf} } // Handle registers a new request handle with the given pattern, method and handlers. -func (r *Router) Handle(method string, pattern string, handlers []Handler) { +func (r *Router) Handle(method string, pattern string, handlers []Handler) *Route { if len(r.groups) > 0 { groupPattern := "" h := make([]Handler, 0) @@ -149,7 +175,7 @@ func (r *Router) Handle(method string, pattern string, handlers []Handler) { } validateHandlers(handlers) - r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) { + return r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) { c := r.m.createContext(resp, req) c.params = params c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers)) @@ -166,64 +192,70 @@ func (r *Router) Group(pattern string, fn func(), h ...Handler) { } // Get is a shortcut for r.Handle("GET", pattern, handlers) -func (r *Router) Get(pattern string, h ...Handler) { - r.Handle("GET", pattern, h) +func (r *Router) Get(pattern string, h ...Handler) (leaf *Route) { + leaf = r.Handle("GET", pattern, h) + if r.autoHead { + r.Head(pattern, h...) + } + return leaf } // Patch is a shortcut for r.Handle("PATCH", pattern, handlers) -func (r *Router) Patch(pattern string, h ...Handler) { - r.Handle("PATCH", pattern, h) +func (r *Router) Patch(pattern string, h ...Handler) *Route { + return r.Handle("PATCH", pattern, h) } // Post is a shortcut for r.Handle("POST", pattern, handlers) -func (r *Router) Post(pattern string, h ...Handler) { - r.Handle("POST", pattern, h) +func (r *Router) Post(pattern string, h ...Handler) *Route { + return r.Handle("POST", pattern, h) } // Put is a shortcut for r.Handle("PUT", pattern, handlers) -func (r *Router) Put(pattern string, h ...Handler) { - r.Handle("PUT", pattern, h) +func (r *Router) Put(pattern string, h ...Handler) *Route { + return r.Handle("PUT", pattern, h) } // Delete is a shortcut for r.Handle("DELETE", pattern, handlers) -func (r *Router) Delete(pattern string, h ...Handler) { - r.Handle("DELETE", pattern, h) +func (r *Router) Delete(pattern string, h ...Handler) *Route { + return r.Handle("DELETE", pattern, h) } // Options is a shortcut for r.Handle("OPTIONS", pattern, handlers) -func (r *Router) Options(pattern string, h ...Handler) { - r.Handle("OPTIONS", pattern, h) +func (r *Router) Options(pattern string, h ...Handler) *Route { + return r.Handle("OPTIONS", pattern, h) } // Head is a shortcut for r.Handle("HEAD", pattern, handlers) -func (r *Router) Head(pattern string, h ...Handler) { - r.Handle("HEAD", pattern, h) +func (r *Router) Head(pattern string, h ...Handler) *Route { + return r.Handle("HEAD", pattern, h) } // Any is a shortcut for r.Handle("*", pattern, handlers) -func (r *Router) Any(pattern string, h ...Handler) { - r.Handle("*", pattern, h) +func (r *Router) Any(pattern string, h ...Handler) *Route { + return r.Handle("*", pattern, h) } // Route is a shortcut for same handlers but different HTTP methods. // // Example: // m.Route("/", "GET,POST", h) -func (r *Router) Route(pattern, methods string, h ...Handler) { +func (r *Router) Route(pattern, methods string, h ...Handler) (route *Route) { for _, m := range strings.Split(methods, ",") { - r.Handle(strings.TrimSpace(m), pattern, h) + route = r.Handle(strings.TrimSpace(m), pattern, h) } + return route } // Combo returns a combo router. func (r *Router) Combo(pattern string, h ...Handler) *ComboRouter { - return &ComboRouter{r, pattern, h, map[string]bool{}} + return &ComboRouter{r, pattern, h, map[string]bool{}, nil} } // Configurable http.HandlerFunc which is called when no matching route is // found. If it is not set, http.NotFound is used. // Be sure to set 404 response code in your handler. func (r *Router) NotFound(handlers ...Handler) { + validateHandlers(handlers) r.notFound = func(rw http.ResponseWriter, req *http.Request) { c := r.m.createContext(rw, req) c.handlers = append(r.m.handlers, handlers...) @@ -231,16 +263,25 @@ func (r *Router) NotFound(handlers ...Handler) { } } +// Configurable handler which is called when route handler returns +// error. If it is not set, default handler is used. +// Be sure to set 500 response code in your handler. +func (r *Router) InternalServerError(handlers ...Handler) { + validateHandlers(handlers) + r.internalServerError = func(c *Context, err error) { + c.index = 0 + c.handlers = handlers + c.Map(err) + c.run() + } +} + func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if t, ok := r.routers[req.Method]; ok { - h, p := t.Match(req.URL.Path) - if h != nil { - if splat, ok := p[":splat"]; ok { - p["*"] = p[":splat"] // Better name. - splatlist := strings.Split(splat, "/") - for k, v := range splatlist { - p[com.ToStr(k)] = v - } + h, p, ok := t.Match(req.URL.Path) + if ok { + if splat, ok := p["*0"]; ok { + p["*"] = splat // Easy name. } h(rw, req, p) return @@ -250,12 +291,23 @@ func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { r.notFound(rw, req) } +// URLFor builds path part of URL by given pair values. +func (r *Router) URLFor(name string, pairs ...string) string { + leaf, ok := r.namedRoutes[name] + if !ok { + panic("route with given name does not exists: " + name) + } + return leaf.URLPath(pairs...) +} + // ComboRouter represents a combo router. type ComboRouter struct { router *Router pattern string handlers []Handler methods map[string]bool // Registered methods. + + lastRoute *Route } func (cr *ComboRouter) checkMethod(name string) { @@ -265,9 +317,9 @@ func (cr *ComboRouter) checkMethod(name string) { cr.methods[name] = true } -func (cr *ComboRouter) route(fn func(string, ...Handler), method string, h ...Handler) *ComboRouter { +func (cr *ComboRouter) route(fn func(string, ...Handler) *Route, method string, h ...Handler) *ComboRouter { cr.checkMethod(method) - fn(cr.pattern, append(cr.handlers, h...)...) + cr.lastRoute = fn(cr.pattern, append(cr.handlers, h...)...) return cr } @@ -298,3 +350,11 @@ func (cr *ComboRouter) Options(h ...Handler) *ComboRouter { func (cr *ComboRouter) Head(h ...Handler) *ComboRouter { return cr.route(cr.router.Head, "HEAD", h...) } + +// Name sets name of ComboRouter route. +func (cr *ComboRouter) Name(name string) { + if cr.lastRoute == nil { + panic("no corresponding route to be named") + } + cr.lastRoute.Name(name) +} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/static.go similarity index 99% rename from Godeps/_workspace/src/github.com/Unknwon/macaron/static.go rename to Godeps/_workspace/src/gopkg.in/macaron.v1/static.go index 69d523d6203..4ff8342fc5e 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/static.go @@ -1,5 +1,5 @@ // Copyright 2013 Martini Authors -// Copyright 2014 Unknwon +// Copyright 2014 The Macaron Authors // // 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 diff --git a/Godeps/_workspace/src/gopkg.in/macaron.v1/tree.go b/Godeps/_workspace/src/gopkg.in/macaron.v1/tree.go new file mode 100644 index 00000000000..8bba72fb340 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/macaron.v1/tree.go @@ -0,0 +1,379 @@ +// Copyright 2015 The Macaron Authors +// +// 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 macaron + +import ( + "regexp" + "strings" + + "github.com/Unknwon/com" +) + +type patternType int8 + +const ( + _PATTERN_STATIC patternType = iota // /home + _PATTERN_REGEXP // /:id([0-9]+) + _PATTERN_PATH_EXT // /*.* + _PATTERN_HOLDER // /:user + _PATTERN_MATCH_ALL // /* +) + +// Leaf represents a leaf route information. +type Leaf struct { + parent *Tree + + typ patternType + pattern string + rawPattern string // Contains wildcard instead of regexp + wildcards []string + reg *regexp.Regexp + optional bool + + handle Handle +} + +var wildcardPattern = regexp.MustCompile(`:[a-zA-Z0-9]+`) + +func isSpecialRegexp(pattern, regStr string, pos []int) bool { + return len(pattern) >= pos[1]+len(regStr) && pattern[pos[1]:pos[1]+len(regStr)] == regStr +} + +// getNextWildcard tries to find next wildcard and update pattern with corresponding regexp. +func getNextWildcard(pattern string) (wildcard, _ string) { + pos := wildcardPattern.FindStringIndex(pattern) + if pos == nil { + return "", pattern + } + wildcard = pattern[pos[0]:pos[1]] + + // Reach last character or no regexp is given. + if len(pattern) == pos[1] { + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } else if pattern[pos[1]] != '(' { + switch { + case isSpecialRegexp(pattern, ":int", pos): + pattern = strings.Replace(pattern, ":int", "([0-9]+)", 1) + case isSpecialRegexp(pattern, ":string", pos): + pattern = strings.Replace(pattern, ":string", "([\\w]+)", 1) + default: + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } + } + + // Cut out placeholder directly. + return wildcard, pattern[:pos[0]] + pattern[pos[1]:] +} + +func getWildcards(pattern string) (string, []string) { + wildcards := make([]string, 0, 2) + + // Keep getting next wildcard until nothing is left. + var wildcard string + for { + wildcard, pattern = getNextWildcard(pattern) + if len(wildcard) > 0 { + wildcards = append(wildcards, wildcard) + } else { + break + } + } + + return pattern, wildcards +} + +// getRawPattern removes all regexp but keeps wildcards for building URL path. +func getRawPattern(rawPattern string) string { + rawPattern = strings.Replace(rawPattern, ":int", "", -1) + rawPattern = strings.Replace(rawPattern, ":string", "", -1) + + for { + startIdx := strings.Index(rawPattern, "(") + if startIdx == -1 { + break + } + + closeIdx := strings.Index(rawPattern, ")") + if closeIdx > -1 { + rawPattern = rawPattern[:startIdx] + rawPattern[closeIdx+1:] + } + } + return rawPattern +} + +func checkPattern(pattern string) (typ patternType, rawPattern string, wildcards []string, reg *regexp.Regexp) { + pattern = strings.TrimLeft(pattern, "?") + rawPattern = getRawPattern(pattern) + + if pattern == "*" { + typ = _PATTERN_MATCH_ALL + } else if pattern == "*.*" { + typ = _PATTERN_PATH_EXT + } else if strings.Contains(pattern, ":") { + typ = _PATTERN_REGEXP + pattern, wildcards = getWildcards(pattern) + if pattern == "(.+)" { + typ = _PATTERN_HOLDER + } else { + reg = regexp.MustCompile(pattern) + } + } + return typ, rawPattern, wildcards, reg +} + +func NewLeaf(parent *Tree, pattern string, handle Handle) *Leaf { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + optional := false + if len(pattern) > 0 && pattern[0] == '?' { + optional = true + } + return &Leaf{parent, typ, pattern, rawPattern, wildcards, reg, optional, handle} +} + +// URLPath build path part of URL by given pair values. +func (l *Leaf) URLPath(pairs ...string) string { + if len(pairs)%2 != 0 { + panic("number of pairs does not match") + } + + urlPath := l.rawPattern + parent := l.parent + for parent != nil { + urlPath = parent.rawPattern + "/" + urlPath + parent = parent.parent + } + for i := 0; i < len(pairs); i += 2 { + if len(pairs[i]) == 0 { + panic("pair value cannot be empty: " + com.ToStr(i)) + } else if pairs[i][0] != ':' && pairs[i] != "*" && pairs[i] != "*.*" { + pairs[i] = ":" + pairs[i] + } + urlPath = strings.Replace(urlPath, pairs[i], pairs[i+1], 1) + } + return urlPath +} + +// Tree represents a router tree in Macaron. +type Tree struct { + parent *Tree + + typ patternType + pattern string + rawPattern string + wildcards []string + reg *regexp.Regexp + + subtrees []*Tree + leaves []*Leaf +} + +func NewSubtree(parent *Tree, pattern string) *Tree { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + return &Tree{parent, typ, pattern, rawPattern, wildcards, reg, make([]*Tree, 0, 5), make([]*Leaf, 0, 5)} +} + +func NewTree() *Tree { + return NewSubtree(nil, "") +} + +func (t *Tree) addLeaf(pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.leaves); i++ { + if t.leaves[i].pattern == pattern { + return t.leaves[i] + } + } + + leaf := NewLeaf(t, pattern, handle) + + // Add exact same leaf to grandparent/parent level without optional. + if leaf.optional { + parent := leaf.parent + if parent.parent != nil { + parent.parent.addLeaf(parent.pattern, handle) + } else { + parent.addLeaf("", handle) // Root tree can add as empty pattern. + } + } + + i := 0 + for ; i < len(t.leaves); i++ { + if leaf.typ < t.leaves[i].typ { + break + } + } + + if i == len(t.leaves) { + t.leaves = append(t.leaves, leaf) + } else { + t.leaves = append(t.leaves[:i], append([]*Leaf{leaf}, t.leaves[i:]...)...) + } + return leaf +} + +func (t *Tree) addSubtree(segment, pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.subtrees); i++ { + if t.subtrees[i].pattern == segment { + return t.subtrees[i].addNextSegment(pattern, handle) + } + } + + subtree := NewSubtree(t, segment) + i := 0 + for ; i < len(t.subtrees); i++ { + if subtree.typ < t.subtrees[i].typ { + break + } + } + + if i == len(t.subtrees) { + t.subtrees = append(t.subtrees, subtree) + } else { + t.subtrees = append(t.subtrees[:i], append([]*Tree{subtree}, t.subtrees[i:]...)...) + } + return subtree.addNextSegment(pattern, handle) +} + +func (t *Tree) addNextSegment(pattern string, handle Handle) *Leaf { + pattern = strings.TrimPrefix(pattern, "/") + + i := strings.Index(pattern, "/") + if i == -1 { + return t.addLeaf(pattern, handle) + } + return t.addSubtree(pattern[:i], pattern[i+1:], handle) +} + +func (t *Tree) Add(pattern string, handle Handle) *Leaf { + pattern = strings.TrimSuffix(pattern, "/") + return t.addNextSegment(pattern, handle) +} + +func (t *Tree) matchLeaf(globLevel int, url string, params Params) (Handle, bool) { + for i := 0; i < len(t.leaves); i++ { + switch t.leaves[i].typ { + case _PATTERN_STATIC: + if t.leaves[i].pattern == url { + return t.leaves[i].handle, true + } + case _PATTERN_REGEXP: + results := t.leaves[i].reg.FindStringSubmatch(url) + // Number of results and wildcasrd should be exact same. + if len(results)-1 != len(t.leaves[i].wildcards) { + break + } + + for j := 0; j < len(t.leaves[i].wildcards); j++ { + params[t.leaves[i].wildcards[j]] = results[j+1] + } + return t.leaves[i].handle, true + case _PATTERN_PATH_EXT: + j := strings.LastIndex(url, ".") + if j > -1 { + params[":path"] = url[:j] + params[":ext"] = url[j+1:] + } else { + params[":path"] = url + } + return t.leaves[i].handle, true + case _PATTERN_HOLDER: + params[t.leaves[i].wildcards[0]] = url + return t.leaves[i].handle, true + case _PATTERN_MATCH_ALL: + params["*"] = url + params["*"+com.ToStr(globLevel)] = url + return t.leaves[i].handle, true + } + } + return nil, false +} + +func (t *Tree) matchSubtree(globLevel int, segment, url string, params Params) (Handle, bool) { + for i := 0; i < len(t.subtrees); i++ { + switch t.subtrees[i].typ { + case _PATTERN_STATIC: + if t.subtrees[i].pattern == segment { + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + } + case _PATTERN_REGEXP: + results := t.subtrees[i].reg.FindStringSubmatch(segment) + if len(results)-1 != len(t.subtrees[i].wildcards) { + break + } + + for j := 0; j < len(t.subtrees[i].wildcards); j++ { + params[t.subtrees[i].wildcards[j]] = results[j+1] + } + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + case _PATTERN_HOLDER: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params[t.subtrees[i].wildcards[0]] = segment + return handle, true + } + case _PATTERN_MATCH_ALL: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params["*"+com.ToStr(globLevel)] = segment + return handle, true + } + } + } + + if len(t.leaves) > 0 { + leaf := t.leaves[len(t.leaves)-1] + if leaf.typ == _PATTERN_PATH_EXT { + url = segment + "/" + url + j := strings.LastIndex(url, ".") + if j > -1 { + params[":path"] = url[:j] + params[":ext"] = url[j+1:] + } else { + params[":path"] = url + } + return leaf.handle, true + } else if leaf.typ == _PATTERN_MATCH_ALL { + params["*"] = segment + "/" + url + params["*"+com.ToStr(globLevel)] = segment + "/" + url + return leaf.handle, true + } + } + return nil, false +} + +func (t *Tree) matchNextSegment(globLevel int, url string, params Params) (Handle, bool) { + i := strings.Index(url, "/") + if i == -1 { + return t.matchLeaf(globLevel, url, params) + } + return t.matchSubtree(globLevel, url[:i], url[i+1:], params) +} + +func (t *Tree) Match(url string) (Handle, Params, bool) { + url = strings.TrimPrefix(url, "/") + url = strings.TrimSuffix(url, "/") + params := make(Params) + handle, ok := t.matchNextSegment(0, url, params) + return handle, params, ok +} + +// MatchTest returns true if given URL is matched by given pattern. +func MatchTest(pattern, url string) bool { + t := NewTree() + t.Add(pattern, nil) + _, _, ok := t.Match(url) + return ok +} diff --git a/pkg/api/api.go b/pkg/api/api.go index a893ffd72e1..1770b210439 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -1,11 +1,11 @@ package api import ( - "github.com/Unknwon/macaron" + "github.com/go-macaron/binding" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" - "github.com/macaron-contrib/binding" + "gopkg.in/macaron.v1" ) // Register adds http routes diff --git a/pkg/api/common.go b/pkg/api/common.go index 28e95866402..5a7d48a5cbe 100644 --- a/pkg/api/common.go +++ b/pkg/api/common.go @@ -4,11 +4,11 @@ import ( "encoding/json" "net/http" - "github.com/Unknwon/macaron" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" ) var ( diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 7193198155f..77742a7a9f3 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -102,6 +102,6 @@ func ProxyDataSourceRequest(c *middleware.Context) { proxyPath := c.Params("*") proxy := NewReverseProxy(ds, proxyPath, targetUrl) proxy.Transport = dataProxyTransport - proxy.ServeHTTP(c.RW(), c.Req.Request) + proxy.ServeHTTP(c.Resp, c.Req.Request) } } diff --git a/pkg/api/static/static.go b/pkg/api/static/static.go index 43ba6a32b20..7a61c85b4f3 100644 --- a/pkg/api/static/static.go +++ b/pkg/api/static/static.go @@ -24,7 +24,7 @@ import ( "strings" "sync" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" ) var Root string diff --git a/pkg/cmd/web.go b/pkg/cmd/web.go index ff7f8a053bc..40ef22d8024 100644 --- a/pkg/cmd/web.go +++ b/pkg/cmd/web.go @@ -8,7 +8,7 @@ import ( "net/http" "path" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" "github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api/static" diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 2497183b356..be3415d990b 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -4,7 +4,7 @@ import ( "net/url" "strings" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" diff --git a/pkg/middleware/logger.go b/pkg/middleware/logger.go index 9e0c91f69e8..4cb8acc6354 100644 --- a/pkg/middleware/logger.go +++ b/pkg/middleware/logger.go @@ -20,9 +20,9 @@ import ( "net/http" "time" - "github.com/Unknwon/macaron" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" ) func Logger() macaron.Handler { diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 9ad417606f5..7a51fd4e8d8 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/Unknwon/macaron" + "gopkg.in/macaron.v1" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/apikeygen" diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 97d369d00cf..cb37a809212 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -7,13 +7,13 @@ import ( "path/filepath" "testing" - "github.com/Unknwon/macaron" + "github.com/go-macaron/session" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" - "github.com/macaron-contrib/session" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/macaron.v1" ) func TestMiddlewareContext(t *testing.T) { diff --git a/pkg/middleware/quota.go b/pkg/middleware/quota.go index f6ba74d77df..d9e68f7560a 100644 --- a/pkg/middleware/quota.go +++ b/pkg/middleware/quota.go @@ -3,11 +3,11 @@ package middleware import ( "fmt" - "github.com/Unknwon/macaron" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" ) func Quota(target string) macaron.Handler { diff --git a/pkg/middleware/session.go b/pkg/middleware/session.go index bad912aa46f..583c57b85a5 100644 --- a/pkg/middleware/session.go +++ b/pkg/middleware/session.go @@ -3,12 +3,12 @@ package middleware import ( "time" - "github.com/Unknwon/macaron" - "github.com/macaron-contrib/session" - _ "github.com/macaron-contrib/session/memcache" - _ "github.com/macaron-contrib/session/mysql" - _ "github.com/macaron-contrib/session/postgres" - _ "github.com/macaron-contrib/session/redis" + "github.com/go-macaron/session" + _ "github.com/go-macaron/session/memcache" + _ "github.com/go-macaron/session/mysql" + _ "github.com/go-macaron/session/postgres" + _ "github.com/go-macaron/session/redis" + "gopkg.in/macaron.v1" ) const ( diff --git a/pkg/middleware/util.go b/pkg/middleware/util.go index 0823e4de4bf..babda94d5e7 100644 --- a/pkg/middleware/util.go +++ b/pkg/middleware/util.go @@ -3,11 +3,12 @@ package middleware import ( "strings" - "github.com/Unknwon/macaron" + "github.com/go-macaron/gzip" + "gopkg.in/macaron.v1" ) func Gziper() macaron.Handler { - macaronGziper := macaron.Gziper() + macaronGziper := gzip.Gziper() return func(ctx *macaron.Context) { requestPath := ctx.Req.URL.RequestURI() diff --git a/pkg/middleware/validate_host.go b/pkg/middleware/validate_host.go index 56e0a8ee35a..606606b56e4 100644 --- a/pkg/middleware/validate_host.go +++ b/pkg/middleware/validate_host.go @@ -3,8 +3,8 @@ package middleware import ( "strings" - "github.com/Unknwon/macaron" "github.com/grafana/grafana/pkg/setting" + "gopkg.in/macaron.v1" ) func ValidateHostHeader(domain string) macaron.Handler { diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 420e06aa6b3..3de0e4b558f 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -16,7 +16,7 @@ import ( "runtime" "strings" - "github.com/macaron-contrib/session" + "github.com/go-macaron/session" "gopkg.in/ini.v1" "github.com/grafana/grafana/pkg/log"