@cryptotaxi247 / kubo / commits / c5a69db60

updated godeps

Henry committed Mar 23, 2015 at 09:51 UTC c5a69db601bf291fc42b1a92f04377bd9eadb88e
89 files changed +3406 -5312
Godeps/Godeps.json
+12 -20
@@ -7,7 +7,7 @@
7 "Deps": [
8 {
9 "ImportPath": "bazil.org/fuse",
10 - "Rev": "489e985dcb429718328be2bb055adb685c4ae1e3"
10 + "Rev": "79d103f5608724e3ccee0a10daccc5e4aff03591"
11 },
12 {
13 "ImportPath": "code.google.com/p/go-uuid/uuid",
@@ -33,8 +33,8 @@
33 },
34 {
35 "ImportPath": "github.com/Sirupsen/logrus",
36 - "Comment": "v0.6.5",
37 - "Rev": "c0f7e35ed2e48f188c37581b4b743cf7383f85c6"
36 + "Comment": "v0.7.1",
37 + "Rev": "3fc34d061b9c78a70db853c7cb6b0576b6d4f32d"
38 },
39 {
40 "ImportPath": "github.com/braintree/manners",
@@ -67,7 +67,7 @@
67 },
68 {
69 "ImportPath": "github.com/coreos/go-semver/semver",
70 - "Rev": "6fe83ccda8fb9b7549c9ab4ba47f47858bc950aa"
70 + "Rev": "568e959cd89871e61434c1143528d9162da89ef2"
71 },
72 {
73 "ImportPath": "github.com/crowdmob/goamz/aws",
@@ -102,14 +102,6 @@
102 "Comment": "v0.5.1",
103 "Rev": "27a863cdffdb0998d13e1e11992b18489aeeaa25"
104 },
105 - {
106 - "ImportPath": "github.com/gorilla/context",
107 - "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
108 - },
109 - {
110 - "ImportPath": "github.com/gorilla/mux",
111 - "Rev": "4b8fbc56f3b2400a7c7ea3dba9b3539787c486b6"
112 - },
105 {
106 "ImportPath": "github.com/h2so5/utp",
107 "Rev": "654d875bb65e96729678180215cf080fe2810371"
@@ -141,7 +133,7 @@
133 },
134 {
135 "ImportPath": "github.com/jbenet/go-base58",
144 - "Rev": "568a28d73fd97651d3442392036a658b6976eed5"
136 + "Rev": "6237cf65f3a6f7111cd8a42be3590df99a66bc7d"
137 },
138 {
139 "ImportPath": "github.com/jbenet/go-ctxgroup",
@@ -195,7 +187,7 @@
187 },
188 {
189 "ImportPath": "github.com/jbenet/go-reuseport",
198 - "Rev": "096958438ae3683c9f3c8ae0f7139b5ce600e6a8"
190 + "Rev": "c71a70ef82a7acb87ad77704de1be7b9cd776a55"
191 },
192 {
193 "ImportPath": "github.com/jbenet/go-sockaddr/net",
@@ -211,7 +203,7 @@
203 },
204 {
205 "ImportPath": "github.com/kardianos/osext",
214 - "Rev": "ccfcd0245381f0c94c68f50626665eed3c6b726a"
206 + "Rev": "efacde03154693404c65e7aa7d461ac9014acd0c"
207 },
208 {
209 "ImportPath": "github.com/kr/binarydist",
@@ -223,23 +215,23 @@
215 },
216 {
217 "ImportPath": "github.com/syndtr/goleveldb/leveldb",
226 - "Rev": "871eee0a7546bb7d1b2795142e29c4534abc49b3"
218 + "Rev": "87e4e645d80ae9c537e8f2dee52b28036a5dd75e"
219 },
220 {
221 "ImportPath": "github.com/syndtr/gosnappy/snappy",
230 - "Rev": "ce8acff4829e0c2458a67ead32390ac0a381c862"
222 + "Rev": "156a073208e131d7d2e212cb749feae7c339e846"
223 },
224 {
225 "ImportPath": "golang.org/x/crypto/blowfish",
234 - "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d"
226 + "Rev": "b7d6bf2c61544745a02f83dec90393985fc3a065"
227 },
228 {
229 "ImportPath": "golang.org/x/crypto/sha3",
238 - "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d"
230 + "Rev": "b7d6bf2c61544745a02f83dec90393985fc3a065"
231 },
232 {
233 "ImportPath": "golang.org/x/net/context",
242 - "Rev": "b6fdb7d8a4ccefede406f8fe0f017fb58265054c"
234 + "Rev": "7dbad50ab5b31073856416cdcfeb2796d682f844"
235 },
236 {
237 "ImportPath": "gopkg.in/fsnotify.v1",
Godeps/_workspace/src/bazil.org/fuse/error_darwin.go
+8 -27
@@ -4,33 +4,14 @@ import (
4 "syscall"
5 )
6
7 -type getxattrError struct {
8 - error
9 -}
10 -
11 -func (getxattrError) Errno() Errno {
12 - return Errno(syscall.ENOATTR)
13 -}
14 -
15 -// getxattr return value for "extended attribute does not exist" is
16 -// ENOATTR on OS X, and ENODATA on Linux and apparently at least
17 -// NetBSD. There may be a #define ENOATTR too, but the value is
18 -// ENODATA in the actual syscalls. ENOATTR is not in any of the
19 -// standards, ENODATA exists but is only used for STREAMs.
20 -//
21 -// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
22 -// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html
23 -// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html
24 -// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
25 -func translateGetxattrError(err error) error {
26 - ferr, ok := err.(ErrorNumber)
27 - if !ok {
28 - return err
29 - }
7 +const (
8 + ENOATTR = Errno(syscall.ENOATTR)
9 +)
10
31 - if ferr.Errno() != ENODATA {
32 - return err
33 - }
11 +const (
12 + errNoXattr = ENOATTR
13 +)
14
35 - return getxattrError{err}
15 +func init() {
16 + errnoNames[errNoXattr] = "ENOATTR"
17 }
Godeps/_workspace/src/bazil.org/fuse/error_freebsd.go new
+15
@@ -0,0 +1,15 @@
1 +package fuse
2 +
3 +import "syscall"
4 +
5 +const (
6 + ENOATTR = Errno(syscall.ENOATTR)
7 +)
8 +
9 +const (
10 + errNoXattr = ENOATTR
11 +)
12 +
13 +func init() {
14 + errnoNames[errNoXattr] = "ENOATTR"
15 +}
Godeps/_workspace/src/bazil.org/fuse/error_linux.go new
+17
@@ -0,0 +1,17 @@
1 +package fuse
2 +
3 +import (
4 + "syscall"
5 +)
6 +
7 +const (
8 + ENODATA = Errno(syscall.ENODATA)
9 +)
10 +
11 +const (
12 + errNoXattr = ENODATA
13 +)
14 +
15 +func init() {
16 + errnoNames[errNoXattr] = "ENODATA"
17 +}
Godeps/_workspace/src/bazil.org/fuse/error_std.go
+29 -5
@@ -1,7 +1,31 @@
1 -// +build !darwin
2 -
1 package fuse
2
5 -func translateGetxattrError(err error) error {
6 - return err
7 -}
3 +// There is very little commonality in extended attribute errors
4 +// across platforms.
5 +//
6 +// getxattr return value for "extended attribute does not exist" is
7 +// ENOATTR on OS X, and ENODATA on Linux and apparently at least
8 +// NetBSD. There may be a #define ENOATTR on Linux too, but the value
9 +// is ENODATA in the actual syscalls. FreeBSD and OpenBSD have no
10 +// ENODATA, only ENOATTR. ENOATTR is not in any of the standards,
11 +// ENODATA exists but is only used for STREAMs.
12 +//
13 +// Each platform will define it a errNoXattr constant, and this file
14 +// will enforce that it implements the right interfaces and hide the
15 +// implementation.
16 +//
17 +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
18 +// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html
19 +// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html
20 +// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
21 +// http://www.freebsd.org/cgi/man.cgi?query=extattr_get_file&sektion=2
22 +// http://nixdoc.net/man-pages/openbsd/man2/extattr_get_file.2.html
23 +
24 +// ErrNoXattr is a platform-independent error value meaning the
25 +// extended attribute was not found. It can be used to respond to
26 +// GetxattrRequest and such.
27 +const ErrNoXattr = errNoXattr
28 +
29 +var _ error = ErrNoXattr
30 +var _ Errno = ErrNoXattr
31 +var _ ErrorNumber = ErrNoXattr
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo.go
+12
@@ -12,3 +12,15 @@ type MountInfo struct {
12 func GetMountInfo(mnt string) (*MountInfo, error) {
13 return getMountInfo(mnt)
14 }
15 +
16 +// cstr converts a nil-terminated C string into a Go string
17 +func cstr(ca []int8) string {
18 + s := make([]byte, 0, len(ca))
19 + for _, c := range ca {
20 + if c == 0x00 {
21 + break
22 + }
23 + s = append(s, byte(c))
24 + }
25 + return string(s)
26 +}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_darwin.go
-12
@@ -5,18 +5,6 @@ import (
5 "syscall"
6 )
7
8 -// cstr converts a nil-terminated C string into a Go string
9 -func cstr(ca []int8) string {
10 - s := make([]byte, 0, len(ca))
11 - for _, c := range ca {
12 - if c == 0x00 {
13 - break
14 - }
15 - s = append(s, byte(c))
16 - }
17 - return string(s)
18 -}
19 -
8 var re = regexp.MustCompile(`\\(.)`)
9
10 // unescape removes backslash-escaping. The escaped characters are not
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/mountinfo_freebsd.go new
+7
@@ -0,0 +1,7 @@
1 +package fstestutil
2 +
3 +import "errors"
4 +
5 +func getMountInfo(mnt string) (*MountInfo, error) {
6 + return nil, errors.New("FreeBSD has no useful mount information")
7 +}
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/record/record.go
+3 -4
@@ -20,8 +20,7 @@ func (w *Writes) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.W
20 n, err := w.buf.Write(req.Data)
21 resp.Size = n
22 if err != nil {
23 - // TODO hiding error
24 - return fuse.EIO
23 + return err
24 }
25 return nil
26 }
@@ -293,7 +292,7 @@ var _ = fs.NodeGetxattrer(&Getxattrs{})
292 func (r *Getxattrs) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
293 tmp := *req
294 r.rec.RecordRequest(&tmp)
296 - return fuse.ENODATA
295 + return fuse.ErrNoXattr
296 }
297
298 // RecordedGetxattr returns information about the Getxattr request.
@@ -318,7 +317,7 @@ var _ = fs.NodeListxattrer(&Listxattrs{})
317 func (r *Listxattrs) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
318 tmp := *req
319 r.rec.RecordRequest(&tmp)
321 - return fuse.ENODATA
320 + return fuse.ErrNoXattr
321 }
322
323 // RecordedListxattr returns information about the Listxattr request.
Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil/testfs.go
+19
@@ -5,6 +5,7 @@ import (
5
6 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
8 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
9 )
10
11 // SimpleFS is a trivial FS that just implements the Root method.
@@ -27,3 +28,21 @@ func (f File) Attr() fuse.Attr { return fuse.Attr{Mode: 0666} }
28 type Dir struct{}
29
30 func (f Dir) Attr() fuse.Attr { return fuse.Attr{Mode: os.ModeDir | 0777} }
31 +
32 +// ChildMap is a directory with child nodes looked up from a map.
33 +type ChildMap map[string]fs.Node
34 +
35 +var _ = fs.Node(ChildMap{})
36 +var _ = fs.NodeStringLookuper(ChildMap{})
37 +
38 +func (f ChildMap) Attr() fuse.Attr {
39 + return fuse.Attr{Mode: os.ModeDir | 0777}
40 +}
41 +
42 +func (f ChildMap) Lookup(ctx context.Context, name string) (fs.Node, error) {
43 + child, ok := f[name]
44 + if !ok {
45 + return nil, fuse.ENOENT
46 + }
47 + return child, nil
48 +}
Godeps/_workspace/src/bazil.org/fuse/fs/helpers_test.go new
+67
@@ -0,0 +1,67 @@
1 +package fs_test
2 +
3 +import (
4 + "errors"
5 + "flag"
6 + "os"
7 + "os/exec"
8 + "path/filepath"
9 + "testing"
10 +)
11 +
12 +var childHelpers = map[string]func(){}
13 +
14 +type childProcess struct {
15 + name string
16 + fn func()
17 +}
18 +
19 +var _ flag.Value = (*childProcess)(nil)
20 +
21 +func (c *childProcess) String() string {
22 + return c.name
23 +}
24 +
25 +func (c *childProcess) Set(s string) error {
26 + fn, ok := childHelpers[s]
27 + if !ok {
28 + return errors.New("helper not found")
29 + }
30 + c.name = s
31 + c.fn = fn
32 + return nil
33 +}
34 +
35 +var childMode childProcess
36 +
37 +func init() {
38 + flag.Var(&childMode, "fuse.internal.child", "internal use only")
39 +}
40 +
41 +// childCmd prepares a test function to be run in a subprocess, with
42 +// childMode set to true. Caller must still call Run or Start.
43 +//
44 +// Re-using the test executable as the subprocess is useful because
45 +// now test executables can e.g. be cross-compiled, transferred
46 +// between hosts, and run in settings where the whole Go development
47 +// environment is not installed.
48 +func childCmd(childName string) (*exec.Cmd, error) {
49 + // caller may set cwd, so we can't rely on relative paths
50 + executable, err := filepath.Abs(os.Args[0])
51 + if err != nil {
52 + return nil, err
53 + }
54 + cmd := exec.Command(executable, "-fuse.internal.child="+childName)
55 + cmd.Stdout = os.Stdout
56 + cmd.Stderr = os.Stderr
57 + return cmd, nil
58 +}
59 +
60 +func TestMain(m *testing.M) {
61 + flag.Parse()
62 + if childMode.fn != nil {
63 + childMode.fn()
64 + os.Exit(0)
65 + }
66 + os.Exit(m.Run())
67 +}
Godeps/_workspace/src/bazil.org/fuse/fs/serve.go
+30 -6
@@ -101,6 +101,15 @@ type NodeGetattrer interface {
101
102 type NodeSetattrer interface {
103 // Setattr sets the standard metadata for the receiver.
104 + //
105 + // Note, this is also used to communicate changes in the size of
106 + // the file. Not implementing Setattr causes writes to be unable
107 + // to grow the file (except with OpenDirectIO, which bypasses that
108 + // mechanism).
109 + //
110 + // req.Valid is a bitmask of what fields are actually being set.
111 + // For example, the method should not change the mode of the file
112 + // unless req.Valid.Mode() is true.
113 Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error
114 }
115
@@ -203,9 +212,7 @@ type NodeGetxattrer interface {
212 // Getxattr gets an extended attribute by the given name from the
213 // node.
214 //
206 - // If there is no xattr by that name, returns fuse.ENODATA. This
207 - // will be translated to the platform-specific correct error code
208 - // by the framework.
215 + // If there is no xattr by that name, returns fuse.ErrNoXattr.
216 Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error
217 }
218
@@ -223,9 +230,7 @@ type NodeSetxattrer interface {
230 type NodeRemovexattrer interface {
231 // Removexattr removes an extended attribute for the name.
232 //
226 - // If there is no xattr by that name, returns fuse.ENODATA. This
227 - // will be translated to the platform-specific correct error code
228 - // by the framework.
233 + // If there is no xattr by that name, returns fuse.ErrNoXattr.
234 Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error
235 }
236
@@ -279,10 +284,29 @@ type HandleReadDirAller interface {
284 }
285
286 type HandleReader interface {
287 + // Read requests to read data from the handle.
288 + //
289 + // There is a page cache in the kernel that normally submits only
290 + // page-aligned reads spanning one or more pages. However, you
291 + // should not rely on this. To see individual requests as
292 + // submitted by the file system clients, set OpenDirectIO.
293 + //
294 + // Note that reads beyond the size of the file as reported by Attr
295 + // are not even attempted (except in OpenDirectIO mode).
296 Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error
297 }
298
299 type HandleWriter interface {
300 + // Write requests to write data into the handle.
301 + //
302 + // There is a writeback page cache in the kernel that normally submits
303 + // only page-aligned writes spanning one or more pages. However,
304 + // you should not rely on this. To see individual requests as
305 + // submitted by the file system clients, set OpenDirectIO.
306 + //
307 + // Note that file size changes are communicated through Setattr.
308 + // Writes beyond the size of the file as reported by Attr are not
309 + // even attempted (except in OpenDirectIO mode).
310 Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error
311 }
312
Godeps/_workspace/src/bazil.org/fuse/fs/serve_test.go
+125 -126
@@ -3,14 +3,11 @@ package fs_test
3 import (
4 "bytes"
5 "errors"
6 - "flag"
6 "io"
7 "io/ioutil"
8 "log"
9 "os"
10 "os/exec"
12 - "path/filepath"
13 - "regexp"
11 "runtime"
12 "strings"
13 "syscall"
@@ -40,55 +37,6 @@ func init() {
37 fstestutil.DebugByDefault()
38 }
39
43 -var childMode bool
44 -
45 -func init() {
46 - flag.BoolVar(&childMode, "fuse.internal.childmode", false, "internal use only")
47 -}
48 -
49 -// childCmd prepares a test function to be run in a subprocess, with
50 -// childMode set to true. Caller must still call Run or Start.
51 -//
52 -// Re-using the test executable as the subprocess is useful because
53 -// now test executables can e.g. be cross-compiled, transferred
54 -// between hosts, and run in settings where the whole Go development
55 -// environment is not installed.
56 -func childCmd(testName string) (*exec.Cmd, error) {
57 - // caller may set cwd, so we can't rely on relative paths
58 - executable, err := filepath.Abs(os.Args[0])
59 - if err != nil {
60 - return nil, err
61 - }
62 - testName = regexp.QuoteMeta(testName)
63 - cmd := exec.Command(executable, "-test.run=^"+testName+"$", "-fuse.internal.childmode")
64 - cmd.Stdout = os.Stdout
65 - cmd.Stderr = os.Stderr
66 - return cmd, nil
67 -}
68 -
69 -// childMapFS is an FS with one fixed child named "child".
70 -type childMapFS map[string]fs.Node
71 -
72 -var _ = fs.FS(childMapFS{})
73 -var _ = fs.Node(childMapFS{})
74 -var _ = fs.NodeStringLookuper(childMapFS{})
75 -
76 -func (f childMapFS) Attr() fuse.Attr {
77 - return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0777}
78 -}
79 -
80 -func (f childMapFS) Root() (fs.Node, error) {
81 - return f, nil
82 -}
83 -
84 -func (f childMapFS) Lookup(ctx context.Context, name string) (fs.Node, error) {
85 - child, ok := f[name]
86 - if !ok {
87 - return nil, fuse.ENOENT
88 - }
89 - return child, nil
90 -}
91 -
40 // symlink can be embedded in a struct to make it look like a symlink.
41 type symlink struct {
42 target string
@@ -271,7 +219,7 @@ func testReadAll(t *testing.T, path string) {
219
220 func TestReadAll(t *testing.T) {
221 t.Parallel()
274 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": readAll{}})
222 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": readAll{}}})
223 if err != nil {
224 t.Fatal(err)
225 }
@@ -300,7 +248,7 @@ func (readWithHandleRead) Read(ctx context.Context, req *fuse.ReadRequest, resp
248
249 func TestReadAllWithHandleRead(t *testing.T) {
250 t.Parallel()
303 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": readWithHandleRead{}})
251 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": readWithHandleRead{}}})
252 if err != nil {
253 t.Fatal(err)
254 }
@@ -319,7 +267,7 @@ type release struct {
267 func TestRelease(t *testing.T) {
268 t.Parallel()
269 r := &release{}
322 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": r})
270 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": r}})
271 if err != nil {
272 t.Fatal(err)
273 }
@@ -346,7 +294,7 @@ type write struct {
294 func TestWrite(t *testing.T) {
295 t.Parallel()
296 w := &write{}
349 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
297 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
298 if err != nil {
299 t.Fatal(err)
300 }
@@ -393,7 +341,7 @@ type writeLarge struct {
341 func TestWriteLarge(t *testing.T) {
342 t.Parallel()
343 w := &write{}
396 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
344 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
345 if err != nil {
346 t.Fatal(err)
347 }
@@ -440,7 +388,7 @@ type writeTruncateFlush struct {
388 func TestWriteTruncateFlush(t *testing.T) {
389 t.Parallel()
390 w := &writeTruncateFlush{}
443 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
391 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
392 if err != nil {
393 t.Fatal(err)
394 }
@@ -838,7 +786,7 @@ func (dataHandleTest) Open(ctx context.Context, req *fuse.OpenRequest, resp *fus
786 func TestDataHandle(t *testing.T) {
787 t.Parallel()
788 f := &dataHandleTest{}
841 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
789 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
790 if err != nil {
791 t.Fatal(err)
792 }
@@ -883,7 +831,7 @@ func TestInterrupt(t *testing.T) {
831 t.Parallel()
832 f := &interrupt{}
833 f.hanging = make(chan struct{}, 1)
886 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
834 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
835 if err != nil {
836 t.Fatal(err)
837 }
@@ -947,7 +895,7 @@ type truncate struct {
895 func testTruncate(t *testing.T, toSize int64) {
896 t.Parallel()
897 f := &truncate{}
950 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
898 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
899 if err != nil {
900 t.Fatal(err)
901 }
@@ -988,7 +936,7 @@ type ftruncate struct {
936 func testFtruncate(t *testing.T, toSize int64) {
937 t.Parallel()
938 f := &ftruncate{}
991 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
939 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
940 if err != nil {
941 t.Fatal(err)
942 }
@@ -1038,7 +986,7 @@ type truncateWithOpen struct {
986 func TestTruncateWithOpen(t *testing.T) {
987 t.Parallel()
988 f := &truncateWithOpen{}
1041 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
989 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
990 if err != nil {
991 t.Fatal(err)
992 }
@@ -1165,7 +1113,7 @@ func (f *chmod) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fus
1113 func TestChmod(t *testing.T) {
1114 t.Parallel()
1115 f := &chmod{}
1168 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1116 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1117 if err != nil {
1118 t.Fatal(err)
1119 }
@@ -1199,7 +1147,7 @@ func (f *open) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR
1147 func TestOpen(t *testing.T) {
1148 t.Parallel()
1149 f := &open{}
1202 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1150 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1151 if err != nil {
1152 t.Fatal(err)
1153 }
@@ -1307,7 +1255,7 @@ func (f *getxattr) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp
1255 func TestGetxattr(t *testing.T) {
1256 t.Parallel()
1257 f := &getxattr{}
1310 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1258 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1259 if err != nil {
1260 t.Fatal(err)
1261 }
@@ -1343,7 +1291,7 @@ func (f *getxattrTooSmall) Getxattr(ctx context.Context, req *fuse.GetxattrReque
1291 func TestGetxattrTooSmall(t *testing.T) {
1292 t.Parallel()
1293 f := &getxattrTooSmall{}
1346 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1294 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1295 if err != nil {
1296 t.Fatal(err)
1297 }
@@ -1374,7 +1322,7 @@ func (f *getxattrSize) Getxattr(ctx context.Context, req *fuse.GetxattrRequest,
1322 func TestGetxattrSize(t *testing.T) {
1323 t.Parallel()
1324 f := &getxattrSize{}
1377 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1325 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1326 if err != nil {
1327 t.Fatal(err)
1328 }
@@ -1406,7 +1354,7 @@ func (f *listxattr) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, r
1354 func TestListxattr(t *testing.T) {
1355 t.Parallel()
1356 f := &listxattr{}
1409 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1357 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1358 if err != nil {
1359 t.Fatal(err)
1360 }
@@ -1445,7 +1393,7 @@ func (f *listxattrTooSmall) Listxattr(ctx context.Context, req *fuse.ListxattrRe
1393 func TestListxattrTooSmall(t *testing.T) {
1394 t.Parallel()
1395 f := &listxattrTooSmall{}
1448 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1396 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1397 if err != nil {
1398 t.Fatal(err)
1399 }
@@ -1476,7 +1424,7 @@ func (f *listxattrSize) Listxattr(ctx context.Context, req *fuse.ListxattrReques
1424 func TestListxattrSize(t *testing.T) {
1425 t.Parallel()
1426 f := &listxattrSize{}
1479 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1427 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1428 if err != nil {
1429 t.Fatal(err)
1430 }
@@ -1507,7 +1455,7 @@ func testSetxattr(t *testing.T, size int) {
1455
1456 t.Parallel()
1457 f := &setxattr{}
1510 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1458 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1459 if err != nil {
1460 t.Fatal(err)
1461 }
@@ -1560,7 +1508,7 @@ type removexattr struct {
1508 func TestRemovexattr(t *testing.T) {
1509 t.Parallel()
1510 f := &removexattr{}
1563 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": f})
1511 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": f}})
1512 if err != nil {
1513 t.Fatal(err)
1514 }
@@ -1681,74 +1629,78 @@ func (f *inMemoryFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *
1629 return nil
1630 }
1631
1684 -type mmap struct {
1685 - inMemoryFile
1686 - // We don't actually care about whether the fsync happened or not;
1687 - // this just lets us force the page cache to send the writes to
1688 - // FUSE, so we can reliably verify they came through.
1689 - record.Fsyncs
1632 +const mmapSize = 16 * 4096
1633 +
1634 +var mmapWrites = map[int]byte{
1635 + 10: 'a',
1636 + 4096: 'b',
1637 + 4097: 'c',
1638 + mmapSize - 4096: 'd',
1639 + mmapSize - 1: 'z',
1640 }
1641
1692 -func TestMmap(t *testing.T) {
1693 - const size = 16 * 4096
1694 - writes := map[int]byte{
1695 - 10: 'a',
1696 - 4096: 'b',
1697 - 4097: 'c',
1698 - size - 4096: 'd',
1699 - size - 1: 'z',
1642 +func helperMmap() {
1643 + f, err := os.Create("child")
1644 + if err != nil {
1645 + log.Fatalf("Create: %v", err)
1646 }
1647 + defer f.Close()
1648
1702 - // Run the mmap-using parts of the test in a subprocess, to avoid
1703 - // an intentional page fault hanging the whole process (because it
1704 - // would need to be served by the same process, and there might
1705 - // not be a thread free to do that). Merely bumping GOMAXPROCS is
1706 - // not enough to prevent the hangs reliably.
1707 - if childMode {
1708 - f, err := os.Create("child")
1709 - if err != nil {
1710 - t.Fatalf("Create: %v", err)
1711 - }
1712 - defer f.Close()
1649 + data, err := syscall.Mmap(int(f.Fd()), 0, mmapSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
1650 + if err != nil {
1651 + log.Fatalf("Mmap: %v", err)
1652 + }
1653
1714 - data, err := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
1715 - if err != nil {
1716 - t.Fatalf("Mmap: %v", err)
1717 - }
1654 + for i, b := range mmapWrites {
1655 + data[i] = b
1656 + }
1657
1719 - for i, b := range writes {
1720 - data[i] = b
1721 - }
1658 + if err := syscallx.Msync(data, syscall.MS_SYNC); err != nil {
1659 + log.Fatalf("Msync: %v", err)
1660 + }
1661
1723 - if err := syscallx.Msync(data, syscall.MS_SYNC); err != nil {
1724 - t.Fatalf("Msync: %v", err)
1725 - }
1662 + if err := syscall.Munmap(data); err != nil {
1663 + log.Fatalf("Munmap: %v", err)
1664 + }
1665
1727 - if err := syscall.Munmap(data); err != nil {
1728 - t.Fatalf("Munmap: %v", err)
1729 - }
1666 + if err := f.Sync(); err != nil {
1667 + log.Fatalf("Fsync = %v", err)
1668 + }
1669
1731 - if err := f.Sync(); err != nil {
1732 - t.Fatalf("Fsync = %v", err)
1733 - }
1670 + err = f.Close()
1671 + if err != nil {
1672 + log.Fatalf("Close: %v", err)
1673 + }
1674 +}
1675
1735 - err = f.Close()
1736 - if err != nil {
1737 - t.Fatalf("Close: %v", err)
1738 - }
1676 +func init() {
1677 + childHelpers["mmap"] = helperMmap
1678 +}
1679
1740 - return
1741 - }
1680 +type mmap struct {
1681 + inMemoryFile
1682 + // We don't actually care about whether the fsync happened or not;
1683 + // this just lets us force the page cache to send the writes to
1684 + // FUSE, so we can reliably verify they came through.
1685 + record.Fsyncs
1686 +}
1687 +
1688 +func TestMmap(t *testing.T) {
1689
1690 w := &mmap{}
1744 - w.data = make([]byte, size)
1745 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": w})
1691 + w.data = make([]byte, mmapSize)
1692 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
1693 if err != nil {
1694 t.Fatal(err)
1695 }
1696 defer mnt.Close()
1697
1751 - child, err := childCmd("TestMmap")
1698 + // Run the mmap-using parts of the test in a subprocess, to avoid
1699 + // an intentional page fault hanging the whole process (because it
1700 + // would need to be served by the same process, and there might
1701 + // not be a thread free to do that). Merely bumping GOMAXPROCS is
1702 + // not enough to prevent the hangs reliably.
1703 + child, err := childCmd("mmap")
1704 if err != nil {
1705 t.Fatal(err)
1706 }
@@ -1758,12 +1710,12 @@ func TestMmap(t *testing.T) {
1710 }
1711
1712 got := w.data
1761 - if g, e := len(got), size; g != e {
1713 + if g, e := len(got), mmapSize; g != e {
1714 t.Fatalf("bad write length: %d != %d", g, e)
1715 }
1716 for i, g := range got {
1717 // default '\x00' for writes[i] is good here
1766 - if e := writes[i]; g != e {
1718 + if e := mmapWrites[i]; g != e {
1719 t.Errorf("wrong byte at offset %d: %q != %q", i, g, e)
1720 }
1721 }
@@ -1790,7 +1742,7 @@ func (directRead) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.Re
1742
1743 func TestDirectRead(t *testing.T) {
1744 t.Parallel()
1793 - mnt, err := fstestutil.MountedT(t, childMapFS{"child": directRead{}})
1745 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": directRead{}}})
1746 if err != nil {
1747 t.Fatal(err)
1748 }
@@ -1798,3 +1750,50 @@ func TestDirectRead(t *testing.T) {
1750
1751 testReadAll(t, mnt.Dir+"/child")
1752 }
1753 +
1754 +// Test direct Write.
1755 +
1756 +type directWrite struct {
1757 + fstestutil.File
1758 + record.Writes
1759 +}
1760 +
1761 +// explicitly not defining Attr / Setattr and managing Size
1762 +
1763 +func (f *directWrite) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
1764 + // do not allow the kernel to use page cache
1765 + resp.Flags |= fuse.OpenDirectIO
1766 + return f, nil
1767 +}
1768 +
1769 +func TestDirectWrite(t *testing.T) {
1770 + t.Parallel()
1771 + w := &directWrite{}
1772 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.ChildMap{"child": w}})
1773 + if err != nil {
1774 + t.Fatal(err)
1775 + }
1776 + defer mnt.Close()
1777 +
1778 + f, err := os.OpenFile(mnt.Dir+"/child", os.O_RDWR, 0666)
1779 + if err != nil {
1780 + t.Fatalf("Create: %v", err)
1781 + }
1782 + defer f.Close()
1783 + n, err := f.Write([]byte(hi))
1784 + if err != nil {
1785 + t.Fatalf("Write: %v", err)
1786 + }
1787 + if n != len(hi) {
1788 + t.Fatalf("short write; n=%d; hi=%d", n, len(hi))
1789 + }
1790 +
1791 + err = f.Close()
1792 + if err != nil {
1793 + t.Fatalf("Close: %v", err)
1794 + }
1795 +
1796 + if got := string(w.RecordedWriteData()); got != hi {
1797 + t.Errorf("write = %q, want %q", got, hi)
1798 + }
1799 +}
Godeps/_workspace/src/bazil.org/fuse/fuse.go
+24 -37
@@ -47,11 +47,17 @@
47 //
48 // Op(ctx context.Context, req *OpRequest, resp *OpResponse) Error
49 //
50 -// where Op is the name of a FUSE operation. Op reads request parameters
51 -// from req and writes results to resp. An operation whose only result is
52 -// the error result omits the resp parameter. Multiple goroutines may call
53 -// service methods simultaneously; the methods being called are responsible
54 -// for appropriate synchronization.
50 +// where Op is the name of a FUSE operation. Op reads request
51 +// parameters from req and writes results to resp. An operation whose
52 +// only result is the error result omits the resp parameter.
53 +//
54 +// Multiple goroutines may call service methods simultaneously; the
55 +// methods being called are responsible for appropriate
56 +// synchronization.
57 +//
58 +// The operation must not hold on to the request or response,
59 +// including any []byte fields such as WriteRequest.Data or
60 +// SetxattrRequest.Xattr.
61 //
62 // Errors
63 //
@@ -79,9 +85,12 @@
85 //
86 // Authentication
87 //
82 -// All requests types embed a Header, meaning that the method can inspect
83 -// req.Pid, req.Uid, and req.Gid as necessary to implement permission checking.
84 -// Alternately, XXX.
88 +// All requests types embed a Header, meaning that the method can
89 +// inspect req.Pid, req.Uid, and req.Gid as necessary to implement
90 +// permission checking. The kernel FUSE layer normally prevents other
91 +// users from accessing the FUSE file system (to change this, see
92 +// AllowOther, AllowRoot), but does not enforce access modes (to
93 +// change this, see DefaultPermissions).
94 //
95 // Mount Options
96 //
@@ -90,8 +99,6 @@
99 //
100 package fuse
101
93 -// BUG(rsc): The mount code for FreeBSD has not been written yet.
94 -
102 import (
103 "bytes"
104 "encoding/json"
@@ -241,7 +248,6 @@ const (
248 // See also fs.Intr.
249 EINTR = Errno(syscall.EINTR)
250
244 - ENODATA = Errno(syscall.ENODATA)
251 ERANGE = Errno(syscall.ERANGE)
252 ENOTSUP = Errno(syscall.ENOTSUP)
253 EEXIST = Errno(syscall.EEXIST)
@@ -252,14 +258,13 @@ const (
258 const DefaultErrno = EIO
259
260 var errnoNames = map[Errno]string{
255 - ENOSYS: "ENOSYS",
256 - ESTALE: "ESTALE",
257 - ENOENT: "ENOENT",
258 - EIO: "EIO",
259 - EPERM: "EPERM",
260 - EINTR: "EINTR",
261 - ENODATA: "ENODATA",
262 - EEXIST: "EEXIST",
261 + ENOSYS: "ENOSYS",
262 + ESTALE: "ESTALE",
263 + ENOENT: "ENOENT",
264 + EIO: "EIO",
265 + EPERM: "EPERM",
266 + EINTR: "EINTR",
267 + EEXIST: "EEXIST",
268 }
269
270 // Errno implements Error and ErrorNumber using a syscall.Errno.
@@ -1115,9 +1120,6 @@ func (a *Attr) attr() (out attr) {
1120 out.Mode |= syscall.S_ISGID
1121 }
1122 out.Nlink = a.Nlink
1118 - if out.Nlink < 1 {
1119 - out.Nlink = 1
1120 - }
1123 out.Uid = a.Uid
1124 out.Gid = a.Gid
1125 out.Rdev = a.Rdev
@@ -1195,11 +1197,6 @@ func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
1197 }
1198 }
1199
1198 -func (r *GetxattrRequest) RespondError(err error) {
1199 - err = translateGetxattrError(err)
1200 - r.Header.RespondError(err)
1201 -}
1202 -
1200 // A GetxattrResponse is the response to a GetxattrRequest.
1201 type GetxattrResponse struct {
1202 Xattr []byte
@@ -1271,11 +1268,6 @@ func (r *RemovexattrRequest) Respond() {
1268 r.respond(out, unsafe.Sizeof(*out))
1269 }
1270
1274 -func (r *RemovexattrRequest) RespondError(err error) {
1275 - err = translateGetxattrError(err)
1276 - r.Header.RespondError(err)
1277 -}
1278 -
1271 // A SetxattrRequest asks to set an extended attribute associated with a file.
1272 type SetxattrRequest struct {
1273 Header `json:"-"`
@@ -1321,11 +1313,6 @@ func (r *SetxattrRequest) Respond() {
1313 r.respond(out, unsafe.Sizeof(*out))
1314 }
1315
1324 -func (r *SetxattrRequest) RespondError(err error) {
1325 - err = translateGetxattrError(err)
1326 - r.Header.RespondError(err)
1327 -}
1328 -
1316 // A LookupRequest asks to look up the given name in the directory named by r.Node.
1317 type LookupRequest struct {
1318 Header `json:"-"`
Godeps/_workspace/src/bazil.org/fuse/fuse_kernel_freebsd.go new
+60
@@ -0,0 +1,60 @@
1 +package fuse
2 +
3 +import "time"
4 +
5 +type attr struct {
6 + Ino uint64
7 + Size uint64
8 + Blocks uint64
9 + Atime uint64
10 + Mtime uint64
11 + Ctime uint64
12 + AtimeNsec uint32
13 + MtimeNsec uint32
14 + CtimeNsec uint32
15 + Mode uint32
16 + Nlink uint32
17 + Uid uint32
18 + Gid uint32
19 + Rdev uint32
20 +}
21 +
22 +func (a *attr) Crtime() time.Time {
23 + return time.Time{}
24 +}
25 +
26 +func (a *attr) SetCrtime(s uint64, ns uint32) {
27 + // ignored on freebsd
28 +}
29 +
30 +func (a *attr) SetFlags(f uint32) {
31 + // ignored on freebsd
32 +}
33 +
34 +type setattrIn struct {
35 + setattrInCommon
36 +}
37 +
38 +func (in *setattrIn) BkupTime() time.Time {
39 + return time.Time{}
40 +}
41 +
42 +func (in *setattrIn) Chgtime() time.Time {
43 + return time.Time{}
44 +}
45 +
46 +func (in *setattrIn) Flags() uint32 {
47 + return 0
48 +}
49 +
50 +func openFlags(flags uint32) OpenFlags {
51 + return OpenFlags(flags)
52 +}
53 +
54 +type getxattrIn struct {
55 + getxattrInCommon
56 +}
57 +
58 +type setxattrIn struct {
59 + setxattrInCommon
60 +}
Godeps/_workspace/src/bazil.org/fuse/mount_darwin.go
+2 -2
@@ -59,7 +59,7 @@ func callMount(dir string, conf *MountConfig, f *os.File, ready chan<- struct{},
59 if strings.Contains(k, ",") || strings.Contains(v, ",") {
60 // Silly limitation but the mount helper does not
61 // understand any escaping. See TestMountOptionCommaError.
62 - return fmt.Errorf("mount options cannot contain commas on OS X: %q=%q", k, v)
62 + return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v)
63 }
64 }
65 cmd := exec.Command(
@@ -89,7 +89,7 @@ func callMount(dir string, conf *MountConfig, f *os.File, ready chan<- struct{},
89 return err
90 }
91 go func() {
92 - err = cmd.Wait()
92 + err := cmd.Wait()
93 if err != nil {
94 if buf.Len() > 0 {
95 output := buf.Bytes()
Godeps/_workspace/src/bazil.org/fuse/mount_freebsd.go new
+41
@@ -0,0 +1,41 @@
1 +package fuse
2 +
3 +import (
4 + "fmt"
5 + "os"
6 + "os/exec"
7 + "strings"
8 +)
9 +
10 +func mount(dir string, conf *MountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
11 + for k, v := range conf.options {
12 + if strings.Contains(k, ",") || strings.Contains(v, ",") {
13 + // Silly limitation but the mount helper does not
14 + // understand any escaping. See TestMountOptionCommaError.
15 + return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
16 + }
17 + }
18 +
19 + f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
20 + if err != nil {
21 + *errp = err
22 + return nil, err
23 + }
24 +
25 + cmd := exec.Command(
26 + "/sbin/mount_fusefs",
27 + "--safe",
28 + "-o", conf.getOptions(),
29 + "3",
30 + dir,
31 + )
32 + cmd.ExtraFiles = []*os.File{f}
33 +
34 + out, err := cmd.CombinedOutput()
35 + if err != nil {
36 + return nil, fmt.Errorf("mount_fusefs: %q, %v", out, err)
37 + }
38 +
39 + close(ready)
40 + return f, nil
41 +}
Godeps/_workspace/src/bazil.org/fuse/options.go
+32
@@ -5,6 +5,10 @@ import (
5 "strings"
6 )
7
8 +func dummyOption(conf *MountConfig) error {
9 + return nil
10 +}
11 +
12 // MountConfig holds the configuration for a mount operation.
13 // Use it by passing MountOption values to Mount.
14 type MountConfig struct {
@@ -37,6 +41,8 @@ type MountOption func(*MountConfig) error
41
42 // FSName sets the file system name (also called source) that is
43 // visible in the list of mounted file systems.
44 +//
45 +// FreeBSD ignores this option.
46 func FSName(name string) MountOption {
47 return func(conf *MountConfig) error {
48 conf.options["fsname"] = name
@@ -49,6 +55,7 @@ func FSName(name string) MountOption {
55 // `fuse.foo`.
56 //
57 // OS X ignores this option.
58 +// FreeBSD ignores this option.
59 func Subtype(fstype string) MountOption {
60 return func(conf *MountConfig) error {
61 conf.options["subtype"] = fstype
@@ -89,6 +96,8 @@ func AllowOther() MountOption {
96 // AllowRoot allows other users to access the file system.
97 //
98 // Only one of AllowOther or AllowRoot can be used.
99 +//
100 +// FreeBSD ignores this option.
101 func AllowRoot() MountOption {
102 return func(conf *MountConfig) error {
103 if _, ok := conf.options["allow_other"]; ok {
@@ -98,3 +107,26 @@ func AllowRoot() MountOption {
107 return nil
108 }
109 }
110 +
111 +// DefaultPermissions makes the kernel enforce access control based on
112 +// the file mode (as in chmod).
113 +//
114 +// Without this option, the Node itself decides what is and is not
115 +// allowed. This is normally ok because FUSE file systems cannot be
116 +// accessed by other users without AllowOther/AllowRoot.
117 +//
118 +// FreeBSD ignores this option.
119 +func DefaultPermissions() MountOption {
120 + return func(conf *MountConfig) error {
121 + conf.options["default_permissions"] = ""
122 + return nil
123 + }
124 +}
125 +
126 +// ReadOnly makes the mount read-only.
127 +func ReadOnly() MountOption {
128 + return func(conf *MountConfig) error {
129 + conf.options["ro"] = ""
130 + return nil
131 + }
132 +}
Godeps/_workspace/src/bazil.org/fuse/options_darwin_test.go deleted
-27
@@ -1,27 +0,0 @@
1 -package fuse_test
2 -
3 -import (
4 - "testing"
5 -
6 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
7 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
8 -)
9 -
10 -func TestMountOptionCommaError(t *testing.T) {
11 - t.Parallel()
12 - // this test is not tied to FSName, but needs just some option
13 - // with string content
14 - var name = "FuseTest,Marker"
15 - mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
16 - fuse.FSName(name),
17 - )
18 - switch {
19 - case err == nil:
20 - mnt.Close()
21 - t.Fatal("expected an error about commas")
22 - case err.Error() == `mount options cannot contain commas on OS X: "fsname"="FuseTest,Marker"`:
23 - // all good
24 - default:
25 - t.Fatalf("expected an error about commas, got: %v", err)
26 - }
27 -}
Godeps/_workspace/src/bazil.org/fuse/options_freebsd.go new
+9
@@ -0,0 +1,9 @@
1 +package fuse
2 +
3 +func localVolume(conf *MountConfig) error {
4 + return nil
5 +}
6 +
7 +func volumeName(name string) MountOption {
8 + return dummyOption
9 +}
Godeps/_workspace/src/bazil.org/fuse/options_helper_test.go new
+6
@@ -0,0 +1,6 @@
1 +package fuse
2 +
3 +// for TestMountOptionCommaError
4 +func ForTestSetMountOption(conf *MountConfig, k, v string) {
5 + conf.options[k] = v
6 +}
Godeps/_workspace/src/bazil.org/fuse/options_linux.go
-4
@@ -1,9 +1,5 @@
1 package fuse
2
3 -func dummyOption(conf *MountConfig) error {
4 - return nil
5 -}
6 -
3 func localVolume(conf *MountConfig) error {
4 return nil
5 }
Godeps/_workspace/src/bazil.org/fuse/options_nocomma_test.go new
+34
@@ -0,0 +1,34 @@
1 +// This file contains tests for platforms that have no escape
2 +// mechanism for including commas in mount options.
3 +//
4 +// +build darwin
5 +
6 +package fuse_test
7 +
8 +import (
9 + "runtime"
10 + "testing"
11 +
12 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
13 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
14 +)
15 +
16 +func TestMountOptionCommaError(t *testing.T) {
17 + t.Parallel()
18 + // this test is not tied to any specific option, it just needs
19 + // some string content
20 + var evil = "FuseTest,Marker"
21 + mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
22 + func(conf *fuse.MountConfig) error {
23 + fuse.ForTestSetMountOption(conf, "fusetest", evil)
24 + return nil
25 + },
26 + )
27 + if err == nil {
28 + mnt.Close()
29 + t.Fatal("expected an error about commas")
30 + }
31 + if g, e := err.Error(), `mount options cannot contain commas on `+runtime.GOOS+`: "fusetest"="FuseTest,Marker"`; g != e {
32 + t.Fatalf("wrong error: %q != %q", g, e)
33 + }
34 +}
Godeps/_workspace/src/bazil.org/fuse/options_test.go
+85
@@ -1,11 +1,15 @@
1 package fuse_test
2
3 import (
4 + "os"
5 "runtime"
6 + "syscall"
7 "testing"
8
9 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
10 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
11 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
12 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
13 )
14
15 func init() {
@@ -13,6 +17,9 @@ func init() {
17 }
18
19 func TestMountOptionFSName(t *testing.T) {
20 + if runtime.GOOS == "freebsd" {
21 + t.Skip("FreeBSD does not support FSName")
22 + }
23 t.Parallel()
24 const name = "FuseTestMarker"
25 mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -33,6 +40,9 @@ func TestMountOptionFSName(t *testing.T) {
40 }
41
42 func testMountOptionFSNameEvil(t *testing.T, evil string) {
43 + if runtime.GOOS == "freebsd" {
44 + t.Skip("FreeBSD does not support FSName")
45 + }
46 t.Parallel()
47 var name = "FuseTest" + evil + "Marker"
48 mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -87,6 +97,9 @@ func TestMountOptionSubtype(t *testing.T) {
97 if runtime.GOOS == "darwin" {
98 t.Skip("OS X does not support Subtype")
99 }
100 + if runtime.GOOS == "freebsd" {
101 + t.Skip("FreeBSD does not support Subtype")
102 + }
103 t.Parallel()
104 const name = "FuseTestMarker"
105 mnt, err := fstestutil.MountedT(t, fstestutil.SimpleFS{fstestutil.Dir{}},
@@ -139,3 +152,75 @@ func TestMountOptionAllowRootThenAllowOther(t *testing.T) {
152 t.Fatalf("wrong error: %v != %v", g, e)
153 }
154 }
155 +
156 +type unwritableFile struct{}
157 +
158 +func (f unwritableFile) Attr() fuse.Attr { return fuse.Attr{Mode: 0000} }
159 +
160 +func TestMountOptionDefaultPermissions(t *testing.T) {
161 + if runtime.GOOS == "freebsd" {
162 + t.Skip("FreeBSD does not support DefaultPermissions")
163 + }
164 + t.Parallel()
165 + mnt, err := fstestutil.MountedT(t,
166 + fstestutil.SimpleFS{
167 + fstestutil.ChildMap{"child": unwritableFile{}},
168 + },
169 + fuse.DefaultPermissions(),
170 + )
171 +
172 + if err != nil {
173 + t.Fatal(err)
174 + }
175 + defer mnt.Close()
176 +
177 + // This will be prevented by kernel-level access checking when
178 + // DefaultPermissions is used.
179 + f, err := os.OpenFile(mnt.Dir+"/child", os.O_WRONLY, 0000)
180 + if err == nil {
181 + f.Close()
182 + t.Fatal("expected an error")
183 + }
184 + if !os.IsPermission(err) {
185 + t.Fatalf("expected a permission error, got %T: %v", err, err)
186 + }
187 +}
188 +
189 +type createrDir struct {
190 + fstestutil.Dir
191 +}
192 +
193 +var _ fs.NodeCreater = createrDir{}
194 +
195 +func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
196 + // pick a really distinct error, to identify it later
197 + return nil, nil, fuse.Errno(syscall.ENAMETOOLONG)
198 +}
199 +
200 +func TestMountOptionReadOnly(t *testing.T) {
201 + t.Parallel()
202 + mnt, err := fstestutil.MountedT(t,
203 + fstestutil.SimpleFS{createrDir{}},
204 + fuse.ReadOnly(),
205 + )
206 +
207 + if err != nil {
208 + t.Fatal(err)
209 + }
210 + defer mnt.Close()
211 +
212 + // This will be prevented by kernel-level access checking when
213 + // ReadOnly is used.
214 + f, err := os.Create(mnt.Dir + "/child")
215 + if err == nil {
216 + f.Close()
217 + t.Fatal("expected an error")
218 + }
219 + perr, ok := err.(*os.PathError)
220 + if !ok {
221 + t.Fatalf("expected PathError, got %T: %v", err, err)
222 + }
223 + if perr.Err != syscall.EROFS {
224 + t.Fatalf("expected EROFS, got %T: %v", err, err)
225 + }
226 +}
Godeps/_workspace/src/bazil.org/fuse/syscallx/syscallx_std.go
+6 -6
@@ -3,24 +3,24 @@
3 package syscallx
4
5 // This file just contains wrappers for platforms that already have
6 -// the right stuff in stdlib.
6 +// the right stuff in golang.org/x/sys/unix.
7
8 import (
9 - "syscall"
9 + "golang.org/x/sys/unix"
10 )
11
12 func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
13 - return syscall.Getxattr(path, attr, dest)
13 + return unix.Getxattr(path, attr, dest)
14 }
15
16 func Listxattr(path string, dest []byte) (sz int, err error) {
17 - return syscall.Listxattr(path, dest)
17 + return unix.Listxattr(path, dest)
18 }
19
20 func Setxattr(path string, attr string, data []byte, flags int) (err error) {
21 - return syscall.Setxattr(path, attr, data, flags)
21 + return unix.Setxattr(path, attr, data, flags)
22 }
23
24 func Removexattr(path string, attr string) (err error) {
25 - return syscall.Removexattr(path, attr)
25 + return unix.Removexattr(path, attr)
26 }
Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
+12 -39
@@ -82,7 +82,7 @@ func init() {
82
83 // Use the Airbrake hook to report errors that have Error severity or above to
84 // an exception tracker. You can create custom hooks, see the Hooks section.
85 - log.AddHook(&logrus_airbrake.AirbrakeHook{})
85 + log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
86
87 // Output to stderr instead of stdout, could also be a file.
88 log.SetOutput(os.Stderr)
@@ -164,43 +164,8 @@ You can add hooks for logging levels. For example to send errors to an exception
164 tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
165 multiple places simultaneously, e.g. syslog.
166
167 -```go
168 -// Not the real implementation of the Airbrake hook. Just a simple sample.
169 -import (
170 - log "github.com/Sirupsen/logrus"
171 -)
172 -
173 -func init() {
174 - log.AddHook(new(AirbrakeHook))
175 -}
176 -
177 -type AirbrakeHook struct{}
178 -
179 -// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
180 -// the fields for the entry. See the Fields section of the README.
181 -func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
182 - err := airbrake.Notify(entry.Data["error"].(error))
183 - if err != nil {
184 - log.WithFields(log.Fields{
185 - "source": "airbrake",
186 - "endpoint": airbrake.Endpoint,
187 - }).Info("Failed to send error to Airbrake")
188 - }
189 -
190 - return nil
191 -}
192 -
193 -// `Levels()` returns a slice of `Levels` the hook is fired for.
194 -func (hook *AirbrakeHook) Levels() []log.Level {
195 - return []log.Level{
196 - log.ErrorLevel,
197 - log.FatalLevel,
198 - log.PanicLevel,
199 - }
200 -}
201 -```
202 -
203 -Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
167 +Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
168 +`init`:
169
170 ```go
171 import (
@@ -211,7 +176,7 @@ import (
176 )
177
178 func init() {
214 - log.AddHook(new(logrus_airbrake.AirbrakeHook))
179 + log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
180
181 hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
182 if err != nil {
@@ -233,6 +198,9 @@ func init() {
198 Send errors to remote syslog server.
199 Uses standard library `log/syslog` behind the scenes.
200
201 +* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go)
202 + Send errors to the Bugsnag exception tracking service.
203 +
204 * [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
205 Send errors to a channel in hipchat.
206
@@ -321,6 +289,11 @@ The built-in logging formatters are:
289 field to `true`. To force no colored output even if there is a TTY set the
290 `DisableColors` field to `true`
291 * `logrus.JSONFormatter`. Logs fields as JSON.
292 +* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net).
293 +
294 + ```go
295 + logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"})
296 + ```
297
298 Third party logging formatters:
299
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
+10
@@ -9,6 +9,7 @@ var log = logrus.New()
9 func init() {
10 log.Formatter = new(logrus.JSONFormatter)
11 log.Formatter = new(logrus.TextFormatter) // default
12 + log.Level = logrus.DebugLevel
13 }
14
15 func main() {
@@ -23,6 +24,11 @@ func main() {
24 }
25 }()
26
27 + log.WithFields(logrus.Fields{
28 + "animal": "walrus",
29 + "number": 8,
30 + }).Debug("Started observing beach")
31 +
32 log.WithFields(logrus.Fields{
33 "animal": "walrus",
34 "size": 10,
@@ -33,6 +39,10 @@ func main() {
39 "number": 122,
40 }).Warn("The group's number increased tremendously!")
41
42 + log.WithFields(logrus.Fields{
43 + "temperature": -4,
44 + }).Debug("Temperature changes")
45 +
46 log.WithFields(logrus.Fields{
47 "animal": "orca",
48 "size": 9009,
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
+3 -7
@@ -3,21 +3,17 @@ package main
3 import (
4 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake"
6 - "github.com/tobi/airbrake-go"
6 )
7
8 var log = logrus.New()
9
10 func init() {
12 - log.Formatter = new(logrus.TextFormatter) // default
13 - log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
11 + log.Formatter = // default
12 + new(logrus.TextFormatter)
13 + log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development"))
14 }
15
16 func main() {
17 - airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
18 - airbrake.ApiKey = "whatever"
19 - airbrake.Environment = "production"
20 -
17 log.WithFields(logrus.Fields{
18 "animal": "walrus",
19 "size": 10,
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go new
+48
@@ -0,0 +1,48 @@
1 +package logstash
2 +
3 +import (
4 + "encoding/json"
5 + "fmt"
6 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
7 + "time"
8 +)
9 +
10 +// Formatter generates json in logstash format.
11 +// Logstash site: http://logstash.net/
12 +type LogstashFormatter struct {
13 + Type string // if not empty use for logstash type field.
14 +}
15 +
16 +func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
17 + entry.Data["@version"] = 1
18 + entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)
19 +
20 + // set message field
21 + v, ok := entry.Data["message"]
22 + if ok {
23 + entry.Data["fields.message"] = v
24 + }
25 + entry.Data["message"] = entry.Message
26 +
27 + // set level field
28 + v, ok = entry.Data["level"]
29 + if ok {
30 + entry.Data["fields.level"] = v
31 + }
32 + entry.Data["level"] = entry.Level.String()
33 +
34 + // set type field
35 + if f.Type != "" {
36 + v, ok = entry.Data["type"]
37 + if ok {
38 + entry.Data["fields.type"] = v
39 + }
40 + entry.Data["type"] = f.Type
41 + }
42 +
43 + serialized, err := json.Marshal(entry.Data)
44 + if err != nil {
45 + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
46 + }
47 + return append(serialized, '\n'), nil
48 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go new
+52
@@ -0,0 +1,52 @@
1 +package logstash
2 +
3 +import (
4 + "bytes"
5 + "encoding/json"
6 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
7 + "github.com/stretchr/testify/assert"
8 + "testing"
9 +)
10 +
11 +func TestLogstashFormatter(t *testing.T) {
12 + assert := assert.New(t)
13 +
14 + lf := LogstashFormatter{Type: "abc"}
15 +
16 + fields := logrus.Fields{
17 + "message": "def",
18 + "level": "ijk",
19 + "type": "lmn",
20 + "one": 1,
21 + "pi": 3.14,
22 + "bool": true,
23 + }
24 +
25 + entry := logrus.WithFields(fields)
26 + entry.Message = "msg"
27 + entry.Level = logrus.InfoLevel
28 +
29 + b, _ := lf.Format(entry)
30 +
31 + var data map[string]interface{}
32 + dec := json.NewDecoder(bytes.NewReader(b))
33 + dec.UseNumber()
34 + dec.Decode(&data)
35 +
36 + // base fields
37 + assert.Equal(json.Number("1"), data["@version"])
38 + assert.NotEmpty(data["@timestamp"])
39 + assert.Equal("abc", data["type"])
40 + assert.Equal("msg", data["message"])
41 + assert.Equal("info", data["level"])
42 +
43 + // substituted fields
44 + assert.Equal("def", data["fields.message"])
45 + assert.Equal("ijk", data["fields.level"])
46 + assert.Equal("lmn", data["fields.type"])
47 +
48 + // formats
49 + assert.Equal(json.Number("1"), data["one"])
50 + assert.Equal(json.Number("3.14"), data["pi"])
51 + assert.Equal(true, data["bool"])
52 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
+30 -30
@@ -1,51 +1,51 @@
1 -package logrus_airbrake
1 +package airbrake
2
3 import (
4 + "errors"
5 + "fmt"
6 +
7 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8 "github.com/tobi/airbrake-go"
9 )
10
11 // AirbrakeHook to send exceptions to an exception-tracking service compatible
9 -// with the Airbrake API. You must set:
10 -// * airbrake.Endpoint
11 -// * airbrake.ApiKey
12 -// * airbrake.Environment (only sends exceptions when set to "production")
13 -//
14 -// Before using this hook, to send an error. Entries that trigger an Error,
15 -// Fatal or Panic should now include an "error" field to send to Airbrake.
16 -type AirbrakeHook struct{}
17 -
18 -func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
19 - if entry.Data["error"] == nil {
20 - entry.Logger.WithFields(logrus.Fields{
21 - "source": "airbrake",
22 - "endpoint": airbrake.Endpoint,
23 - }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
24 - return nil
12 +// with the Airbrake API.
13 +type airbrakeHook struct {
14 + APIKey string
15 + Endpoint string
16 + Environment string
17 +}
18 +
19 +func NewHook(endpoint, apiKey, env string) *airbrakeHook {
20 + return &airbrakeHook{
21 + APIKey: apiKey,
22 + Endpoint: endpoint,
23 + Environment: env,
24 }
25 +}
26 +
27 +func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
28 + airbrake.ApiKey = hook.APIKey
29 + airbrake.Endpoint = hook.Endpoint
30 + airbrake.Environment = hook.Environment
31
32 + var notifyErr error
33 err, ok := entry.Data["error"].(error)
28 - if !ok {
29 - entry.Logger.WithFields(logrus.Fields{
30 - "source": "airbrake",
31 - "endpoint": airbrake.Endpoint,
32 - }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
33 - return nil
34 + if ok {
35 + notifyErr = err
36 + } else {
37 + notifyErr = errors.New(entry.Message)
38 }
39
36 - airErr := airbrake.Notify(err)
40 + airErr := airbrake.Notify(notifyErr)
41 if airErr != nil {
38 - entry.Logger.WithFields(logrus.Fields{
39 - "source": "airbrake",
40 - "endpoint": airbrake.Endpoint,
41 - "error": airErr,
42 - }).Warn("Failed to send error to Airbrake")
42 + return fmt.Errorf("Failed to send error to Airbrake: %s", airErr)
43 }
44
45 return nil
46 }
47
48 -func (hook *AirbrakeHook) Levels() []logrus.Level {
48 +func (hook *airbrakeHook) Levels() []logrus.Level {
49 return []logrus.Level{
50 logrus.ErrorLevel,
51 logrus.FatalLevel,
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake_test.go new
+133
@@ -0,0 +1,133 @@
1 +package airbrake
2 +
3 +import (
4 + "encoding/xml"
5 + "net/http"
6 + "net/http/httptest"
7 + "testing"
8 + "time"
9 +
10 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
11 +)
12 +
13 +type notice struct {
14 + Error NoticeError `xml:"error"`
15 +}
16 +type NoticeError struct {
17 + Class string `xml:"class"`
18 + Message string `xml:"message"`
19 +}
20 +
21 +type customErr struct {
22 + msg string
23 +}
24 +
25 +func (e *customErr) Error() string {
26 + return e.msg
27 +}
28 +
29 +const (
30 + testAPIKey = "abcxyz"
31 + testEnv = "development"
32 + expectedClass = "*airbrake.customErr"
33 + expectedMsg = "foo"
34 + unintendedMsg = "Airbrake will not see this string"
35 +)
36 +
37 +var (
38 + noticeError = make(chan NoticeError, 1)
39 +)
40 +
41 +// TestLogEntryMessageReceived checks if invoking Logrus' log.Error
42 +// method causes an XML payload containing the log entry message is received
43 +// by a HTTP server emulating an Airbrake-compatible endpoint.
44 +func TestLogEntryMessageReceived(t *testing.T) {
45 + log := logrus.New()
46 + ts := startAirbrakeServer(t)
47 + defer ts.Close()
48 +
49 + hook := NewHook(ts.URL, testAPIKey, "production")
50 + log.Hooks.Add(hook)
51 +
52 + log.Error(expectedMsg)
53 +
54 + select {
55 + case received := <-noticeError:
56 + if received.Message != expectedMsg {
57 + t.Errorf("Unexpected message received: %s", received.Message)
58 + }
59 + case <-time.After(time.Second):
60 + t.Error("Timed out; no notice received by Airbrake API")
61 + }
62 +}
63 +
64 +// TestLogEntryMessageReceived confirms that, when passing an error type using
65 +// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the
66 +// error message returned by the Error() method on the error interface
67 +// rather than the logrus.Entry.Message string.
68 +func TestLogEntryWithErrorReceived(t *testing.T) {
69 + log := logrus.New()
70 + ts := startAirbrakeServer(t)
71 + defer ts.Close()
72 +
73 + hook := NewHook(ts.URL, testAPIKey, "production")
74 + log.Hooks.Add(hook)
75 +
76 + log.WithFields(logrus.Fields{
77 + "error": &customErr{expectedMsg},
78 + }).Error(unintendedMsg)
79 +
80 + select {
81 + case received := <-noticeError:
82 + if received.Message != expectedMsg {
83 + t.Errorf("Unexpected message received: %s", received.Message)
84 + }
85 + if received.Class != expectedClass {
86 + t.Errorf("Unexpected error class: %s", received.Class)
87 + }
88 + case <-time.After(time.Second):
89 + t.Error("Timed out; no notice received by Airbrake API")
90 + }
91 +}
92 +
93 +// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a
94 +// non-error type using logrus.Fields, a HTTP server emulating an Airbrake
95 +// endpoint receives the logrus.Entry.Message string.
96 +//
97 +// Only error types are supported when setting the 'error' field using
98 +// logrus.WithFields().
99 +func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) {
100 + log := logrus.New()
101 + ts := startAirbrakeServer(t)
102 + defer ts.Close()
103 +
104 + hook := NewHook(ts.URL, testAPIKey, "production")
105 + log.Hooks.Add(hook)
106 +
107 + log.WithFields(logrus.Fields{
108 + "error": expectedMsg,
109 + }).Error(unintendedMsg)
110 +
111 + select {
112 + case received := <-noticeError:
113 + if received.Message != unintendedMsg {
114 + t.Errorf("Unexpected message received: %s", received.Message)
115 + }
116 + case <-time.After(time.Second):
117 + t.Error("Timed out; no notice received by Airbrake API")
118 + }
119 +}
120 +
121 +func startAirbrakeServer(t *testing.T) *httptest.Server {
122 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
123 + var notice notice
124 + if err := xml.NewDecoder(r.Body).Decode(&notice); err != nil {
125 + t.Error(err)
126 + }
127 + r.Body.Close()
128 +
129 + noticeError <- notice.Error
130 + }))
131 +
132 + return ts
133 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go new
+68
@@ -0,0 +1,68 @@
1 +package logrus_bugsnag
2 +
3 +import (
4 + "errors"
5 +
6 + "github.com/bugsnag/bugsnag-go"
7 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8 +)
9 +
10 +type bugsnagHook struct{}
11 +
12 +// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
13 +// bugsnag.Configure. Bugsnag must be configured before the hook.
14 +var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
15 +
16 +// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
17 +// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
18 +// failed.
19 +type ErrBugsnagSendFailed struct {
20 + err error
21 +}
22 +
23 +func (e ErrBugsnagSendFailed) Error() string {
24 + return "failed to send error to Bugsnag: " + e.err.Error()
25 +}
26 +
27 +// NewBugsnagHook initializes a logrus hook which sends exceptions to an
28 +// exception-tracking service compatible with the Bugsnag API. Before using
29 +// this hook, you must call bugsnag.Configure(). The returned object should be
30 +// registered with a log via `AddHook()`
31 +//
32 +// Entries that trigger an Error, Fatal or Panic should now include an "error"
33 +// field to send to Bugsnag.
34 +func NewBugsnagHook() (*bugsnagHook, error) {
35 + if bugsnag.Config.APIKey == "" {
36 + return nil, ErrBugsnagUnconfigured
37 + }
38 + return &bugsnagHook{}, nil
39 +}
40 +
41 +// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
42 +// "error" field (or the Message if the error isn't present) and sends it off.
43 +func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
44 + var notifyErr error
45 + err, ok := entry.Data["error"].(error)
46 + if ok {
47 + notifyErr = err
48 + } else {
49 + notifyErr = errors.New(entry.Message)
50 + }
51 +
52 + bugsnagErr := bugsnag.Notify(notifyErr)
53 + if bugsnagErr != nil {
54 + return ErrBugsnagSendFailed{bugsnagErr}
55 + }
56 +
57 + return nil
58 +}
59 +
60 +// Levels enumerates the log levels on which the error should be forwarded to
61 +// bugsnag: everything at or above the "Error" level.
62 +func (hook *bugsnagHook) Levels() []logrus.Level {
63 + return []logrus.Level{
64 + logrus.ErrorLevel,
65 + logrus.FatalLevel,
66 + logrus.PanicLevel,
67 + }
68 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go new
+64
@@ -0,0 +1,64 @@
1 +package logrus_bugsnag
2 +
3 +import (
4 + "encoding/json"
5 + "errors"
6 + "io/ioutil"
7 + "net/http"
8 + "net/http/httptest"
9 + "testing"
10 + "time"
11 +
12 + "github.com/bugsnag/bugsnag-go"
13 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
14 +)
15 +
16 +type notice struct {
17 + Events []struct {
18 + Exceptions []struct {
19 + Message string `json:"message"`
20 + } `json:"exceptions"`
21 + } `json:"events"`
22 +}
23 +
24 +func TestNoticeReceived(t *testing.T) {
25 + msg := make(chan string, 1)
26 + expectedMsg := "foo"
27 +
28 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
29 + var notice notice
30 + data, _ := ioutil.ReadAll(r.Body)
31 + if err := json.Unmarshal(data, &notice); err != nil {
32 + t.Error(err)
33 + }
34 + _ = r.Body.Close()
35 +
36 + msg <- notice.Events[0].Exceptions[0].Message
37 + }))
38 + defer ts.Close()
39 +
40 + hook := &bugsnagHook{}
41 +
42 + bugsnag.Configure(bugsnag.Configuration{
43 + Endpoint: ts.URL,
44 + ReleaseStage: "production",
45 + APIKey: "12345678901234567890123456789012",
46 + Synchronous: true,
47 + })
48 +
49 + log := logrus.New()
50 + log.Hooks.Add(hook)
51 +
52 + log.WithFields(logrus.Fields{
53 + "error": errors.New(expectedMsg),
54 + }).Error("Bugsnag will not see this string")
55 +
56 + select {
57 + case received := <-msg:
58 + if received != expectedMsg {
59 + t.Errorf("Unexpected message received: %s", received)
60 + }
61 + case <-time.After(time.Second):
62 + t.Error("Timed out; no notice received by Bugsnag API")
63 + }
64 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md
+1 -1
@@ -57,5 +57,5 @@ with a call to `NewSentryHook`. This can be changed by assigning a value to the
57
58 ```go
59 hook, _ := logrus_sentry.NewSentryHook(...)
60 -hook.Timeout = 20*time.Seconds
60 +hook.Timeout = 20*time.Second
61 ```
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
+8 -1
@@ -11,7 +11,14 @@ type JSONFormatter struct{}
11 func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
12 data := make(Fields, len(entry.Data)+3)
13 for k, v := range entry.Data {
14 - data[k] = v
14 + switch v := v.(type) {
15 + case error:
16 + // Otherwise errors are ignored by `encoding/json`
17 + // https://github.com/Sirupsen/logrus/issues/137
18 + data[k] = v.Error()
19 + default:
20 + data[k] = v
21 + }
22 }
23 prefixFieldClashes(data)
24 data["time"] = entry.Time.Format(time.RFC3339)
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter_test.go new
+120
@@ -0,0 +1,120 @@
1 +package logrus
2 +
3 +import (
4 + "encoding/json"
5 + "errors"
6 +
7 + "testing"
8 +)
9 +
10 +func TestErrorNotLost(t *testing.T) {
11 + formatter := &JSONFormatter{}
12 +
13 + b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
14 + if err != nil {
15 + t.Fatal("Unable to format entry: ", err)
16 + }
17 +
18 + entry := make(map[string]interface{})
19 + err = json.Unmarshal(b, &entry)
20 + if err != nil {
21 + t.Fatal("Unable to unmarshal formatted entry: ", err)
22 + }
23 +
24 + if entry["error"] != "wild walrus" {
25 + t.Fatal("Error field not set")
26 + }
27 +}
28 +
29 +func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
30 + formatter := &JSONFormatter{}
31 +
32 + b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
33 + if err != nil {
34 + t.Fatal("Unable to format entry: ", err)
35 + }
36 +
37 + entry := make(map[string]interface{})
38 + err = json.Unmarshal(b, &entry)
39 + if err != nil {
40 + t.Fatal("Unable to unmarshal formatted entry: ", err)
41 + }
42 +
43 + if entry["omg"] != "wild walrus" {
44 + t.Fatal("Error field not set")
45 + }
46 +}
47 +
48 +func TestFieldClashWithTime(t *testing.T) {
49 + formatter := &JSONFormatter{}
50 +
51 + b, err := formatter.Format(WithField("time", "right now!"))
52 + if err != nil {
53 + t.Fatal("Unable to format entry: ", err)
54 + }
55 +
56 + entry := make(map[string]interface{})
57 + err = json.Unmarshal(b, &entry)
58 + if err != nil {
59 + t.Fatal("Unable to unmarshal formatted entry: ", err)
60 + }
61 +
62 + if entry["fields.time"] != "right now!" {
63 + t.Fatal("fields.time not set to original time field")
64 + }
65 +
66 + if entry["time"] != "0001-01-01T00:00:00Z" {
67 + t.Fatal("time field not set to current time, was: ", entry["time"])
68 + }
69 +}
70 +
71 +func TestFieldClashWithMsg(t *testing.T) {
72 + formatter := &JSONFormatter{}
73 +
74 + b, err := formatter.Format(WithField("msg", "something"))
75 + if err != nil {
76 + t.Fatal("Unable to format entry: ", err)
77 + }
78 +
79 + entry := make(map[string]interface{})
80 + err = json.Unmarshal(b, &entry)
81 + if err != nil {
82 + t.Fatal("Unable to unmarshal formatted entry: ", err)
83 + }
84 +
85 + if entry["fields.msg"] != "something" {
86 + t.Fatal("fields.msg not set to original msg field")
87 + }
88 +}
89 +
90 +func TestFieldClashWithLevel(t *testing.T) {
91 + formatter := &JSONFormatter{}
92 +
93 + b, err := formatter.Format(WithField("level", "something"))
94 + if err != nil {
95 + t.Fatal("Unable to format entry: ", err)
96 + }
97 +
98 + entry := make(map[string]interface{})
99 + err = json.Unmarshal(b, &entry)
100 + if err != nil {
101 + t.Fatal("Unable to unmarshal formatted entry: ", err)
102 + }
103 +
104 + if entry["fields.level"] != "something" {
105 + t.Fatal("fields.level not set to original level field")
106 + }
107 +}
108 +
109 +func TestJSONEntryEndsWithNewline(t *testing.T) {
110 + formatter := &JSONFormatter{}
111 +
112 + b, err := formatter.Format(WithField("level", "something"))
113 + if err != nil {
114 + t.Fatal("Unable to format entry: ", err)
115 + }
116 +
117 + if b[len(b)-1] != '\n' {
118 + t.Fatal("Expected JSON log entry to end with a newline")
119 + }
120 +}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
+63 -21
@@ -65,11 +65,15 @@ func (logger *Logger) WithFields(fields Fields) *Entry {
65 }
66
67 func (logger *Logger) Debugf(format string, args ...interface{}) {
68 - NewEntry(logger).Debugf(format, args...)
68 + if logger.Level >= DebugLevel {
69 + NewEntry(logger).Debugf(format, args...)
70 + }
71 }
72
73 func (logger *Logger) Infof(format string, args ...interface{}) {
72 - NewEntry(logger).Infof(format, args...)
74 + if logger.Level >= InfoLevel {
75 + NewEntry(logger).Infof(format, args...)
76 + }
77 }
78
79 func (logger *Logger) Printf(format string, args ...interface{}) {
@@ -77,31 +81,45 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
81 }
82
83 func (logger *Logger) Warnf(format string, args ...interface{}) {
80 - NewEntry(logger).Warnf(format, args...)
84 + if logger.Level >= WarnLevel {
85 + NewEntry(logger).Warnf(format, args...)
86 + }
87 }
88
89 func (logger *Logger) Warningf(format string, args ...interface{}) {
84 - NewEntry(logger).Warnf(format, args...)
90 + if logger.Level >= WarnLevel {
91 + NewEntry(logger).Warnf(format, args...)
92 + }
93 }
94
95 func (logger *Logger) Errorf(format string, args ...interface{}) {
88 - NewEntry(logger).Errorf(format, args...)
96 + if logger.Level >= ErrorLevel {
97 + NewEntry(logger).Errorf(format, args...)
98 + }
99 }
100
101 func (logger *Logger) Fatalf(format string, args ...interface{}) {
92 - NewEntry(logger).Fatalf(format, args...)
102 + if logger.Level >= FatalLevel {
103 + NewEntry(logger).Fatalf(format, args...)
104 + }
105 }
106
107 func (logger *Logger) Panicf(format string, args ...interface{}) {
96 - NewEntry(logger).Panicf(format, args...)
108 + if logger.Level >= PanicLevel {
109 + NewEntry(logger).Panicf(format, args...)
110 + }
111 }
112
113 func (logger *Logger) Debug(args ...interface{}) {
100 - NewEntry(logger).Debug(args...)
114 + if logger.Level >= DebugLevel {
115 + NewEntry(logger).Debug(args...)
116 + }
117 }
118
119 func (logger *Logger) Info(args ...interface{}) {
104 - NewEntry(logger).Info(args...)
120 + if logger.Level >= InfoLevel {
121 + NewEntry(logger).Info(args...)
122 + }
123 }
124
125 func (logger *Logger) Print(args ...interface{}) {
@@ -109,31 +127,45 @@ func (logger *Logger) Print(args ...interface{}) {
127 }
128
129 func (logger *Logger) Warn(args ...interface{}) {
112 - NewEntry(logger).Warn(args...)
130 + if logger.Level >= WarnLevel {
131 + NewEntry(logger).Warn(args...)
132 + }
133 }
134
135 func (logger *Logger) Warning(args ...interface{}) {
116 - NewEntry(logger).Warn(args...)
136 + if logger.Level >= WarnLevel {
137 + NewEntry(logger).Warn(args...)
138 + }
139 }
140
141 func (logger *Logger) Error(args ...interface{}) {
120 - NewEntry(logger).Error(args...)
142 + if logger.Level >= ErrorLevel {
143 + NewEntry(logger).Error(args...)
144 + }
145 }
146
147 func (logger *Logger) Fatal(args ...interface{}) {
124 - NewEntry(logger).Fatal(args...)
148 + if logger.Level >= FatalLevel {
149 + NewEntry(logger).Fatal(args...)
150 + }
151 }
152
153 func (logger *Logger) Panic(args ...interface{}) {
128 - NewEntry(logger).Panic(args...)
154 + if logger.Level >= PanicLevel {
155 + NewEntry(logger).Panic(args...)
156 + }
157 }
158
159 func (logger *Logger) Debugln(args ...interface{}) {
132 - NewEntry(logger).Debugln(args...)
160 + if logger.Level >= DebugLevel {
161 + NewEntry(logger).Debugln(args...)
162 + }
163 }
164
165 func (logger *Logger) Infoln(args ...interface{}) {
136 - NewEntry(logger).Infoln(args...)
166 + if logger.Level >= InfoLevel {
167 + NewEntry(logger).Infoln(args...)
168 + }
169 }
170
171 func (logger *Logger) Println(args ...interface{}) {
@@ -141,21 +173,31 @@ func (logger *Logger) Println(args ...interface{}) {
173 }
174
175 func (logger *Logger) Warnln(args ...interface{}) {
144 - NewEntry(logger).Warnln(args...)
176 + if logger.Level >= WarnLevel {
177 + NewEntry(logger).Warnln(args...)
178 + }
179 }
180
181 func (logger *Logger) Warningln(args ...interface{}) {
148 - NewEntry(logger).Warnln(args...)
182 + if logger.Level >= WarnLevel {
183 + NewEntry(logger).Warnln(args...)
184 + }
185 }
186
187 func (logger *Logger) Errorln(args ...interface{}) {
152 - NewEntry(logger).Errorln(args...)
188 + if logger.Level >= ErrorLevel {
189 + NewEntry(logger).Errorln(args...)
190 + }
191 }
192
193 func (logger *Logger) Fatalln(args ...interface{}) {
156 - NewEntry(logger).Fatalln(args...)
194 + if logger.Level >= FatalLevel {
195 + NewEntry(logger).Fatalln(args...)
196 + }
197 }
198
199 func (logger *Logger) Panicln(args ...interface{}) {
160 - NewEntry(logger).Panicln(args...)
200 + if logger.Level >= PanicLevel {
201 + NewEntry(logger).Panicln(args...)
202 + }
203 }
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go
-1
@@ -1,4 +1,3 @@
1 -
1 package logrus
2
3 import "syscall"
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
+13 -5
@@ -3,7 +3,6 @@ package logrus
3 import (
4 "bytes"
5 "fmt"
6 - "regexp"
6 "sort"
7 "strings"
8 "time"
@@ -15,12 +14,12 @@ const (
14 green = 32
15 yellow = 33
16 blue = 34
17 + gray = 37
18 )
19
20 var (
21 baseTimestamp time.Time
22 isTerminal bool
23 - noQuoteNeeded *regexp.Regexp
23 )
24
25 func init() {
@@ -46,15 +45,22 @@ type TextFormatter struct {
45 // Enable logging the full timestamp when a TTY is attached instead of just
46 // the time passed since beginning of execution.
47 FullTimestamp bool
48 +
49 + // The fields are sorted by default for a consistent output. For applications
50 + // that log extremely frequently and don't use the JSON formatter this may not
51 + // be desired.
52 + DisableSorting bool
53 }
54
55 func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
52 -
56 var keys []string = make([]string, 0, len(entry.Data))
57 for k := range entry.Data {
58 keys = append(keys, k)
59 }
57 - sort.Strings(keys)
60 +
61 + if !f.DisableSorting {
62 + sort.Strings(keys)
63 + }
64
65 b := &bytes.Buffer{}
66
@@ -82,6 +88,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
88 func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
89 var levelColor int
90 switch entry.Level {
91 + case DebugLevel:
92 + levelColor = gray
93 case WarnLevel:
94 levelColor = yellow
95 case ErrorLevel, FatalLevel, PanicLevel:
@@ -107,7 +115,7 @@ func needsQuoting(text string) bool {
115 for _, ch := range text {
116 if !((ch >= 'a' && ch <= 'z') ||
117 (ch >= 'A' && ch <= 'Z') ||
110 - (ch >= '0' && ch < '9') ||
118 + (ch >= '0' && ch <= '9') ||
119 ch == '-' || ch == '.') {
120 return false
121 }
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go
+4
@@ -25,9 +25,13 @@ func TestQuoting(t *testing.T) {
25
26 checkQuoting(false, "abcd")
27 checkQuoting(false, "v1.0")
28 + checkQuoting(false, "1234567890")
29 checkQuoting(true, "/foobar")
30 checkQuoting(true, "x y")
31 checkQuoting(true, "x,y")
32 checkQuoting(false, errors.New("invalid"))
33 checkQuoting(true, errors.New("invalid argument"))
34 }
35 +
36 +// TODO add tests for sorting etc., this requires a parser for the text
37 +// formatter output.
Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go
+1 -1
@@ -6,7 +6,7 @@ import (
6 "runtime"
7 )
8
9 -func (logger *Logger) Writer() (*io.PipeWriter) {
9 +func (logger *Logger) Writer() *io.PipeWriter {
10 reader, writer := io.Pipe()
11
12 go logger.writerScanner(reader)
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
+7
@@ -58,6 +58,13 @@ func NewVersion(version string) (*Version, error) {
58 return &v, nil
59 }
60
61 +func Must(v *Version, err error) *Version {
62 + if err != nil {
63 + panic(err)
64 + }
65 + return v
66 +}
67 +
68 func (v *Version) String() string {
69 var buffer bytes.Buffer
70
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
+36
@@ -1,7 +1,9 @@
1 package semver
2
3 import (
4 + "errors"
5 "math/rand"
6 + "reflect"
7 "testing"
8 "time"
9 )
@@ -185,3 +187,37 @@ func TestBumpPatch(t *testing.T) {
187 t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
188 }
189 }
190 +
191 +func TestMust(t *testing.T) {
192 + tests := []struct {
193 + versionStr string
194 +
195 + version *Version
196 + recov interface{}
197 + }{
198 + {
199 + versionStr: "1.0.0",
200 + version: &Version{Major: 1},
201 + },
202 + {
203 + versionStr: "version number",
204 + recov: errors.New("version number is not in dotted-tri format"),
205 + },
206 + }
207 +
208 + for _, tt := range tests {
209 + func() {
210 + defer func() {
211 + recov := recover()
212 + if !reflect.DeepEqual(tt.recov, recov) {
213 + t.Fatalf("incorrect panic for %q: want %v, got %v", tt.versionStr, tt.recov, recov)
214 + }
215 + }()
216 +
217 + version := Must(NewVersion(tt.versionStr))
218 + if !reflect.DeepEqual(tt.version, version) {
219 + t.Fatalf("incorrect version for %q: want %+v, got %+v", tt.versionStr, tt.version, version)
220 + }
221 + }()
222 + }
223 +}
Godeps/_workspace/src/github.com/gorilla/context/.travis.yml deleted
-7
@@ -1,7 +0,0 @@
1 -language: go
2 -
3 -go:
4 - - 1.0
5 - - 1.1
6 - - 1.2
7 - - tip
Godeps/_workspace/src/github.com/gorilla/context/LICENSE deleted
-27
@@ -1,27 +0,0 @@
1 -Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2 -
3 -Redistribution and use in source and binary forms, with or without
4 -modification, are permitted provided that the following conditions are
5 -met:
6 -
7 - * Redistributions of source code must retain the above copyright
8 -notice, this list of conditions and the following disclaimer.
9 - * Redistributions in binary form must reproduce the above
10 -copyright notice, this list of conditions and the following disclaimer
11 -in the documentation and/or other materials provided with the
12 -distribution.
13 - * Neither the name of Google Inc. nor the names of its
14 -contributors may be used to endorse or promote products derived from
15 -this software without specific prior written permission.
16 -
17 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/gorilla/context/README.md deleted
-7
@@ -1,7 +0,0 @@
1 -context
2 -=======
3 -[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
4 -
5 -gorilla/context is a general purpose registry for global request variables.
6 -
7 -Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
Godeps/_workspace/src/github.com/gorilla/context/context.go deleted
-143
@@ -1,143 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package context
6 -
7 -import (
8 - "net/http"
9 - "sync"
10 - "time"
11 -)
12 -
13 -var (
14 - mutex sync.RWMutex
15 - data = make(map[*http.Request]map[interface{}]interface{})
16 - datat = make(map[*http.Request]int64)
17 -)
18 -
19 -// Set stores a value for a given key in a given request.
20 -func Set(r *http.Request, key, val interface{}) {
21 - mutex.Lock()
22 - if data[r] == nil {
23 - data[r] = make(map[interface{}]interface{})
24 - datat[r] = time.Now().Unix()
25 - }
26 - data[r][key] = val
27 - mutex.Unlock()
28 -}
29 -
30 -// Get returns a value stored for a given key in a given request.
31 -func Get(r *http.Request, key interface{}) interface{} {
32 - mutex.RLock()
33 - if ctx := data[r]; ctx != nil {
34 - value := ctx[key]
35 - mutex.RUnlock()
36 - return value
37 - }
38 - mutex.RUnlock()
39 - return nil
40 -}
41 -
42 -// GetOk returns stored value and presence state like multi-value return of map access.
43 -func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
44 - mutex.RLock()
45 - if _, ok := data[r]; ok {
46 - value, ok := data[r][key]
47 - mutex.RUnlock()
48 - return value, ok
49 - }
50 - mutex.RUnlock()
51 - return nil, false
52 -}
53 -
54 -// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
55 -func GetAll(r *http.Request) map[interface{}]interface{} {
56 - mutex.RLock()
57 - if context, ok := data[r]; ok {
58 - result := make(map[interface{}]interface{}, len(context))
59 - for k, v := range context {
60 - result[k] = v
61 - }
62 - mutex.RUnlock()
63 - return result
64 - }
65 - mutex.RUnlock()
66 - return nil
67 -}
68 -
69 -// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
70 -// the request was registered.
71 -func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
72 - mutex.RLock()
73 - context, ok := data[r]
74 - result := make(map[interface{}]interface{}, len(context))
75 - for k, v := range context {
76 - result[k] = v
77 - }
78 - mutex.RUnlock()
79 - return result, ok
80 -}
81 -
82 -// Delete removes a value stored for a given key in a given request.
83 -func Delete(r *http.Request, key interface{}) {
84 - mutex.Lock()
85 - if data[r] != nil {
86 - delete(data[r], key)
87 - }
88 - mutex.Unlock()
89 -}
90 -
91 -// Clear removes all values stored for a given request.
92 -//
93 -// This is usually called by a handler wrapper to clean up request
94 -// variables at the end of a request lifetime. See ClearHandler().
95 -func Clear(r *http.Request) {
96 - mutex.Lock()
97 - clear(r)
98 - mutex.Unlock()
99 -}
100 -
101 -// clear is Clear without the lock.
102 -func clear(r *http.Request) {
103 - delete(data, r)
104 - delete(datat, r)
105 -}
106 -
107 -// Purge removes request data stored for longer than maxAge, in seconds.
108 -// It returns the amount of requests removed.
109 -//
110 -// If maxAge <= 0, all request data is removed.
111 -//
112 -// This is only used for sanity check: in case context cleaning was not
113 -// properly set some request data can be kept forever, consuming an increasing
114 -// amount of memory. In case this is detected, Purge() must be called
115 -// periodically until the problem is fixed.
116 -func Purge(maxAge int) int {
117 - mutex.Lock()
118 - count := 0
119 - if maxAge <= 0 {
120 - count = len(data)
121 - data = make(map[*http.Request]map[interface{}]interface{})
122 - datat = make(map[*http.Request]int64)
123 - } else {
124 - min := time.Now().Unix() - int64(maxAge)
125 - for r := range data {
126 - if datat[r] < min {
127 - clear(r)
128 - count++
129 - }
130 - }
131 - }
132 - mutex.Unlock()
133 - return count
134 -}
135 -
136 -// ClearHandler wraps an http.Handler and clears request values at the end
137 -// of a request lifetime.
138 -func ClearHandler(h http.Handler) http.Handler {
139 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
140 - defer Clear(r)
141 - h.ServeHTTP(w, r)
142 - })
143 -}
Godeps/_workspace/src/github.com/gorilla/context/context_test.go deleted
-161
@@ -1,161 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package context
6 -
7 -import (
8 - "net/http"
9 - "testing"
10 -)
11 -
12 -type keyType int
13 -
14 -const (
15 - key1 keyType = iota
16 - key2
17 -)
18 -
19 -func TestContext(t *testing.T) {
20 - assertEqual := func(val interface{}, exp interface{}) {
21 - if val != exp {
22 - t.Errorf("Expected %v, got %v.", exp, val)
23 - }
24 - }
25 -
26 - r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
27 - emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
28 -
29 - // Get()
30 - assertEqual(Get(r, key1), nil)
31 -
32 - // Set()
33 - Set(r, key1, "1")
34 - assertEqual(Get(r, key1), "1")
35 - assertEqual(len(data[r]), 1)
36 -
37 - Set(r, key2, "2")
38 - assertEqual(Get(r, key2), "2")
39 - assertEqual(len(data[r]), 2)
40 -
41 - //GetOk
42 - value, ok := GetOk(r, key1)
43 - assertEqual(value, "1")
44 - assertEqual(ok, true)
45 -
46 - value, ok = GetOk(r, "not exists")
47 - assertEqual(value, nil)
48 - assertEqual(ok, false)
49 -
50 - Set(r, "nil value", nil)
51 - value, ok = GetOk(r, "nil value")
52 - assertEqual(value, nil)
53 - assertEqual(ok, true)
54 -
55 - // GetAll()
56 - values := GetAll(r)
57 - assertEqual(len(values), 3)
58 -
59 - // GetAll() for empty request
60 - values = GetAll(emptyR)
61 - if values != nil {
62 - t.Error("GetAll didn't return nil value for invalid request")
63 - }
64 -
65 - // GetAllOk()
66 - values, ok = GetAllOk(r)
67 - assertEqual(len(values), 3)
68 - assertEqual(ok, true)
69 -
70 - // GetAllOk() for empty request
71 - values, ok = GetAllOk(emptyR)
72 - assertEqual(value, nil)
73 - assertEqual(ok, false)
74 -
75 - // Delete()
76 - Delete(r, key1)
77 - assertEqual(Get(r, key1), nil)
78 - assertEqual(len(data[r]), 2)
79 -
80 - Delete(r, key2)
81 - assertEqual(Get(r, key2), nil)
82 - assertEqual(len(data[r]), 1)
83 -
84 - // Clear()
85 - Clear(r)
86 - assertEqual(len(data), 0)
87 -}
88 -
89 -func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
90 - <-wait
91 - for i := 0; i < iterations; i++ {
92 - Get(r, key)
93 - }
94 - done <- struct{}{}
95 -
96 -}
97 -
98 -func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
99 - <-wait
100 - for i := 0; i < iterations; i++ {
101 - Get(r, key)
102 - }
103 - done <- struct{}{}
104 -
105 -}
106 -
107 -func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
108 -
109 - b.StopTimer()
110 - r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
111 - done := make(chan struct{})
112 - b.StartTimer()
113 -
114 - for i := 0; i < b.N; i++ {
115 - wait := make(chan struct{})
116 -
117 - for i := 0; i < numReaders; i++ {
118 - go parallelReader(r, "test", iterations, wait, done)
119 - }
120 -
121 - for i := 0; i < numWriters; i++ {
122 - go parallelWriter(r, "test", "123", iterations, wait, done)
123 - }
124 -
125 - close(wait)
126 -
127 - for i := 0; i < numReaders+numWriters; i++ {
128 - <-done
129 - }
130 -
131 - }
132 -
133 -}
134 -
135 -func BenchmarkMutexSameReadWrite1(b *testing.B) {
136 - benchmarkMutex(b, 1, 1, 32)
137 -}
138 -func BenchmarkMutexSameReadWrite2(b *testing.B) {
139 - benchmarkMutex(b, 2, 2, 32)
140 -}
141 -func BenchmarkMutexSameReadWrite4(b *testing.B) {
142 - benchmarkMutex(b, 4, 4, 32)
143 -}
144 -func BenchmarkMutex1(b *testing.B) {
145 - benchmarkMutex(b, 2, 8, 32)
146 -}
147 -func BenchmarkMutex2(b *testing.B) {
148 - benchmarkMutex(b, 16, 4, 64)
149 -}
150 -func BenchmarkMutex3(b *testing.B) {
151 - benchmarkMutex(b, 1, 2, 128)
152 -}
153 -func BenchmarkMutex4(b *testing.B) {
154 - benchmarkMutex(b, 128, 32, 256)
155 -}
156 -func BenchmarkMutex5(b *testing.B) {
157 - benchmarkMutex(b, 1024, 2048, 64)
158 -}
159 -func BenchmarkMutex6(b *testing.B) {
160 - benchmarkMutex(b, 2048, 1024, 512)
161 -}
Godeps/_workspace/src/github.com/gorilla/context/doc.go deleted
-82
@@ -1,82 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -/*
6 -Package context stores values shared during a request lifetime.
7 -
8 -For example, a router can set variables extracted from the URL and later
9 -application handlers can access those values, or it can be used to store
10 -sessions values to be saved at the end of a request. There are several
11 -others common uses.
12 -
13 -The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
14 -
15 - http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
16 -
17 -Here's the basic usage: first define the keys that you will need. The key
18 -type is interface{} so a key can be of any type that supports equality.
19 -Here we define a key using a custom int type to avoid name collisions:
20 -
21 - package foo
22 -
23 - import (
24 - "github.com/gorilla/context"
25 - )
26 -
27 - type key int
28 -
29 - const MyKey key = 0
30 -
31 -Then set a variable. Variables are bound to an http.Request object, so you
32 -need a request instance to set a value:
33 -
34 - context.Set(r, MyKey, "bar")
35 -
36 -The application can later access the variable using the same key you provided:
37 -
38 - func MyHandler(w http.ResponseWriter, r *http.Request) {
39 - // val is "bar".
40 - val := context.Get(r, foo.MyKey)
41 -
42 - // returns ("bar", true)
43 - val, ok := context.GetOk(r, foo.MyKey)
44 - // ...
45 - }
46 -
47 -And that's all about the basic usage. We discuss some other ideas below.
48 -
49 -Any type can be stored in the context. To enforce a given type, make the key
50 -private and wrap Get() and Set() to accept and return values of a specific
51 -type:
52 -
53 - type key int
54 -
55 - const mykey key = 0
56 -
57 - // GetMyKey returns a value for this package from the request values.
58 - func GetMyKey(r *http.Request) SomeType {
59 - if rv := context.Get(r, mykey); rv != nil {
60 - return rv.(SomeType)
61 - }
62 - return nil
63 - }
64 -
65 - // SetMyKey sets a value for this package in the request values.
66 - func SetMyKey(r *http.Request, val SomeType) {
67 - context.Set(r, mykey, val)
68 - }
69 -
70 -Variables must be cleared at the end of a request, to remove all values
71 -that were stored. This can be done in an http.Handler, after a request was
72 -served. Just call Clear() passing the request:
73 -
74 - context.Clear(r)
75 -
76 -...or use ClearHandler(), which conveniently wraps an http.Handler to clear
77 -variables at the end of a request lifetime.
78 -
79 -The Routers from the packages gorilla/mux and gorilla/pat call Clear()
80 -so if you are using either of them you don't need to clear the context manually.
81 -*/
82 -package context
Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml deleted
-7
@@ -1,7 +0,0 @@
1 -language: go
2 -
3 -go:
4 - - 1.0
5 - - 1.1
6 - - 1.2
7 - - tip
Godeps/_workspace/src/github.com/gorilla/mux/LICENSE deleted
-27
@@ -1,27 +0,0 @@
1 -Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2 -
3 -Redistribution and use in source and binary forms, with or without
4 -modification, are permitted provided that the following conditions are
5 -met:
6 -
7 - * Redistributions of source code must retain the above copyright
8 -notice, this list of conditions and the following disclaimer.
9 - * Redistributions in binary form must reproduce the above
10 -copyright notice, this list of conditions and the following disclaimer
11 -in the documentation and/or other materials provided with the
12 -distribution.
13 - * Neither the name of Google Inc. nor the names of its
14 -contributors may be used to endorse or promote products derived from
15 -this software without specific prior written permission.
16 -
17 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/gorilla/mux/README.md deleted
-7
@@ -1,7 +0,0 @@
1 -mux
2 -===
3 -[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
4 -
5 -gorilla/mux is a powerful URL router and dispatcher.
6 -
7 -Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux
Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go deleted
-21
@@ -1,21 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package mux
6 -
7 -import (
8 - "net/http"
9 - "testing"
10 -)
11 -
12 -func BenchmarkMux(b *testing.B) {
13 - router := new(Router)
14 - handler := func(w http.ResponseWriter, r *http.Request) {}
15 - router.HandleFunc("/v1/{v1}", handler)
16 -
17 - request, _ := http.NewRequest("GET", "/v1/anything", nil)
18 - for i := 0; i < b.N; i++ {
19 - router.ServeHTTP(nil, request)
20 - }
21 -}
Godeps/_workspace/src/github.com/gorilla/mux/doc.go deleted
-199
@@ -1,199 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -/*
6 -Package gorilla/mux implements a request router and dispatcher.
7 -
8 -The name mux stands for "HTTP request multiplexer". Like the standard
9 -http.ServeMux, mux.Router matches incoming requests against a list of
10 -registered routes and calls a handler for the route that matches the URL
11 -or other conditions. The main features are:
12 -
13 - * Requests can be matched based on URL host, path, path prefix, schemes,
14 - header and query values, HTTP methods or using custom matchers.
15 - * URL hosts and paths can have variables with an optional regular
16 - expression.
17 - * Registered URLs can be built, or "reversed", which helps maintaining
18 - references to resources.
19 - * Routes can be used as subrouters: nested routes are only tested if the
20 - parent route matches. This is useful to define groups of routes that
21 - share common conditions like a host, a path prefix or other repeated
22 - attributes. As a bonus, this optimizes request matching.
23 - * It implements the http.Handler interface so it is compatible with the
24 - standard http.ServeMux.
25 -
26 -Let's start registering a couple of URL paths and handlers:
27 -
28 - func main() {
29 - r := mux.NewRouter()
30 - r.HandleFunc("/", HomeHandler)
31 - r.HandleFunc("/products", ProductsHandler)
32 - r.HandleFunc("/articles", ArticlesHandler)
33 - http.Handle("/", r)
34 - }
35 -
36 -Here we register three routes mapping URL paths to handlers. This is
37 -equivalent to how http.HandleFunc() works: if an incoming request URL matches
38 -one of the paths, the corresponding handler is called passing
39 -(http.ResponseWriter, *http.Request) as parameters.
40 -
41 -Paths can have variables. They are defined using the format {name} or
42 -{name:pattern}. If a regular expression pattern is not defined, the matched
43 -variable will be anything until the next slash. For example:
44 -
45 - r := mux.NewRouter()
46 - r.HandleFunc("/products/{key}", ProductHandler)
47 - r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
48 - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
49 -
50 -The names are used to create a map of route variables which can be retrieved
51 -calling mux.Vars():
52 -
53 - vars := mux.Vars(request)
54 - category := vars["category"]
55 -
56 -And this is all you need to know about the basic usage. More advanced options
57 -are explained below.
58 -
59 -Routes can also be restricted to a domain or subdomain. Just define a host
60 -pattern to be matched. They can also have variables:
61 -
62 - r := mux.NewRouter()
63 - // Only matches if domain is "www.domain.com".
64 - r.Host("www.domain.com")
65 - // Matches a dynamic subdomain.
66 - r.Host("{subdomain:[a-z]+}.domain.com")
67 -
68 -There are several other matchers that can be added. To match path prefixes:
69 -
70 - r.PathPrefix("/products/")
71 -
72 -...or HTTP methods:
73 -
74 - r.Methods("GET", "POST")
75 -
76 -...or URL schemes:
77 -
78 - r.Schemes("https")
79 -
80 -...or header values:
81 -
82 - r.Headers("X-Requested-With", "XMLHttpRequest")
83 -
84 -...or query values:
85 -
86 - r.Queries("key", "value")
87 -
88 -...or to use a custom matcher function:
89 -
90 - r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
91 - return r.ProtoMajor == 0
92 - })
93 -
94 -...and finally, it is possible to combine several matchers in a single route:
95 -
96 - r.HandleFunc("/products", ProductsHandler).
97 - Host("www.domain.com").
98 - Methods("GET").
99 - Schemes("http")
100 -
101 -Setting the same matching conditions again and again can be boring, so we have
102 -a way to group several routes that share the same requirements.
103 -We call it "subrouting".
104 -
105 -For example, let's say we have several URLs that should only match when the
106 -host is "www.domain.com". Create a route for that host and get a "subrouter"
107 -from it:
108 -
109 - r := mux.NewRouter()
110 - s := r.Host("www.domain.com").Subrouter()
111 -
112 -Then register routes in the subrouter:
113 -
114 - s.HandleFunc("/products/", ProductsHandler)
115 - s.HandleFunc("/products/{key}", ProductHandler)
116 - s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
117 -
118 -The three URL paths we registered above will only be tested if the domain is
119 -"www.domain.com", because the subrouter is tested first. This is not
120 -only convenient, but also optimizes request matching. You can create
121 -subrouters combining any attribute matchers accepted by a route.
122 -
123 -Subrouters can be used to create domain or path "namespaces": you define
124 -subrouters in a central place and then parts of the app can register its
125 -paths relatively to a given subrouter.
126 -
127 -There's one more thing about subroutes. When a subrouter has a path prefix,
128 -the inner routes use it as base for their paths:
129 -
130 - r := mux.NewRouter()
131 - s := r.PathPrefix("/products").Subrouter()
132 - // "/products/"
133 - s.HandleFunc("/", ProductsHandler)
134 - // "/products/{key}/"
135 - s.HandleFunc("/{key}/", ProductHandler)
136 - // "/products/{key}/details"
137 - s.HandleFunc("/{key}/details", ProductDetailsHandler)
138 -
139 -Now let's see how to build registered URLs.
140 -
141 -Routes can be named. All routes that define a name can have their URLs built,
142 -or "reversed". We define a name calling Name() on a route. For example:
143 -
144 - r := mux.NewRouter()
145 - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
146 - Name("article")
147 -
148 -To build a URL, get the route and call the URL() method, passing a sequence of
149 -key/value pairs for the route variables. For the previous route, we would do:
150 -
151 - url, err := r.Get("article").URL("category", "technology", "id", "42")
152 -
153 -...and the result will be a url.URL with the following path:
154 -
155 - "/articles/technology/42"
156 -
157 -This also works for host variables:
158 -
159 - r := mux.NewRouter()
160 - r.Host("{subdomain}.domain.com").
161 - Path("/articles/{category}/{id:[0-9]+}").
162 - HandlerFunc(ArticleHandler).
163 - Name("article")
164 -
165 - // url.String() will be "http://news.domain.com/articles/technology/42"
166 - url, err := r.Get("article").URL("subdomain", "news",
167 - "category", "technology",
168 - "id", "42")
169 -
170 -All variables defined in the route are required, and their values must
171 -conform to the corresponding patterns. These requirements guarantee that a
172 -generated URL will always match a registered route -- the only exception is
173 -for explicitly defined "build-only" routes which never match.
174 -
175 -There's also a way to build only the URL host or path for a route:
176 -use the methods URLHost() or URLPath() instead. For the previous route,
177 -we would do:
178 -
179 - // "http://news.domain.com/"
180 - host, err := r.Get("article").URLHost("subdomain", "news")
181 -
182 - // "/articles/technology/42"
183 - path, err := r.Get("article").URLPath("category", "technology", "id", "42")
184 -
185 -And if you use subrouters, host and path defined separately can be built
186 -as well:
187 -
188 - r := mux.NewRouter()
189 - s := r.Host("{subdomain}.domain.com").Subrouter()
190 - s.Path("/articles/{category}/{id:[0-9]+}").
191 - HandlerFunc(ArticleHandler).
192 - Name("article")
193 -
194 - // "http://news.domain.com/articles/technology/42"
195 - url, err := r.Get("article").URL("subdomain", "news",
196 - "category", "technology",
197 - "id", "42")
198 -*/
199 -package mux
Godeps/_workspace/src/github.com/gorilla/mux/mux.go deleted
-353
@@ -1,353 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package mux
6 -
7 -import (
8 - "fmt"
9 - "net/http"
10 - "path"
11 -
12 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/context"
13 -)
14 -
15 -// NewRouter returns a new router instance.
16 -func NewRouter() *Router {
17 - return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
18 -}
19 -
20 -// Router registers routes to be matched and dispatches a handler.
21 -//
22 -// It implements the http.Handler interface, so it can be registered to serve
23 -// requests:
24 -//
25 -// var router = mux.NewRouter()
26 -//
27 -// func main() {
28 -// http.Handle("/", router)
29 -// }
30 -//
31 -// Or, for Google App Engine, register it in a init() function:
32 -//
33 -// func init() {
34 -// http.Handle("/", router)
35 -// }
36 -//
37 -// This will send all incoming requests to the router.
38 -type Router struct {
39 - // Configurable Handler to be used when no route matches.
40 - NotFoundHandler http.Handler
41 - // Parent route, if this is a subrouter.
42 - parent parentRoute
43 - // Routes to be matched, in order.
44 - routes []*Route
45 - // Routes by name for URL building.
46 - namedRoutes map[string]*Route
47 - // See Router.StrictSlash(). This defines the flag for new routes.
48 - strictSlash bool
49 - // If true, do not clear the request context after handling the request
50 - KeepContext bool
51 -}
52 -
53 -// Match matches registered routes against the request.
54 -func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
55 - for _, route := range r.routes {
56 - if route.Match(req, match) {
57 - return true
58 - }
59 - }
60 - return false
61 -}
62 -
63 -// ServeHTTP dispatches the handler registered in the matched route.
64 -//
65 -// When there is a match, the route variables can be retrieved calling
66 -// mux.Vars(request).
67 -func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
68 - // Clean path to canonical form and redirect.
69 - if p := cleanPath(req.URL.Path); p != req.URL.Path {
70 -
71 - // Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
72 - // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
73 - // http://code.google.com/p/go/issues/detail?id=5252
74 - url := *req.URL
75 - url.Path = p
76 - p = url.String()
77 -
78 - w.Header().Set("Location", p)
79 - w.WriteHeader(http.StatusMovedPermanently)
80 - return
81 - }
82 - var match RouteMatch
83 - var handler http.Handler
84 - if r.Match(req, &match) {
85 - handler = match.Handler
86 - setVars(req, match.Vars)
87 - setCurrentRoute(req, match.Route)
88 - }
89 - if handler == nil {
90 - handler = r.NotFoundHandler
91 - if handler == nil {
92 - handler = http.NotFoundHandler()
93 - }
94 - }
95 - if !r.KeepContext {
96 - defer context.Clear(req)
97 - }
98 - handler.ServeHTTP(w, req)
99 -}
100 -
101 -// Get returns a route registered with the given name.
102 -func (r *Router) Get(name string) *Route {
103 - return r.getNamedRoutes()[name]
104 -}
105 -
106 -// GetRoute returns a route registered with the given name. This method
107 -// was renamed to Get() and remains here for backwards compatibility.
108 -func (r *Router) GetRoute(name string) *Route {
109 - return r.getNamedRoutes()[name]
110 -}
111 -
112 -// StrictSlash defines the trailing slash behavior for new routes. The initial
113 -// value is false.
114 -//
115 -// When true, if the route path is "/path/", accessing "/path" will redirect
116 -// to the former and vice versa. In other words, your application will always
117 -// see the path as specified in the route.
118 -//
119 -// When false, if the route path is "/path", accessing "/path/" will not match
120 -// this route and vice versa.
121 -//
122 -// Special case: when a route sets a path prefix using the PathPrefix() method,
123 -// strict slash is ignored for that route because the redirect behavior can't
124 -// be determined from a prefix alone. However, any subrouters created from that
125 -// route inherit the original StrictSlash setting.
126 -func (r *Router) StrictSlash(value bool) *Router {
127 - r.strictSlash = value
128 - return r
129 -}
130 -
131 -// ----------------------------------------------------------------------------
132 -// parentRoute
133 -// ----------------------------------------------------------------------------
134 -
135 -// getNamedRoutes returns the map where named routes are registered.
136 -func (r *Router) getNamedRoutes() map[string]*Route {
137 - if r.namedRoutes == nil {
138 - if r.parent != nil {
139 - r.namedRoutes = r.parent.getNamedRoutes()
140 - } else {
141 - r.namedRoutes = make(map[string]*Route)
142 - }
143 - }
144 - return r.namedRoutes
145 -}
146 -
147 -// getRegexpGroup returns regexp definitions from the parent route, if any.
148 -func (r *Router) getRegexpGroup() *routeRegexpGroup {
149 - if r.parent != nil {
150 - return r.parent.getRegexpGroup()
151 - }
152 - return nil
153 -}
154 -
155 -// ----------------------------------------------------------------------------
156 -// Route factories
157 -// ----------------------------------------------------------------------------
158 -
159 -// NewRoute registers an empty route.
160 -func (r *Router) NewRoute() *Route {
161 - route := &Route{parent: r, strictSlash: r.strictSlash}
162 - r.routes = append(r.routes, route)
163 - return route
164 -}
165 -
166 -// Handle registers a new route with a matcher for the URL path.
167 -// See Route.Path() and Route.Handler().
168 -func (r *Router) Handle(path string, handler http.Handler) *Route {
169 - return r.NewRoute().Path(path).Handler(handler)
170 -}
171 -
172 -// HandleFunc registers a new route with a matcher for the URL path.
173 -// See Route.Path() and Route.HandlerFunc().
174 -func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
175 - *http.Request)) *Route {
176 - return r.NewRoute().Path(path).HandlerFunc(f)
177 -}
178 -
179 -// Headers registers a new route with a matcher for request header values.
180 -// See Route.Headers().
181 -func (r *Router) Headers(pairs ...string) *Route {
182 - return r.NewRoute().Headers(pairs...)
183 -}
184 -
185 -// Host registers a new route with a matcher for the URL host.
186 -// See Route.Host().
187 -func (r *Router) Host(tpl string) *Route {
188 - return r.NewRoute().Host(tpl)
189 -}
190 -
191 -// MatcherFunc registers a new route with a custom matcher function.
192 -// See Route.MatcherFunc().
193 -func (r *Router) MatcherFunc(f MatcherFunc) *Route {
194 - return r.NewRoute().MatcherFunc(f)
195 -}
196 -
197 -// Methods registers a new route with a matcher for HTTP methods.
198 -// See Route.Methods().
199 -func (r *Router) Methods(methods ...string) *Route {
200 - return r.NewRoute().Methods(methods...)
201 -}
202 -
203 -// Path registers a new route with a matcher for the URL path.
204 -// See Route.Path().
205 -func (r *Router) Path(tpl string) *Route {
206 - return r.NewRoute().Path(tpl)
207 -}
208 -
209 -// PathPrefix registers a new route with a matcher for the URL path prefix.
210 -// See Route.PathPrefix().
211 -func (r *Router) PathPrefix(tpl string) *Route {
212 - return r.NewRoute().PathPrefix(tpl)
213 -}
214 -
215 -// Queries registers a new route with a matcher for URL query values.
216 -// See Route.Queries().
217 -func (r *Router) Queries(pairs ...string) *Route {
218 - return r.NewRoute().Queries(pairs...)
219 -}
220 -
221 -// Schemes registers a new route with a matcher for URL schemes.
222 -// See Route.Schemes().
223 -func (r *Router) Schemes(schemes ...string) *Route {
224 - return r.NewRoute().Schemes(schemes...)
225 -}
226 -
227 -// ----------------------------------------------------------------------------
228 -// Context
229 -// ----------------------------------------------------------------------------
230 -
231 -// RouteMatch stores information about a matched route.
232 -type RouteMatch struct {
233 - Route *Route
234 - Handler http.Handler
235 - Vars map[string]string
236 -}
237 -
238 -type contextKey int
239 -
240 -const (
241 - varsKey contextKey = iota
242 - routeKey
243 -)
244 -
245 -// Vars returns the route variables for the current request, if any.
246 -func Vars(r *http.Request) map[string]string {
247 - if rv := context.Get(r, varsKey); rv != nil {
248 - return rv.(map[string]string)
249 - }
250 - return nil
251 -}
252 -
253 -// CurrentRoute returns the matched route for the current request, if any.
254 -func CurrentRoute(r *http.Request) *Route {
255 - if rv := context.Get(r, routeKey); rv != nil {
256 - return rv.(*Route)
257 - }
258 - return nil
259 -}
260 -
261 -func setVars(r *http.Request, val interface{}) {
262 - context.Set(r, varsKey, val)
263 -}
264 -
265 -func setCurrentRoute(r *http.Request, val interface{}) {
266 - context.Set(r, routeKey, val)
267 -}
268 -
269 -// ----------------------------------------------------------------------------
270 -// Helpers
271 -// ----------------------------------------------------------------------------
272 -
273 -// cleanPath returns the canonical path for p, eliminating . and .. elements.
274 -// Borrowed from the net/http package.
275 -func cleanPath(p string) string {
276 - if p == "" {
277 - return "/"
278 - }
279 - if p[0] != '/' {
280 - p = "/" + p
281 - }
282 - np := path.Clean(p)
283 - // path.Clean removes trailing slash except for root;
284 - // put the trailing slash back if necessary.
285 - if p[len(p)-1] == '/' && np != "/" {
286 - np += "/"
287 - }
288 - return np
289 -}
290 -
291 -// uniqueVars returns an error if two slices contain duplicated strings.
292 -func uniqueVars(s1, s2 []string) error {
293 - for _, v1 := range s1 {
294 - for _, v2 := range s2 {
295 - if v1 == v2 {
296 - return fmt.Errorf("mux: duplicated route variable %q", v2)
297 - }
298 - }
299 - }
300 - return nil
301 -}
302 -
303 -// mapFromPairs converts variadic string parameters to a string map.
304 -func mapFromPairs(pairs ...string) (map[string]string, error) {
305 - length := len(pairs)
306 - if length%2 != 0 {
307 - return nil, fmt.Errorf(
308 - "mux: number of parameters must be multiple of 2, got %v", pairs)
309 - }
310 - m := make(map[string]string, length/2)
311 - for i := 0; i < length; i += 2 {
312 - m[pairs[i]] = pairs[i+1]
313 - }
314 - return m, nil
315 -}
316 -
317 -// matchInArray returns true if the given string value is in the array.
318 -func matchInArray(arr []string, value string) bool {
319 - for _, v := range arr {
320 - if v == value {
321 - return true
322 - }
323 - }
324 - return false
325 -}
326 -
327 -// matchMap returns true if the given key/value pairs exist in a given map.
328 -func matchMap(toCheck map[string]string, toMatch map[string][]string,
329 - canonicalKey bool) bool {
330 - for k, v := range toCheck {
331 - // Check if key exists.
332 - if canonicalKey {
333 - k = http.CanonicalHeaderKey(k)
334 - }
335 - if values := toMatch[k]; values == nil {
336 - return false
337 - } else if v != "" {
338 - // If value was defined as an empty string we only check that the
339 - // key exists. Otherwise we also check for equality.
340 - valueExists := false
341 - for _, value := range values {
342 - if v == value {
343 - valueExists = true
344 - break
345 - }
346 - }
347 - if !valueExists {
348 - return false
349 - }
350 - }
351 - }
352 - return true
353 -}
Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go deleted
-943
@@ -1,943 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package mux
6 -
7 -import (
8 - "fmt"
9 - "net/http"
10 - "testing"
11 -
12 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/context"
13 -)
14 -
15 -type routeTest struct {
16 - title string // title of the test
17 - route *Route // the route being tested
18 - request *http.Request // a request to test the route
19 - vars map[string]string // the expected vars of the match
20 - host string // the expected host of the match
21 - path string // the expected path of the match
22 - shouldMatch bool // whether the request is expected to match the route at all
23 - shouldRedirect bool // whether the request should result in a redirect
24 -}
25 -
26 -func TestHost(t *testing.T) {
27 - // newRequestHost a new request with a method, url, and host header
28 - newRequestHost := func(method, url, host string) *http.Request {
29 - req, err := http.NewRequest(method, url, nil)
30 - if err != nil {
31 - panic(err)
32 - }
33 - req.Host = host
34 - return req
35 - }
36 -
37 - tests := []routeTest{
38 - {
39 - title: "Host route match",
40 - route: new(Route).Host("aaa.bbb.ccc"),
41 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
42 - vars: map[string]string{},
43 - host: "aaa.bbb.ccc",
44 - path: "",
45 - shouldMatch: true,
46 - },
47 - {
48 - title: "Host route, wrong host in request URL",
49 - route: new(Route).Host("aaa.bbb.ccc"),
50 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
51 - vars: map[string]string{},
52 - host: "aaa.bbb.ccc",
53 - path: "",
54 - shouldMatch: false,
55 - },
56 - {
57 - title: "Host route with port, match",
58 - route: new(Route).Host("aaa.bbb.ccc:1234"),
59 - request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
60 - vars: map[string]string{},
61 - host: "aaa.bbb.ccc:1234",
62 - path: "",
63 - shouldMatch: true,
64 - },
65 - {
66 - title: "Host route with port, wrong port in request URL",
67 - route: new(Route).Host("aaa.bbb.ccc:1234"),
68 - request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
69 - vars: map[string]string{},
70 - host: "aaa.bbb.ccc:1234",
71 - path: "",
72 - shouldMatch: false,
73 - },
74 - {
75 - title: "Host route, match with host in request header",
76 - route: new(Route).Host("aaa.bbb.ccc"),
77 - request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
78 - vars: map[string]string{},
79 - host: "aaa.bbb.ccc",
80 - path: "",
81 - shouldMatch: true,
82 - },
83 - {
84 - title: "Host route, wrong host in request header",
85 - route: new(Route).Host("aaa.bbb.ccc"),
86 - request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
87 - vars: map[string]string{},
88 - host: "aaa.bbb.ccc",
89 - path: "",
90 - shouldMatch: false,
91 - },
92 - // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
93 - {
94 - title: "Host route with port, wrong host in request header",
95 - route: new(Route).Host("aaa.bbb.ccc:1234"),
96 - request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
97 - vars: map[string]string{},
98 - host: "aaa.bbb.ccc:1234",
99 - path: "",
100 - shouldMatch: false,
101 - },
102 - {
103 - title: "Host route with pattern, match",
104 - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
105 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
106 - vars: map[string]string{"v1": "bbb"},
107 - host: "aaa.bbb.ccc",
108 - path: "",
109 - shouldMatch: true,
110 - },
111 - {
112 - title: "Host route with pattern, wrong host in request URL",
113 - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
114 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
115 - vars: map[string]string{"v1": "bbb"},
116 - host: "aaa.bbb.ccc",
117 - path: "",
118 - shouldMatch: false,
119 - },
120 - {
121 - title: "Host route with multiple patterns, match",
122 - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
123 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
124 - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
125 - host: "aaa.bbb.ccc",
126 - path: "",
127 - shouldMatch: true,
128 - },
129 - {
130 - title: "Host route with multiple patterns, wrong host in request URL",
131 - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
132 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
133 - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
134 - host: "aaa.bbb.ccc",
135 - path: "",
136 - shouldMatch: false,
137 - },
138 - }
139 - for _, test := range tests {
140 - testRoute(t, test)
141 - }
142 -}
143 -
144 -func TestPath(t *testing.T) {
145 - tests := []routeTest{
146 - {
147 - title: "Path route, match",
148 - route: new(Route).Path("/111/222/333"),
149 - request: newRequest("GET", "http://localhost/111/222/333"),
150 - vars: map[string]string{},
151 - host: "",
152 - path: "/111/222/333",
153 - shouldMatch: true,
154 - },
155 - {
156 - title: "Path route, match with trailing slash in request and path",
157 - route: new(Route).Path("/111/"),
158 - request: newRequest("GET", "http://localhost/111/"),
159 - vars: map[string]string{},
160 - host: "",
161 - path: "/111/",
162 - shouldMatch: true,
163 - },
164 - {
165 - title: "Path route, do not match with trailing slash in path",
166 - route: new(Route).Path("/111/"),
167 - request: newRequest("GET", "http://localhost/111"),
168 - vars: map[string]string{},
169 - host: "",
170 - path: "/111",
171 - shouldMatch: false,
172 - },
173 - {
174 - title: "Path route, do not match with trailing slash in request",
175 - route: new(Route).Path("/111"),
176 - request: newRequest("GET", "http://localhost/111/"),
177 - vars: map[string]string{},
178 - host: "",
179 - path: "/111/",
180 - shouldMatch: false,
181 - },
182 - {
183 - title: "Path route, wrong path in request in request URL",
184 - route: new(Route).Path("/111/222/333"),
185 - request: newRequest("GET", "http://localhost/1/2/3"),
186 - vars: map[string]string{},
187 - host: "",
188 - path: "/111/222/333",
189 - shouldMatch: false,
190 - },
191 - {
192 - title: "Path route with pattern, match",
193 - route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
194 - request: newRequest("GET", "http://localhost/111/222/333"),
195 - vars: map[string]string{"v1": "222"},
196 - host: "",
197 - path: "/111/222/333",
198 - shouldMatch: true,
199 - },
200 - {
201 - title: "Path route with pattern, URL in request does not match",
202 - route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
203 - request: newRequest("GET", "http://localhost/111/aaa/333"),
204 - vars: map[string]string{"v1": "222"},
205 - host: "",
206 - path: "/111/222/333",
207 - shouldMatch: false,
208 - },
209 - {
210 - title: "Path route with multiple patterns, match",
211 - route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
212 - request: newRequest("GET", "http://localhost/111/222/333"),
213 - vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
214 - host: "",
215 - path: "/111/222/333",
216 - shouldMatch: true,
217 - },
218 - {
219 - title: "Path route with multiple patterns, URL in request does not match",
220 - route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
221 - request: newRequest("GET", "http://localhost/111/aaa/333"),
222 - vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
223 - host: "",
224 - path: "/111/222/333",
225 - shouldMatch: false,
226 - },
227 - }
228 -
229 - for _, test := range tests {
230 - testRoute(t, test)
231 - }
232 -}
233 -
234 -func TestPathPrefix(t *testing.T) {
235 - tests := []routeTest{
236 - {
237 - title: "PathPrefix route, match",
238 - route: new(Route).PathPrefix("/111"),
239 - request: newRequest("GET", "http://localhost/111/222/333"),
240 - vars: map[string]string{},
241 - host: "",
242 - path: "/111",
243 - shouldMatch: true,
244 - },
245 - {
246 - title: "PathPrefix route, match substring",
247 - route: new(Route).PathPrefix("/1"),
248 - request: newRequest("GET", "http://localhost/111/222/333"),
249 - vars: map[string]string{},
250 - host: "",
251 - path: "/1",
252 - shouldMatch: true,
253 - },
254 - {
255 - title: "PathPrefix route, URL prefix in request does not match",
256 - route: new(Route).PathPrefix("/111"),
257 - request: newRequest("GET", "http://localhost/1/2/3"),
258 - vars: map[string]string{},
259 - host: "",
260 - path: "/111",
261 - shouldMatch: false,
262 - },
263 - {
264 - title: "PathPrefix route with pattern, match",
265 - route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
266 - request: newRequest("GET", "http://localhost/111/222/333"),
267 - vars: map[string]string{"v1": "222"},
268 - host: "",
269 - path: "/111/222",
270 - shouldMatch: true,
271 - },
272 - {
273 - title: "PathPrefix route with pattern, URL prefix in request does not match",
274 - route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
275 - request: newRequest("GET", "http://localhost/111/aaa/333"),
276 - vars: map[string]string{"v1": "222"},
277 - host: "",
278 - path: "/111/222",
279 - shouldMatch: false,
280 - },
281 - {
282 - title: "PathPrefix route with multiple patterns, match",
283 - route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
284 - request: newRequest("GET", "http://localhost/111/222/333"),
285 - vars: map[string]string{"v1": "111", "v2": "222"},
286 - host: "",
287 - path: "/111/222",
288 - shouldMatch: true,
289 - },
290 - {
291 - title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
292 - route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
293 - request: newRequest("GET", "http://localhost/111/aaa/333"),
294 - vars: map[string]string{"v1": "111", "v2": "222"},
295 - host: "",
296 - path: "/111/222",
297 - shouldMatch: false,
298 - },
299 - }
300 -
301 - for _, test := range tests {
302 - testRoute(t, test)
303 - }
304 -}
305 -
306 -func TestHostPath(t *testing.T) {
307 - tests := []routeTest{
308 - {
309 - title: "Host and Path route, match",
310 - route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
311 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
312 - vars: map[string]string{},
313 - host: "",
314 - path: "",
315 - shouldMatch: true,
316 - },
317 - {
318 - title: "Host and Path route, wrong host in request URL",
319 - route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
320 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
321 - vars: map[string]string{},
322 - host: "",
323 - path: "",
324 - shouldMatch: false,
325 - },
326 - {
327 - title: "Host and Path route with pattern, match",
328 - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
329 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
330 - vars: map[string]string{"v1": "bbb", "v2": "222"},
331 - host: "aaa.bbb.ccc",
332 - path: "/111/222/333",
333 - shouldMatch: true,
334 - },
335 - {
336 - title: "Host and Path route with pattern, URL in request does not match",
337 - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
338 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
339 - vars: map[string]string{"v1": "bbb", "v2": "222"},
340 - host: "aaa.bbb.ccc",
341 - path: "/111/222/333",
342 - shouldMatch: false,
343 - },
344 - {
345 - title: "Host and Path route with multiple patterns, match",
346 - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
347 - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
348 - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
349 - host: "aaa.bbb.ccc",
350 - path: "/111/222/333",
351 - shouldMatch: true,
352 - },
353 - {
354 - title: "Host and Path route with multiple patterns, URL in request does not match",
355 - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
356 - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
357 - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
358 - host: "aaa.bbb.ccc",
359 - path: "/111/222/333",
360 - shouldMatch: false,
361 - },
362 - }
363 -
364 - for _, test := range tests {
365 - testRoute(t, test)
366 - }
367 -}
368 -
369 -func TestHeaders(t *testing.T) {
370 - // newRequestHeaders creates a new request with a method, url, and headers
371 - newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
372 - req, err := http.NewRequest(method, url, nil)
373 - if err != nil {
374 - panic(err)
375 - }
376 - for k, v := range headers {
377 - req.Header.Add(k, v)
378 - }
379 - return req
380 - }
381 -
382 - tests := []routeTest{
383 - {
384 - title: "Headers route, match",
385 - route: new(Route).Headers("foo", "bar", "baz", "ding"),
386 - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
387 - vars: map[string]string{},
388 - host: "",
389 - path: "",
390 - shouldMatch: true,
391 - },
392 - {
393 - title: "Headers route, bad header values",
394 - route: new(Route).Headers("foo", "bar", "baz", "ding"),
395 - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
396 - vars: map[string]string{},
397 - host: "",
398 - path: "",
399 - shouldMatch: false,
400 - },
401 - }
402 -
403 - for _, test := range tests {
404 - testRoute(t, test)
405 - }
406 -
407 -}
408 -
409 -func TestMethods(t *testing.T) {
410 - tests := []routeTest{
411 - {
412 - title: "Methods route, match GET",
413 - route: new(Route).Methods("GET", "POST"),
414 - request: newRequest("GET", "http://localhost"),
415 - vars: map[string]string{},
416 - host: "",
417 - path: "",
418 - shouldMatch: true,
419 - },
420 - {
421 - title: "Methods route, match POST",
422 - route: new(Route).Methods("GET", "POST"),
423 - request: newRequest("POST", "http://localhost"),
424 - vars: map[string]string{},
425 - host: "",
426 - path: "",
427 - shouldMatch: true,
428 - },
429 - {
430 - title: "Methods route, bad method",
431 - route: new(Route).Methods("GET", "POST"),
432 - request: newRequest("PUT", "http://localhost"),
433 - vars: map[string]string{},
434 - host: "",
435 - path: "",
436 - shouldMatch: false,
437 - },
438 - }
439 -
440 - for _, test := range tests {
441 - testRoute(t, test)
442 - }
443 -}
444 -
445 -func TestQueries(t *testing.T) {
446 - tests := []routeTest{
447 - {
448 - title: "Queries route, match",
449 - route: new(Route).Queries("foo", "bar", "baz", "ding"),
450 - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
451 - vars: map[string]string{},
452 - host: "",
453 - path: "",
454 - shouldMatch: true,
455 - },
456 - {
457 - title: "Queries route, match with a query string",
458 - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
459 - request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
460 - vars: map[string]string{},
461 - host: "",
462 - path: "",
463 - shouldMatch: true,
464 - },
465 - {
466 - title: "Queries route, match with a query string out of order",
467 - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
468 - request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
469 - vars: map[string]string{},
470 - host: "",
471 - path: "",
472 - shouldMatch: true,
473 - },
474 - {
475 - title: "Queries route, bad query",
476 - route: new(Route).Queries("foo", "bar", "baz", "ding"),
477 - request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
478 - vars: map[string]string{},
479 - host: "",
480 - path: "",
481 - shouldMatch: false,
482 - },
483 - {
484 - title: "Queries route with pattern, match",
485 - route: new(Route).Queries("foo", "{v1}"),
486 - request: newRequest("GET", "http://localhost?foo=bar"),
487 - vars: map[string]string{"v1": "bar"},
488 - host: "",
489 - path: "",
490 - shouldMatch: true,
491 - },
492 - {
493 - title: "Queries route with multiple patterns, match",
494 - route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
495 - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
496 - vars: map[string]string{"v1": "bar", "v2": "ding"},
497 - host: "",
498 - path: "",
499 - shouldMatch: true,
500 - },
501 - {
502 - title: "Queries route with regexp pattern, match",
503 - route: new(Route).Queries("foo", "{v1:[0-9]+}"),
504 - request: newRequest("GET", "http://localhost?foo=10"),
505 - vars: map[string]string{"v1": "10"},
506 - host: "",
507 - path: "",
508 - shouldMatch: true,
509 - },
510 - {
511 - title: "Queries route with regexp pattern, regexp does not match",
512 - route: new(Route).Queries("foo", "{v1:[0-9]+}"),
513 - request: newRequest("GET", "http://localhost?foo=a"),
514 - vars: map[string]string{},
515 - host: "",
516 - path: "",
517 - shouldMatch: false,
518 - },
519 - }
520 -
521 - for _, test := range tests {
522 - testRoute(t, test)
523 - }
524 -}
525 -
526 -func TestSchemes(t *testing.T) {
527 - tests := []routeTest{
528 - // Schemes
529 - {
530 - title: "Schemes route, match https",
531 - route: new(Route).Schemes("https", "ftp"),
532 - request: newRequest("GET", "https://localhost"),
533 - vars: map[string]string{},
534 - host: "",
535 - path: "",
536 - shouldMatch: true,
537 - },
538 - {
539 - title: "Schemes route, match ftp",
540 - route: new(Route).Schemes("https", "ftp"),
541 - request: newRequest("GET", "ftp://localhost"),
542 - vars: map[string]string{},
543 - host: "",
544 - path: "",
545 - shouldMatch: true,
546 - },
547 - {
548 - title: "Schemes route, bad scheme",
549 - route: new(Route).Schemes("https", "ftp"),
550 - request: newRequest("GET", "http://localhost"),
551 - vars: map[string]string{},
552 - host: "",
553 - path: "",
554 - shouldMatch: false,
555 - },
556 - }
557 - for _, test := range tests {
558 - testRoute(t, test)
559 - }
560 -}
561 -
562 -func TestMatcherFunc(t *testing.T) {
563 - m := func(r *http.Request, m *RouteMatch) bool {
564 - if r.URL.Host == "aaa.bbb.ccc" {
565 - return true
566 - }
567 - return false
568 - }
569 -
570 - tests := []routeTest{
571 - {
572 - title: "MatchFunc route, match",
573 - route: new(Route).MatcherFunc(m),
574 - request: newRequest("GET", "http://aaa.bbb.ccc"),
575 - vars: map[string]string{},
576 - host: "",
577 - path: "",
578 - shouldMatch: true,
579 - },
580 - {
581 - title: "MatchFunc route, non-match",
582 - route: new(Route).MatcherFunc(m),
583 - request: newRequest("GET", "http://aaa.222.ccc"),
584 - vars: map[string]string{},
585 - host: "",
586 - path: "",
587 - shouldMatch: false,
588 - },
589 - }
590 -
591 - for _, test := range tests {
592 - testRoute(t, test)
593 - }
594 -}
595 -
596 -func TestSubRouter(t *testing.T) {
597 - subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
598 - subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
599 -
600 - tests := []routeTest{
601 - {
602 - route: subrouter1.Path("/{v2:[a-z]+}"),
603 - request: newRequest("GET", "http://aaa.google.com/bbb"),
604 - vars: map[string]string{"v1": "aaa", "v2": "bbb"},
605 - host: "aaa.google.com",
606 - path: "/bbb",
607 - shouldMatch: true,
608 - },
609 - {
610 - route: subrouter1.Path("/{v2:[a-z]+}"),
611 - request: newRequest("GET", "http://111.google.com/111"),
612 - vars: map[string]string{"v1": "aaa", "v2": "bbb"},
613 - host: "aaa.google.com",
614 - path: "/bbb",
615 - shouldMatch: false,
616 - },
617 - {
618 - route: subrouter2.Path("/baz/{v2}"),
619 - request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
620 - vars: map[string]string{"v1": "bar", "v2": "ding"},
621 - host: "",
622 - path: "/foo/bar/baz/ding",
623 - shouldMatch: true,
624 - },
625 - {
626 - route: subrouter2.Path("/baz/{v2}"),
627 - request: newRequest("GET", "http://localhost/foo/bar"),
628 - vars: map[string]string{"v1": "bar", "v2": "ding"},
629 - host: "",
630 - path: "/foo/bar/baz/ding",
631 - shouldMatch: false,
632 - },
633 - }
634 -
635 - for _, test := range tests {
636 - testRoute(t, test)
637 - }
638 -}
639 -
640 -func TestNamedRoutes(t *testing.T) {
641 - r1 := NewRouter()
642 - r1.NewRoute().Name("a")
643 - r1.NewRoute().Name("b")
644 - r1.NewRoute().Name("c")
645 -
646 - r2 := r1.NewRoute().Subrouter()
647 - r2.NewRoute().Name("d")
648 - r2.NewRoute().Name("e")
649 - r2.NewRoute().Name("f")
650 -
651 - r3 := r2.NewRoute().Subrouter()
652 - r3.NewRoute().Name("g")
653 - r3.NewRoute().Name("h")
654 - r3.NewRoute().Name("i")
655 -
656 - if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
657 - t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
658 - } else if r1.Get("i") == nil {
659 - t.Errorf("Subroute name not registered")
660 - }
661 -}
662 -
663 -func TestStrictSlash(t *testing.T) {
664 - r := NewRouter()
665 - r.StrictSlash(true)
666 -
667 - tests := []routeTest{
668 - {
669 - title: "Redirect path without slash",
670 - route: r.NewRoute().Path("/111/"),
671 - request: newRequest("GET", "http://localhost/111"),
672 - vars: map[string]string{},
673 - host: "",
674 - path: "/111/",
675 - shouldMatch: true,
676 - shouldRedirect: true,
677 - },
678 - {
679 - title: "Do not redirect path with slash",
680 - route: r.NewRoute().Path("/111/"),
681 - request: newRequest("GET", "http://localhost/111/"),
682 - vars: map[string]string{},
683 - host: "",
684 - path: "/111/",
685 - shouldMatch: true,
686 - shouldRedirect: false,
687 - },
688 - {
689 - title: "Redirect path with slash",
690 - route: r.NewRoute().Path("/111"),
691 - request: newRequest("GET", "http://localhost/111/"),
692 - vars: map[string]string{},
693 - host: "",
694 - path: "/111",
695 - shouldMatch: true,
696 - shouldRedirect: true,
697 - },
698 - {
699 - title: "Do not redirect path without slash",
700 - route: r.NewRoute().Path("/111"),
701 - request: newRequest("GET", "http://localhost/111"),
702 - vars: map[string]string{},
703 - host: "",
704 - path: "/111",
705 - shouldMatch: true,
706 - shouldRedirect: false,
707 - },
708 - {
709 - title: "Propagate StrictSlash to subrouters",
710 - route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
711 - request: newRequest("GET", "http://localhost/static/images"),
712 - vars: map[string]string{},
713 - host: "",
714 - path: "/static/images/",
715 - shouldMatch: true,
716 - shouldRedirect: true,
717 - },
718 - {
719 - title: "Ignore StrictSlash for path prefix",
720 - route: r.NewRoute().PathPrefix("/static/"),
721 - request: newRequest("GET", "http://localhost/static/logo.png"),
722 - vars: map[string]string{},
723 - host: "",
724 - path: "/static/",
725 - shouldMatch: true,
726 - shouldRedirect: false,
727 - },
728 - }
729 -
730 - for _, test := range tests {
731 - testRoute(t, test)
732 - }
733 -}
734 -
735 -// ----------------------------------------------------------------------------
736 -// Helpers
737 -// ----------------------------------------------------------------------------
738 -
739 -func getRouteTemplate(route *Route) string {
740 - host, path := "none", "none"
741 - if route.regexp != nil {
742 - if route.regexp.host != nil {
743 - host = route.regexp.host.template
744 - }
745 - if route.regexp.path != nil {
746 - path = route.regexp.path.template
747 - }
748 - }
749 - return fmt.Sprintf("Host: %v, Path: %v", host, path)
750 -}
751 -
752 -func testRoute(t *testing.T, test routeTest) {
753 - request := test.request
754 - route := test.route
755 - vars := test.vars
756 - shouldMatch := test.shouldMatch
757 - host := test.host
758 - path := test.path
759 - url := test.host + test.path
760 - shouldRedirect := test.shouldRedirect
761 -
762 - var match RouteMatch
763 - ok := route.Match(request, &match)
764 - if ok != shouldMatch {
765 - msg := "Should match"
766 - if !shouldMatch {
767 - msg = "Should not match"
768 - }
769 - t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
770 - return
771 - }
772 - if shouldMatch {
773 - if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
774 - t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
775 - return
776 - }
777 - if host != "" {
778 - u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
779 - if host != u.Host {
780 - t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
781 - return
782 - }
783 - }
784 - if path != "" {
785 - u, _ := route.URLPath(mapToPairs(match.Vars)...)
786 - if path != u.Path {
787 - t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
788 - return
789 - }
790 - }
791 - if url != "" {
792 - u, _ := route.URL(mapToPairs(match.Vars)...)
793 - if url != u.Host+u.Path {
794 - t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
795 - return
796 - }
797 - }
798 - if shouldRedirect && match.Handler == nil {
799 - t.Errorf("(%v) Did not redirect", test.title)
800 - return
801 - }
802 - if !shouldRedirect && match.Handler != nil {
803 - t.Errorf("(%v) Unexpected redirect", test.title)
804 - return
805 - }
806 - }
807 -}
808 -
809 -// Tests that the context is cleared or not cleared properly depending on
810 -// the configuration of the router
811 -func TestKeepContext(t *testing.T) {
812 - func1 := func(w http.ResponseWriter, r *http.Request) {}
813 -
814 - r := NewRouter()
815 - r.HandleFunc("/", func1).Name("func1")
816 -
817 - req, _ := http.NewRequest("GET", "http://localhost/", nil)
818 - context.Set(req, "t", 1)
819 -
820 - res := new(http.ResponseWriter)
821 - r.ServeHTTP(*res, req)
822 -
823 - if _, ok := context.GetOk(req, "t"); ok {
824 - t.Error("Context should have been cleared at end of request")
825 - }
826 -
827 - r.KeepContext = true
828 -
829 - req, _ = http.NewRequest("GET", "http://localhost/", nil)
830 - context.Set(req, "t", 1)
831 -
832 - r.ServeHTTP(*res, req)
833 - if _, ok := context.GetOk(req, "t"); !ok {
834 - t.Error("Context should NOT have been cleared at end of request")
835 - }
836 -
837 -}
838 -
839 -type TestA301ResponseWriter struct {
840 - hh http.Header
841 - status int
842 -}
843 -
844 -func (ho TestA301ResponseWriter) Header() http.Header {
845 - return http.Header(ho.hh)
846 -}
847 -
848 -func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
849 - return 0, nil
850 -}
851 -
852 -func (ho TestA301ResponseWriter) WriteHeader(code int) {
853 - ho.status = code
854 -}
855 -
856 -func Test301Redirect(t *testing.T) {
857 - m := make(http.Header)
858 -
859 - func1 := func(w http.ResponseWriter, r *http.Request) {}
860 - func2 := func(w http.ResponseWriter, r *http.Request) {}
861 -
862 - r := NewRouter()
863 - r.HandleFunc("/api/", func2).Name("func2")
864 - r.HandleFunc("/", func1).Name("func1")
865 -
866 - req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
867 -
868 - res := TestA301ResponseWriter{
869 - hh: m,
870 - status: 0,
871 - }
872 - r.ServeHTTP(&res, req)
873 -
874 - if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
875 - t.Errorf("Should have complete URL with query string")
876 - }
877 -}
878 -
879 -// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
880 -func TestSubrouterHeader(t *testing.T) {
881 - expected := "func1 response"
882 - func1 := func(w http.ResponseWriter, r *http.Request) {
883 - fmt.Fprint(w, expected)
884 - }
885 - func2 := func(http.ResponseWriter, *http.Request) {}
886 -
887 - r := NewRouter()
888 - s := r.Headers("SomeSpecialHeader", "").Subrouter()
889 - s.HandleFunc("/", func1).Name("func1")
890 - r.HandleFunc("/", func2).Name("func2")
891 -
892 - req, _ := http.NewRequest("GET", "http://localhost/", nil)
893 - req.Header.Add("SomeSpecialHeader", "foo")
894 - match := new(RouteMatch)
895 - matched := r.Match(req, match)
896 - if !matched {
897 - t.Errorf("Should match request")
898 - }
899 - if match.Route.GetName() != "func1" {
900 - t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
901 - }
902 - resp := NewRecorder()
903 - match.Handler.ServeHTTP(resp, req)
904 - if resp.Body.String() != expected {
905 - t.Errorf("Expecting %q", expected)
906 - }
907 -}
908 -
909 -// mapToPairs converts a string map to a slice of string pairs
910 -func mapToPairs(m map[string]string) []string {
911 - var i int
912 - p := make([]string, len(m)*2)
913 - for k, v := range m {
914 - p[i] = k
915 - p[i+1] = v
916 - i += 2
917 - }
918 - return p
919 -}
920 -
921 -// stringMapEqual checks the equality of two string maps
922 -func stringMapEqual(m1, m2 map[string]string) bool {
923 - nil1 := m1 == nil
924 - nil2 := m2 == nil
925 - if nil1 != nil2 || len(m1) != len(m2) {
926 - return false
927 - }
928 - for k, v := range m1 {
929 - if v != m2[k] {
930 - return false
931 - }
932 - }
933 - return true
934 -}
935 -
936 -// newRequest is a helper function to create a new request with a method and url
937 -func newRequest(method, url string) *http.Request {
938 - req, err := http.NewRequest(method, url, nil)
939 - if err != nil {
940 - panic(err)
941 - }
942 - return req
943 -}
Godeps/_workspace/src/github.com/gorilla/mux/old_test.go deleted
-714
@@ -1,714 +0,0 @@
1 -// Old tests ported to Go1. This is a mess. Want to drop it one day.
2 -
3 -// Copyright 2011 Gorilla Authors. All rights reserved.
4 -// Use of this source code is governed by a BSD-style
5 -// license that can be found in the LICENSE file.
6 -
7 -package mux
8 -
9 -import (
10 - "bytes"
11 - "net/http"
12 - "testing"
13 -)
14 -
15 -// ----------------------------------------------------------------------------
16 -// ResponseRecorder
17 -// ----------------------------------------------------------------------------
18 -// Copyright 2009 The Go Authors. All rights reserved.
19 -// Use of this source code is governed by a BSD-style
20 -// license that can be found in the LICENSE file.
21 -
22 -// ResponseRecorder is an implementation of http.ResponseWriter that
23 -// records its mutations for later inspection in tests.
24 -type ResponseRecorder struct {
25 - Code int // the HTTP response code from WriteHeader
26 - HeaderMap http.Header // the HTTP response headers
27 - Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
28 - Flushed bool
29 -}
30 -
31 -// NewRecorder returns an initialized ResponseRecorder.
32 -func NewRecorder() *ResponseRecorder {
33 - return &ResponseRecorder{
34 - HeaderMap: make(http.Header),
35 - Body: new(bytes.Buffer),
36 - }
37 -}
38 -
39 -// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
40 -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
41 -const DefaultRemoteAddr = "1.2.3.4"
42 -
43 -// Header returns the response headers.
44 -func (rw *ResponseRecorder) Header() http.Header {
45 - return rw.HeaderMap
46 -}
47 -
48 -// Write always succeeds and writes to rw.Body, if not nil.
49 -func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
50 - if rw.Body != nil {
51 - rw.Body.Write(buf)
52 - }
53 - if rw.Code == 0 {
54 - rw.Code = http.StatusOK
55 - }
56 - return len(buf), nil
57 -}
58 -
59 -// WriteHeader sets rw.Code.
60 -func (rw *ResponseRecorder) WriteHeader(code int) {
61 - rw.Code = code
62 -}
63 -
64 -// Flush sets rw.Flushed to true.
65 -func (rw *ResponseRecorder) Flush() {
66 - rw.Flushed = true
67 -}
68 -
69 -// ----------------------------------------------------------------------------
70 -
71 -func TestRouteMatchers(t *testing.T) {
72 - var scheme, host, path, query, method string
73 - var headers map[string]string
74 - var resultVars map[bool]map[string]string
75 -
76 - router := NewRouter()
77 - router.NewRoute().Host("{var1}.google.com").
78 - Path("/{var2:[a-z]+}/{var3:[0-9]+}").
79 - Queries("foo", "bar").
80 - Methods("GET").
81 - Schemes("https").
82 - Headers("x-requested-with", "XMLHttpRequest")
83 - router.NewRoute().Host("www.{var4}.com").
84 - PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
85 - Queries("baz", "ding").
86 - Methods("POST").
87 - Schemes("http").
88 - Headers("Content-Type", "application/json")
89 -
90 - reset := func() {
91 - // Everything match.
92 - scheme = "https"
93 - host = "www.google.com"
94 - path = "/product/42"
95 - query = "?foo=bar"
96 - method = "GET"
97 - headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
98 - resultVars = map[bool]map[string]string{
99 - true: {"var1": "www", "var2": "product", "var3": "42"},
100 - false: {},
101 - }
102 - }
103 -
104 - reset2 := func() {
105 - // Everything match.
106 - scheme = "http"
107 - host = "www.google.com"
108 - path = "/foo/product/42/path/that/is/ignored"
109 - query = "?baz=ding"
110 - method = "POST"
111 - headers = map[string]string{"Content-Type": "application/json"}
112 - resultVars = map[bool]map[string]string{
113 - true: {"var4": "google", "var5": "product", "var6": "42"},
114 - false: {},
115 - }
116 - }
117 -
118 - match := func(shouldMatch bool) {
119 - url := scheme + "://" + host + path + query
120 - request, _ := http.NewRequest(method, url, nil)
121 - for key, value := range headers {
122 - request.Header.Add(key, value)
123 - }
124 -
125 - var routeMatch RouteMatch
126 - matched := router.Match(request, &routeMatch)
127 - if matched != shouldMatch {
128 - // Need better messages. :)
129 - if matched {
130 - t.Errorf("Should match.")
131 - } else {
132 - t.Errorf("Should not match.")
133 - }
134 - }
135 -
136 - if matched {
137 - currentRoute := routeMatch.Route
138 - if currentRoute == nil {
139 - t.Errorf("Expected a current route.")
140 - }
141 - vars := routeMatch.Vars
142 - expectedVars := resultVars[shouldMatch]
143 - if len(vars) != len(expectedVars) {
144 - t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
145 - }
146 - for name, value := range vars {
147 - if expectedVars[name] != value {
148 - t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
149 - }
150 - }
151 - }
152 - }
153 -
154 - // 1st route --------------------------------------------------------------
155 -
156 - // Everything match.
157 - reset()
158 - match(true)
159 -
160 - // Scheme doesn't match.
161 - reset()
162 - scheme = "http"
163 - match(false)
164 -
165 - // Host doesn't match.
166 - reset()
167 - host = "www.mygoogle.com"
168 - match(false)
169 -
170 - // Path doesn't match.
171 - reset()
172 - path = "/product/notdigits"
173 - match(false)
174 -
175 - // Query doesn't match.
176 - reset()
177 - query = "?foo=baz"
178 - match(false)
179 -
180 - // Method doesn't match.
181 - reset()
182 - method = "POST"
183 - match(false)
184 -
185 - // Header doesn't match.
186 - reset()
187 - headers = map[string]string{}
188 - match(false)
189 -
190 - // Everything match, again.
191 - reset()
192 - match(true)
193 -
194 - // 2nd route --------------------------------------------------------------
195 -
196 - // Everything match.
197 - reset2()
198 - match(true)
199 -
200 - // Scheme doesn't match.
201 - reset2()
202 - scheme = "https"
203 - match(false)
204 -
205 - // Host doesn't match.
206 - reset2()
207 - host = "sub.google.com"
208 - match(false)
209 -
210 - // Path doesn't match.
211 - reset2()
212 - path = "/bar/product/42"
213 - match(false)
214 -
215 - // Query doesn't match.
216 - reset2()
217 - query = "?foo=baz"
218 - match(false)
219 -
220 - // Method doesn't match.
221 - reset2()
222 - method = "GET"
223 - match(false)
224 -
225 - // Header doesn't match.
226 - reset2()
227 - headers = map[string]string{}
228 - match(false)
229 -
230 - // Everything match, again.
231 - reset2()
232 - match(true)
233 -}
234 -
235 -type headerMatcherTest struct {
236 - matcher headerMatcher
237 - headers map[string]string
238 - result bool
239 -}
240 -
241 -var headerMatcherTests = []headerMatcherTest{
242 - {
243 - matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
244 - headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
245 - result: true,
246 - },
247 - {
248 - matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
249 - headers: map[string]string{"X-Requested-With": "anything"},
250 - result: true,
251 - },
252 - {
253 - matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
254 - headers: map[string]string{},
255 - result: false,
256 - },
257 -}
258 -
259 -type hostMatcherTest struct {
260 - matcher *Route
261 - url string
262 - vars map[string]string
263 - result bool
264 -}
265 -
266 -var hostMatcherTests = []hostMatcherTest{
267 - {
268 - matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
269 - url: "http://abc.def.ghi/",
270 - vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
271 - result: true,
272 - },
273 - {
274 - matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
275 - url: "http://a.b.c/",
276 - vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
277 - result: false,
278 - },
279 -}
280 -
281 -type methodMatcherTest struct {
282 - matcher methodMatcher
283 - method string
284 - result bool
285 -}
286 -
287 -var methodMatcherTests = []methodMatcherTest{
288 - {
289 - matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
290 - method: "GET",
291 - result: true,
292 - },
293 - {
294 - matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
295 - method: "POST",
296 - result: true,
297 - },
298 - {
299 - matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
300 - method: "PUT",
301 - result: true,
302 - },
303 - {
304 - matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
305 - method: "DELETE",
306 - result: false,
307 - },
308 -}
309 -
310 -type pathMatcherTest struct {
311 - matcher *Route
312 - url string
313 - vars map[string]string
314 - result bool
315 -}
316 -
317 -var pathMatcherTests = []pathMatcherTest{
318 - {
319 - matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
320 - url: "http://localhost:8080/123/456/789",
321 - vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
322 - result: true,
323 - },
324 - {
325 - matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
326 - url: "http://localhost:8080/1/2/3",
327 - vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
328 - result: false,
329 - },
330 -}
331 -
332 -type schemeMatcherTest struct {
333 - matcher schemeMatcher
334 - url string
335 - result bool
336 -}
337 -
338 -var schemeMatcherTests = []schemeMatcherTest{
339 - {
340 - matcher: schemeMatcher([]string{"http", "https"}),
341 - url: "http://localhost:8080/",
342 - result: true,
343 - },
344 - {
345 - matcher: schemeMatcher([]string{"http", "https"}),
346 - url: "https://localhost:8080/",
347 - result: true,
348 - },
349 - {
350 - matcher: schemeMatcher([]string{"https"}),
351 - url: "http://localhost:8080/",
352 - result: false,
353 - },
354 - {
355 - matcher: schemeMatcher([]string{"http"}),
356 - url: "https://localhost:8080/",
357 - result: false,
358 - },
359 -}
360 -
361 -type urlBuildingTest struct {
362 - route *Route
363 - vars []string
364 - url string
365 -}
366 -
367 -var urlBuildingTests = []urlBuildingTest{
368 - {
369 - route: new(Route).Host("foo.domain.com"),
370 - vars: []string{},
371 - url: "http://foo.domain.com",
372 - },
373 - {
374 - route: new(Route).Host("{subdomain}.domain.com"),
375 - vars: []string{"subdomain", "bar"},
376 - url: "http://bar.domain.com",
377 - },
378 - {
379 - route: new(Route).Host("foo.domain.com").Path("/articles"),
380 - vars: []string{},
381 - url: "http://foo.domain.com/articles",
382 - },
383 - {
384 - route: new(Route).Path("/articles"),
385 - vars: []string{},
386 - url: "/articles",
387 - },
388 - {
389 - route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
390 - vars: []string{"category", "technology", "id", "42"},
391 - url: "/articles/technology/42",
392 - },
393 - {
394 - route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
395 - vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
396 - url: "http://foo.domain.com/articles/technology/42",
397 - },
398 -}
399 -
400 -func TestHeaderMatcher(t *testing.T) {
401 - for _, v := range headerMatcherTests {
402 - request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
403 - for key, value := range v.headers {
404 - request.Header.Add(key, value)
405 - }
406 - var routeMatch RouteMatch
407 - result := v.matcher.Match(request, &routeMatch)
408 - if result != v.result {
409 - if v.result {
410 - t.Errorf("%#v: should match %v.", v.matcher, request.Header)
411 - } else {
412 - t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
413 - }
414 - }
415 - }
416 -}
417 -
418 -func TestHostMatcher(t *testing.T) {
419 - for _, v := range hostMatcherTests {
420 - request, _ := http.NewRequest("GET", v.url, nil)
421 - var routeMatch RouteMatch
422 - result := v.matcher.Match(request, &routeMatch)
423 - vars := routeMatch.Vars
424 - if result != v.result {
425 - if v.result {
426 - t.Errorf("%#v: should match %v.", v.matcher, v.url)
427 - } else {
428 - t.Errorf("%#v: should not match %v.", v.matcher, v.url)
429 - }
430 - }
431 - if result {
432 - if len(vars) != len(v.vars) {
433 - t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
434 - }
435 - for name, value := range vars {
436 - if v.vars[name] != value {
437 - t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
438 - }
439 - }
440 - } else {
441 - if len(vars) != 0 {
442 - t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
443 - }
444 - }
445 - }
446 -}
447 -
448 -func TestMethodMatcher(t *testing.T) {
449 - for _, v := range methodMatcherTests {
450 - request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
451 - var routeMatch RouteMatch
452 - result := v.matcher.Match(request, &routeMatch)
453 - if result != v.result {
454 - if v.result {
455 - t.Errorf("%#v: should match %v.", v.matcher, v.method)
456 - } else {
457 - t.Errorf("%#v: should not match %v.", v.matcher, v.method)
458 - }
459 - }
460 - }
461 -}
462 -
463 -func TestPathMatcher(t *testing.T) {
464 - for _, v := range pathMatcherTests {
465 - request, _ := http.NewRequest("GET", v.url, nil)
466 - var routeMatch RouteMatch
467 - result := v.matcher.Match(request, &routeMatch)
468 - vars := routeMatch.Vars
469 - if result != v.result {
470 - if v.result {
471 - t.Errorf("%#v: should match %v.", v.matcher, v.url)
472 - } else {
473 - t.Errorf("%#v: should not match %v.", v.matcher, v.url)
474 - }
475 - }
476 - if result {
477 - if len(vars) != len(v.vars) {
478 - t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
479 - }
480 - for name, value := range vars {
481 - if v.vars[name] != value {
482 - t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
483 - }
484 - }
485 - } else {
486 - if len(vars) != 0 {
487 - t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
488 - }
489 - }
490 - }
491 -}
492 -
493 -func TestSchemeMatcher(t *testing.T) {
494 - for _, v := range schemeMatcherTests {
495 - request, _ := http.NewRequest("GET", v.url, nil)
496 - var routeMatch RouteMatch
497 - result := v.matcher.Match(request, &routeMatch)
498 - if result != v.result {
499 - if v.result {
500 - t.Errorf("%#v: should match %v.", v.matcher, v.url)
501 - } else {
502 - t.Errorf("%#v: should not match %v.", v.matcher, v.url)
503 - }
504 - }
505 - }
506 -}
507 -
508 -func TestUrlBuilding(t *testing.T) {
509 -
510 - for _, v := range urlBuildingTests {
511 - u, _ := v.route.URL(v.vars...)
512 - url := u.String()
513 - if url != v.url {
514 - t.Errorf("expected %v, got %v", v.url, url)
515 - /*
516 - reversePath := ""
517 - reverseHost := ""
518 - if v.route.pathTemplate != nil {
519 - reversePath = v.route.pathTemplate.Reverse
520 - }
521 - if v.route.hostTemplate != nil {
522 - reverseHost = v.route.hostTemplate.Reverse
523 - }
524 -
525 - t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
526 - */
527 - }
528 - }
529 -
530 - ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
531 - }
532 -
533 - router := NewRouter()
534 - router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
535 -
536 - url, _ := router.Get("article").URL("category", "technology", "id", "42")
537 - expected := "/articles/technology/42"
538 - if url.String() != expected {
539 - t.Errorf("Expected %v, got %v", expected, url.String())
540 - }
541 -}
542 -
543 -func TestMatchedRouteName(t *testing.T) {
544 - routeName := "stock"
545 - router := NewRouter()
546 - route := router.NewRoute().Path("/products/").Name(routeName)
547 -
548 - url := "http://www.domain.com/products/"
549 - request, _ := http.NewRequest("GET", url, nil)
550 - var rv RouteMatch
551 - ok := router.Match(request, &rv)
552 -
553 - if !ok || rv.Route != route {
554 - t.Errorf("Expected same route, got %+v.", rv.Route)
555 - }
556 -
557 - retName := rv.Route.GetName()
558 - if retName != routeName {
559 - t.Errorf("Expected %q, got %q.", routeName, retName)
560 - }
561 -}
562 -
563 -func TestSubRouting(t *testing.T) {
564 - // Example from docs.
565 - router := NewRouter()
566 - subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
567 - route := subrouter.NewRoute().Path("/products/").Name("products")
568 -
569 - url := "http://www.domain.com/products/"
570 - request, _ := http.NewRequest("GET", url, nil)
571 - var rv RouteMatch
572 - ok := router.Match(request, &rv)
573 -
574 - if !ok || rv.Route != route {
575 - t.Errorf("Expected same route, got %+v.", rv.Route)
576 - }
577 -
578 - u, _ := router.Get("products").URL()
579 - builtUrl := u.String()
580 - // Yay, subroute aware of the domain when building!
581 - if builtUrl != url {
582 - t.Errorf("Expected %q, got %q.", url, builtUrl)
583 - }
584 -}
585 -
586 -func TestVariableNames(t *testing.T) {
587 - route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
588 - if route.err == nil {
589 - t.Errorf("Expected error for duplicated variable names")
590 - }
591 -}
592 -
593 -func TestRedirectSlash(t *testing.T) {
594 - var route *Route
595 - var routeMatch RouteMatch
596 - r := NewRouter()
597 -
598 - r.StrictSlash(false)
599 - route = r.NewRoute()
600 - if route.strictSlash != false {
601 - t.Errorf("Expected false redirectSlash.")
602 - }
603 -
604 - r.StrictSlash(true)
605 - route = r.NewRoute()
606 - if route.strictSlash != true {
607 - t.Errorf("Expected true redirectSlash.")
608 - }
609 -
610 - route = new(Route)
611 - route.strictSlash = true
612 - route.Path("/{arg1}/{arg2:[0-9]+}/")
613 - request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
614 - routeMatch = RouteMatch{}
615 - _ = route.Match(request, &routeMatch)
616 - vars := routeMatch.Vars
617 - if vars["arg1"] != "foo" {
618 - t.Errorf("Expected foo.")
619 - }
620 - if vars["arg2"] != "123" {
621 - t.Errorf("Expected 123.")
622 - }
623 - rsp := NewRecorder()
624 - routeMatch.Handler.ServeHTTP(rsp, request)
625 - if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
626 - t.Errorf("Expected redirect header.")
627 - }
628 -
629 - route = new(Route)
630 - route.strictSlash = true
631 - route.Path("/{arg1}/{arg2:[0-9]+}")
632 - request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
633 - routeMatch = RouteMatch{}
634 - _ = route.Match(request, &routeMatch)
635 - vars = routeMatch.Vars
636 - if vars["arg1"] != "foo" {
637 - t.Errorf("Expected foo.")
638 - }
639 - if vars["arg2"] != "123" {
640 - t.Errorf("Expected 123.")
641 - }
642 - rsp = NewRecorder()
643 - routeMatch.Handler.ServeHTTP(rsp, request)
644 - if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
645 - t.Errorf("Expected redirect header.")
646 - }
647 -}
648 -
649 -// Test for the new regexp library, still not available in stable Go.
650 -func TestNewRegexp(t *testing.T) {
651 - var p *routeRegexp
652 - var matches []string
653 -
654 - tests := map[string]map[string][]string{
655 - "/{foo:a{2}}": {
656 - "/a": nil,
657 - "/aa": {"aa"},
658 - "/aaa": nil,
659 - "/aaaa": nil,
660 - },
661 - "/{foo:a{2,}}": {
662 - "/a": nil,
663 - "/aa": {"aa"},
664 - "/aaa": {"aaa"},
665 - "/aaaa": {"aaaa"},
666 - },
667 - "/{foo:a{2,3}}": {
668 - "/a": nil,
669 - "/aa": {"aa"},
670 - "/aaa": {"aaa"},
671 - "/aaaa": nil,
672 - },
673 - "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
674 - "/a": nil,
675 - "/ab": nil,
676 - "/abc": nil,
677 - "/abcd": nil,
678 - "/abc/ab": {"abc", "ab"},
679 - "/abc/abc": nil,
680 - "/abcd/ab": nil,
681 - },
682 - `/{foo:\w{3,}}/{bar:\d{2,}}`: {
683 - "/a": nil,
684 - "/ab": nil,
685 - "/abc": nil,
686 - "/abc/1": nil,
687 - "/abc/12": {"abc", "12"},
688 - "/abcd/12": {"abcd", "12"},
689 - "/abcd/123": {"abcd", "123"},
690 - },
691 - }
692 -
693 - for pattern, paths := range tests {
694 - p, _ = newRouteRegexp(pattern, false, false, false, false)
695 - for path, result := range paths {
696 - matches = p.regexp.FindStringSubmatch(path)
697 - if result == nil {
698 - if matches != nil {
699 - t.Errorf("%v should not match %v.", pattern, path)
700 - }
701 - } else {
702 - if len(matches) != len(result)+1 {
703 - t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
704 - } else {
705 - for k, v := range result {
706 - if matches[k+1] != v {
707 - t.Errorf("Expected %v, got %v.", v, matches[k+1])
708 - }
709 - }
710 - }
711 - }
712 - }
713 - }
714 -}
Godeps/_workspace/src/github.com/gorilla/mux/regexp.go deleted
-274
@@ -1,274 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package mux
6 -
7 -import (
8 - "bytes"
9 - "fmt"
10 - "net/http"
11 - "net/url"
12 - "regexp"
13 - "strings"
14 -)
15 -
16 -// newRouteRegexp parses a route template and returns a routeRegexp,
17 -// used to match a host, a path or a query string.
18 -//
19 -// It will extract named variables, assemble a regexp to be matched, create
20 -// a "reverse" template to build URLs and compile regexps to validate variable
21 -// values used in URL building.
22 -//
23 -// Previously we accepted only Python-like identifiers for variable
24 -// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
25 -// name and pattern can't be empty, and names can't contain a colon.
26 -func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
27 - // Check if it is well-formed.
28 - idxs, errBraces := braceIndices(tpl)
29 - if errBraces != nil {
30 - return nil, errBraces
31 - }
32 - // Backup the original.
33 - template := tpl
34 - // Now let's parse it.
35 - defaultPattern := "[^/]+"
36 - if matchQuery {
37 - defaultPattern = "[^?&]+"
38 - matchPrefix, strictSlash = true, false
39 - } else if matchHost {
40 - defaultPattern = "[^.]+"
41 - matchPrefix, strictSlash = false, false
42 - }
43 - if matchPrefix {
44 - strictSlash = false
45 - }
46 - // Set a flag for strictSlash.
47 - endSlash := false
48 - if strictSlash && strings.HasSuffix(tpl, "/") {
49 - tpl = tpl[:len(tpl)-1]
50 - endSlash = true
51 - }
52 - varsN := make([]string, len(idxs)/2)
53 - varsR := make([]*regexp.Regexp, len(idxs)/2)
54 - pattern := bytes.NewBufferString("")
55 - if !matchQuery {
56 - pattern.WriteByte('^')
57 - }
58 - reverse := bytes.NewBufferString("")
59 - var end int
60 - var err error
61 - for i := 0; i < len(idxs); i += 2 {
62 - // Set all values we are interested in.
63 - raw := tpl[end:idxs[i]]
64 - end = idxs[i+1]
65 - parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
66 - name := parts[0]
67 - patt := defaultPattern
68 - if len(parts) == 2 {
69 - patt = parts[1]
70 - }
71 - // Name or pattern can't be empty.
72 - if name == "" || patt == "" {
73 - return nil, fmt.Errorf("mux: missing name or pattern in %q",
74 - tpl[idxs[i]:end])
75 - }
76 - // Build the regexp pattern.
77 - fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
78 - // Build the reverse template.
79 - fmt.Fprintf(reverse, "%s%%s", raw)
80 - // Append variable name and compiled pattern.
81 - varsN[i/2] = name
82 - varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
83 - if err != nil {
84 - return nil, err
85 - }
86 - }
87 - // Add the remaining.
88 - raw := tpl[end:]
89 - pattern.WriteString(regexp.QuoteMeta(raw))
90 - if strictSlash {
91 - pattern.WriteString("[/]?")
92 - }
93 - if !matchPrefix {
94 - pattern.WriteByte('$')
95 - }
96 - reverse.WriteString(raw)
97 - if endSlash {
98 - reverse.WriteByte('/')
99 - }
100 - // Compile full regexp.
101 - reg, errCompile := regexp.Compile(pattern.String())
102 - if errCompile != nil {
103 - return nil, errCompile
104 - }
105 - // Done!
106 - return &routeRegexp{
107 - template: template,
108 - matchHost: matchHost,
109 - matchQuery: matchQuery,
110 - strictSlash: strictSlash,
111 - regexp: reg,
112 - reverse: reverse.String(),
113 - varsN: varsN,
114 - varsR: varsR,
115 - }, nil
116 -}
117 -
118 -// routeRegexp stores a regexp to match a host or path and information to
119 -// collect and validate route variables.
120 -type routeRegexp struct {
121 - // The unmodified template.
122 - template string
123 - // True for host match, false for path or query string match.
124 - matchHost bool
125 - // True for query string match, false for path and host match.
126 - matchQuery bool
127 - // The strictSlash value defined on the route, but disabled if PathPrefix was used.
128 - strictSlash bool
129 - // Expanded regexp.
130 - regexp *regexp.Regexp
131 - // Reverse template.
132 - reverse string
133 - // Variable names.
134 - varsN []string
135 - // Variable regexps (validators).
136 - varsR []*regexp.Regexp
137 -}
138 -
139 -// Match matches the regexp against the URL host or path.
140 -func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
141 - if !r.matchHost {
142 - if r.matchQuery {
143 - return r.regexp.MatchString(req.URL.RawQuery)
144 - } else {
145 - return r.regexp.MatchString(req.URL.Path)
146 - }
147 - }
148 - return r.regexp.MatchString(getHost(req))
149 -}
150 -
151 -// url builds a URL part using the given values.
152 -func (r *routeRegexp) url(pairs ...string) (string, error) {
153 - values, err := mapFromPairs(pairs...)
154 - if err != nil {
155 - return "", err
156 - }
157 - urlValues := make([]interface{}, len(r.varsN))
158 - for k, v := range r.varsN {
159 - value, ok := values[v]
160 - if !ok {
161 - return "", fmt.Errorf("mux: missing route variable %q", v)
162 - }
163 - urlValues[k] = value
164 - }
165 - rv := fmt.Sprintf(r.reverse, urlValues...)
166 - if !r.regexp.MatchString(rv) {
167 - // The URL is checked against the full regexp, instead of checking
168 - // individual variables. This is faster but to provide a good error
169 - // message, we check individual regexps if the URL doesn't match.
170 - for k, v := range r.varsN {
171 - if !r.varsR[k].MatchString(values[v]) {
172 - return "", fmt.Errorf(
173 - "mux: variable %q doesn't match, expected %q", values[v],
174 - r.varsR[k].String())
175 - }
176 - }
177 - }
178 - return rv, nil
179 -}
180 -
181 -// braceIndices returns the first level curly brace indices from a string.
182 -// It returns an error in case of unbalanced braces.
183 -func braceIndices(s string) ([]int, error) {
184 - var level, idx int
185 - idxs := make([]int, 0)
186 - for i := 0; i < len(s); i++ {
187 - switch s[i] {
188 - case '{':
189 - if level++; level == 1 {
190 - idx = i
191 - }
192 - case '}':
193 - if level--; level == 0 {
194 - idxs = append(idxs, idx, i+1)
195 - } else if level < 0 {
196 - return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
197 - }
198 - }
199 - }
200 - if level != 0 {
201 - return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
202 - }
203 - return idxs, nil
204 -}
205 -
206 -// ----------------------------------------------------------------------------
207 -// routeRegexpGroup
208 -// ----------------------------------------------------------------------------
209 -
210 -// routeRegexpGroup groups the route matchers that carry variables.
211 -type routeRegexpGroup struct {
212 - host *routeRegexp
213 - path *routeRegexp
214 - queries []*routeRegexp
215 -}
216 -
217 -// setMatch extracts the variables from the URL once a route matches.
218 -func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
219 - // Store host variables.
220 - if v.host != nil {
221 - hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
222 - if hostVars != nil {
223 - for k, v := range v.host.varsN {
224 - m.Vars[v] = hostVars[k+1]
225 - }
226 - }
227 - }
228 - // Store path variables.
229 - if v.path != nil {
230 - pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
231 - if pathVars != nil {
232 - for k, v := range v.path.varsN {
233 - m.Vars[v] = pathVars[k+1]
234 - }
235 - // Check if we should redirect.
236 - if v.path.strictSlash {
237 - p1 := strings.HasSuffix(req.URL.Path, "/")
238 - p2 := strings.HasSuffix(v.path.template, "/")
239 - if p1 != p2 {
240 - u, _ := url.Parse(req.URL.String())
241 - if p1 {
242 - u.Path = u.Path[:len(u.Path)-1]
243 - } else {
244 - u.Path += "/"
245 - }
246 - m.Handler = http.RedirectHandler(u.String(), 301)
247 - }
248 - }
249 - }
250 - }
251 - // Store query string variables.
252 - rawQuery := req.URL.RawQuery
253 - for _, q := range v.queries {
254 - queryVars := q.regexp.FindStringSubmatch(rawQuery)
255 - if queryVars != nil {
256 - for k, v := range q.varsN {
257 - m.Vars[v] = queryVars[k+1]
258 - }
259 - }
260 - }
261 -}
262 -
263 -// getHost tries its best to return the request host.
264 -func getHost(r *http.Request) string {
265 - if !r.URL.IsAbs() {
266 - host := r.Host
267 - // Slice off any port information.
268 - if i := strings.Index(host, ":"); i != -1 {
269 - host = host[:i]
270 - }
271 - return host
272 - }
273 - return r.URL.Host
274 -}
Godeps/_workspace/src/github.com/gorilla/mux/route.go deleted
-524
@@ -1,524 +0,0 @@
1 -// Copyright 2012 The Gorilla Authors. All rights reserved.
2 -// Use of this source code is governed by a BSD-style
3 -// license that can be found in the LICENSE file.
4 -
5 -package mux
6 -
7 -import (
8 - "errors"
9 - "fmt"
10 - "net/http"
11 - "net/url"
12 - "strings"
13 -)
14 -
15 -// Route stores information to match a request and build URLs.
16 -type Route struct {
17 - // Parent where the route was registered (a Router).
18 - parent parentRoute
19 - // Request handler for the route.
20 - handler http.Handler
21 - // List of matchers.
22 - matchers []matcher
23 - // Manager for the variables from host and path.
24 - regexp *routeRegexpGroup
25 - // If true, when the path pattern is "/path/", accessing "/path" will
26 - // redirect to the former and vice versa.
27 - strictSlash bool
28 - // If true, this route never matches: it is only used to build URLs.
29 - buildOnly bool
30 - // The name used to build URLs.
31 - name string
32 - // Error resulted from building a route.
33 - err error
34 -}
35 -
36 -// Match matches the route against the request.
37 -func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
38 - if r.buildOnly || r.err != nil {
39 - return false
40 - }
41 - // Match everything.
42 - for _, m := range r.matchers {
43 - if matched := m.Match(req, match); !matched {
44 - return false
45 - }
46 - }
47 - // Yay, we have a match. Let's collect some info about it.
48 - if match.Route == nil {
49 - match.Route = r
50 - }
51 - if match.Handler == nil {
52 - match.Handler = r.handler
53 - }
54 - if match.Vars == nil {
55 - match.Vars = make(map[string]string)
56 - }
57 - // Set variables.
58 - if r.regexp != nil {
59 - r.regexp.setMatch(req, match, r)
60 - }
61 - return true
62 -}
63 -
64 -// ----------------------------------------------------------------------------
65 -// Route attributes
66 -// ----------------------------------------------------------------------------
67 -
68 -// GetError returns an error resulted from building the route, if any.
69 -func (r *Route) GetError() error {
70 - return r.err
71 -}
72 -
73 -// BuildOnly sets the route to never match: it is only used to build URLs.
74 -func (r *Route) BuildOnly() *Route {
75 - r.buildOnly = true
76 - return r
77 -}
78 -
79 -// Handler --------------------------------------------------------------------
80 -
81 -// Handler sets a handler for the route.
82 -func (r *Route) Handler(handler http.Handler) *Route {
83 - if r.err == nil {
84 - r.handler = handler
85 - }
86 - return r
87 -}
88 -
89 -// HandlerFunc sets a handler function for the route.
90 -func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
91 - return r.Handler(http.HandlerFunc(f))
92 -}
93 -
94 -// GetHandler returns the handler for the route, if any.
95 -func (r *Route) GetHandler() http.Handler {
96 - return r.handler
97 -}
98 -
99 -// Name -----------------------------------------------------------------------
100 -
101 -// Name sets the name for the route, used to build URLs.
102 -// If the name was registered already it will be overwritten.
103 -func (r *Route) Name(name string) *Route {
104 - if r.name != "" {
105 - r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
106 - r.name, name)
107 - }
108 - if r.err == nil {
109 - r.name = name
110 - r.getNamedRoutes()[name] = r
111 - }
112 - return r
113 -}
114 -
115 -// GetName returns the name for the route, if any.
116 -func (r *Route) GetName() string {
117 - return r.name
118 -}
119 -
120 -// ----------------------------------------------------------------------------
121 -// Matchers
122 -// ----------------------------------------------------------------------------
123 -
124 -// matcher types try to match a request.
125 -type matcher interface {
126 - Match(*http.Request, *RouteMatch) bool
127 -}
128 -
129 -// addMatcher adds a matcher to the route.
130 -func (r *Route) addMatcher(m matcher) *Route {
131 - if r.err == nil {
132 - r.matchers = append(r.matchers, m)
133 - }
134 - return r
135 -}
136 -
137 -// addRegexpMatcher adds a host or path matcher and builder to a route.
138 -func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
139 - if r.err != nil {
140 - return r.err
141 - }
142 - r.regexp = r.getRegexpGroup()
143 - if !matchHost && !matchQuery {
144 - if len(tpl) == 0 || tpl[0] != '/' {
145 - return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
146 - }
147 - if r.regexp.path != nil {
148 - tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
149 - }
150 - }
151 - rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
152 - if err != nil {
153 - return err
154 - }
155 - for _, q := range r.regexp.queries {
156 - if err = uniqueVars(rr.varsN, q.varsN); err != nil {
157 - return err
158 - }
159 - }
160 - if matchHost {
161 - if r.regexp.path != nil {
162 - if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
163 - return err
164 - }
165 - }
166 - r.regexp.host = rr
167 - } else {
168 - if r.regexp.host != nil {
169 - if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
170 - return err
171 - }
172 - }
173 - if matchQuery {
174 - r.regexp.queries = append(r.regexp.queries, rr)
175 - } else {
176 - r.regexp.path = rr
177 - }
178 - }
179 - r.addMatcher(rr)
180 - return nil
181 -}
182 -
183 -// Headers --------------------------------------------------------------------
184 -
185 -// headerMatcher matches the request against header values.
186 -type headerMatcher map[string]string
187 -
188 -func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
189 - return matchMap(m, r.Header, true)
190 -}
191 -
192 -// Headers adds a matcher for request header values.
193 -// It accepts a sequence of key/value pairs to be matched. For example:
194 -//
195 -// r := mux.NewRouter()
196 -// r.Headers("Content-Type", "application/json",
197 -// "X-Requested-With", "XMLHttpRequest")
198 -//
199 -// The above route will only match if both request header values match.
200 -//
201 -// It the value is an empty string, it will match any value if the key is set.
202 -func (r *Route) Headers(pairs ...string) *Route {
203 - if r.err == nil {
204 - var headers map[string]string
205 - headers, r.err = mapFromPairs(pairs...)
206 - return r.addMatcher(headerMatcher(headers))
207 - }
208 - return r
209 -}
210 -
211 -// Host -----------------------------------------------------------------------
212 -
213 -// Host adds a matcher for the URL host.
214 -// It accepts a template with zero or more URL variables enclosed by {}.
215 -// Variables can define an optional regexp pattern to me matched:
216 -//
217 -// - {name} matches anything until the next dot.
218 -//
219 -// - {name:pattern} matches the given regexp pattern.
220 -//
221 -// For example:
222 -//
223 -// r := mux.NewRouter()
224 -// r.Host("www.domain.com")
225 -// r.Host("{subdomain}.domain.com")
226 -// r.Host("{subdomain:[a-z]+}.domain.com")
227 -//
228 -// Variable names must be unique in a given route. They can be retrieved
229 -// calling mux.Vars(request).
230 -func (r *Route) Host(tpl string) *Route {
231 - r.err = r.addRegexpMatcher(tpl, true, false, false)
232 - return r
233 -}
234 -
235 -// MatcherFunc ----------------------------------------------------------------
236 -
237 -// MatcherFunc is the function signature used by custom matchers.
238 -type MatcherFunc func(*http.Request, *RouteMatch) bool
239 -
240 -func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
241 - return m(r, match)
242 -}
243 -
244 -// MatcherFunc adds a custom function to be used as request matcher.
245 -func (r *Route) MatcherFunc(f MatcherFunc) *Route {
246 - return r.addMatcher(f)
247 -}
248 -
249 -// Methods --------------------------------------------------------------------
250 -
251 -// methodMatcher matches the request against HTTP methods.
252 -type methodMatcher []string
253 -
254 -func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
255 - return matchInArray(m, r.Method)
256 -}
257 -
258 -// Methods adds a matcher for HTTP methods.
259 -// It accepts a sequence of one or more methods to be matched, e.g.:
260 -// "GET", "POST", "PUT".
261 -func (r *Route) Methods(methods ...string) *Route {
262 - for k, v := range methods {
263 - methods[k] = strings.ToUpper(v)
264 - }
265 - return r.addMatcher(methodMatcher(methods))
266 -}
267 -
268 -// Path -----------------------------------------------------------------------
269 -
270 -// Path adds a matcher for the URL path.
271 -// It accepts a template with zero or more URL variables enclosed by {}. The
272 -// template must start with a "/".
273 -// Variables can define an optional regexp pattern to me matched:
274 -//
275 -// - {name} matches anything until the next slash.
276 -//
277 -// - {name:pattern} matches the given regexp pattern.
278 -//
279 -// For example:
280 -//
281 -// r := mux.NewRouter()
282 -// r.Path("/products/").Handler(ProductsHandler)
283 -// r.Path("/products/{key}").Handler(ProductsHandler)
284 -// r.Path("/articles/{category}/{id:[0-9]+}").
285 -// Handler(ArticleHandler)
286 -//
287 -// Variable names must be unique in a given route. They can be retrieved
288 -// calling mux.Vars(request).
289 -func (r *Route) Path(tpl string) *Route {
290 - r.err = r.addRegexpMatcher(tpl, false, false, false)
291 - return r
292 -}
293 -
294 -// PathPrefix -----------------------------------------------------------------
295 -
296 -// PathPrefix adds a matcher for the URL path prefix. This matches if the given
297 -// template is a prefix of the full URL path. See Route.Path() for details on
298 -// the tpl argument.
299 -//
300 -// Note that it does not treat slashes specially ("/foobar/" will be matched by
301 -// the prefix "/foo") so you may want to use a trailing slash here.
302 -//
303 -// Also note that the setting of Router.StrictSlash() has no effect on routes
304 -// with a PathPrefix matcher.
305 -func (r *Route) PathPrefix(tpl string) *Route {
306 - r.err = r.addRegexpMatcher(tpl, false, true, false)
307 - return r
308 -}
309 -
310 -// Query ----------------------------------------------------------------------
311 -
312 -// Queries adds a matcher for URL query values.
313 -// It accepts a sequence of key/value pairs. Values may define variables.
314 -// For example:
315 -//
316 -// r := mux.NewRouter()
317 -// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
318 -//
319 -// The above route will only match if the URL contains the defined queries
320 -// values, e.g.: ?foo=bar&id=42.
321 -//
322 -// It the value is an empty string, it will match any value if the key is set.
323 -//
324 -// Variables can define an optional regexp pattern to me matched:
325 -//
326 -// - {name} matches anything until the next slash.
327 -//
328 -// - {name:pattern} matches the given regexp pattern.
329 -func (r *Route) Queries(pairs ...string) *Route {
330 - length := len(pairs)
331 - if length%2 != 0 {
332 - r.err = fmt.Errorf(
333 - "mux: number of parameters must be multiple of 2, got %v", pairs)
334 - return nil
335 - }
336 - for i := 0; i < length; i += 2 {
337 - if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
338 - return r
339 - }
340 - }
341 -
342 - return r
343 -}
344 -
345 -// Schemes --------------------------------------------------------------------
346 -
347 -// schemeMatcher matches the request against URL schemes.
348 -type schemeMatcher []string
349 -
350 -func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
351 - return matchInArray(m, r.URL.Scheme)
352 -}
353 -
354 -// Schemes adds a matcher for URL schemes.
355 -// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
356 -func (r *Route) Schemes(schemes ...string) *Route {
357 - for k, v := range schemes {
358 - schemes[k] = strings.ToLower(v)
359 - }
360 - return r.addMatcher(schemeMatcher(schemes))
361 -}
362 -
363 -// Subrouter ------------------------------------------------------------------
364 -
365 -// Subrouter creates a subrouter for the route.
366 -//
367 -// It will test the inner routes only if the parent route matched. For example:
368 -//
369 -// r := mux.NewRouter()
370 -// s := r.Host("www.domain.com").Subrouter()
371 -// s.HandleFunc("/products/", ProductsHandler)
372 -// s.HandleFunc("/products/{key}", ProductHandler)
373 -// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
374 -//
375 -// Here, the routes registered in the subrouter won't be tested if the host
376 -// doesn't match.
377 -func (r *Route) Subrouter() *Router {
378 - router := &Router{parent: r, strictSlash: r.strictSlash}
379 - r.addMatcher(router)
380 - return router
381 -}
382 -
383 -// ----------------------------------------------------------------------------
384 -// URL building
385 -// ----------------------------------------------------------------------------
386 -
387 -// URL builds a URL for the route.
388 -//
389 -// It accepts a sequence of key/value pairs for the route variables. For
390 -// example, given this route:
391 -//
392 -// r := mux.NewRouter()
393 -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
394 -// Name("article")
395 -//
396 -// ...a URL for it can be built using:
397 -//
398 -// url, err := r.Get("article").URL("category", "technology", "id", "42")
399 -//
400 -// ...which will return an url.URL with the following path:
401 -//
402 -// "/articles/technology/42"
403 -//
404 -// This also works for host variables:
405 -//
406 -// r := mux.NewRouter()
407 -// r.Host("{subdomain}.domain.com").
408 -// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
409 -// Name("article")
410 -//
411 -// // url.String() will be "http://news.domain.com/articles/technology/42"
412 -// url, err := r.Get("article").URL("subdomain", "news",
413 -// "category", "technology",
414 -// "id", "42")
415 -//
416 -// All variables defined in the route are required, and their values must
417 -// conform to the corresponding patterns.
418 -func (r *Route) URL(pairs ...string) (*url.URL, error) {
419 - if r.err != nil {
420 - return nil, r.err
421 - }
422 - if r.regexp == nil {
423 - return nil, errors.New("mux: route doesn't have a host or path")
424 - }
425 - var scheme, host, path string
426 - var err error
427 - if r.regexp.host != nil {
428 - // Set a default scheme.
429 - scheme = "http"
430 - if host, err = r.regexp.host.url(pairs...); err != nil {
431 - return nil, err
432 - }
433 - }
434 - if r.regexp.path != nil {
435 - if path, err = r.regexp.path.url(pairs...); err != nil {
436 - return nil, err
437 - }
438 - }
439 - return &url.URL{
440 - Scheme: scheme,
441 - Host: host,
442 - Path: path,
443 - }, nil
444 -}
445 -
446 -// URLHost builds the host part of the URL for a route. See Route.URL().
447 -//
448 -// The route must have a host defined.
449 -func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
450 - if r.err != nil {
451 - return nil, r.err
452 - }
453 - if r.regexp == nil || r.regexp.host == nil {
454 - return nil, errors.New("mux: route doesn't have a host")
455 - }
456 - host, err := r.regexp.host.url(pairs...)
457 - if err != nil {
458 - return nil, err
459 - }
460 - return &url.URL{
461 - Scheme: "http",
462 - Host: host,
463 - }, nil
464 -}
465 -
466 -// URLPath builds the path part of the URL for a route. See Route.URL().
467 -//
468 -// The route must have a path defined.
469 -func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
470 - if r.err != nil {
471 - return nil, r.err
472 - }
473 - if r.regexp == nil || r.regexp.path == nil {
474 - return nil, errors.New("mux: route doesn't have a path")
475 - }
476 - path, err := r.regexp.path.url(pairs...)
477 - if err != nil {
478 - return nil, err
479 - }
480 - return &url.URL{
481 - Path: path,
482 - }, nil
483 -}
484 -
485 -// ----------------------------------------------------------------------------
486 -// parentRoute
487 -// ----------------------------------------------------------------------------
488 -
489 -// parentRoute allows routes to know about parent host and path definitions.
490 -type parentRoute interface {
491 - getNamedRoutes() map[string]*Route
492 - getRegexpGroup() *routeRegexpGroup
493 -}
494 -
495 -// getNamedRoutes returns the map where named routes are registered.
496 -func (r *Route) getNamedRoutes() map[string]*Route {
497 - if r.parent == nil {
498 - // During tests router is not always set.
499 - r.parent = NewRouter()
500 - }
501 - return r.parent.getNamedRoutes()
502 -}
503 -
504 -// getRegexpGroup returns regexp definitions from this route.
505 -func (r *Route) getRegexpGroup() *routeRegexpGroup {
506 - if r.regexp == nil {
507 - if r.parent == nil {
508 - // During tests router is not always set.
509 - r.parent = NewRouter()
510 - }
511 - regexp := r.parent.getRegexpGroup()
512 - if regexp == nil {
513 - r.regexp = new(routeRegexpGroup)
514 - } else {
515 - // Copy.
516 - r.regexp = &routeRegexpGroup{
517 - host: regexp.host,
518 - path: regexp.path,
519 - queries: regexp.queries,
520 - }
521 - }
522 - }
523 - return r.regexp
524 -}
Godeps/_workspace/src/github.com/jbenet/go-base58/base58_test.go
+34
@@ -94,3 +94,37 @@ func TestBase58(t *testing.T) {
94 }
95 }
96 }
97 +
98 +func BenchmarkDecodeShort(b *testing.B) {
99 + const in = "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"
100 + b.ReportAllocs()
101 + for i := 0; i < b.N; i++ {
102 + _ = Decode(in)
103 + }
104 +}
105 +
106 +func BenchmarkEncodeShort(b *testing.B) {
107 + var in = []byte("00eb15231dfceb60925886b67d065299925915aeb172c06647")
108 + b.ReportAllocs()
109 + for i := 0; i < b.N; i++ {
110 + _ = Encode(in)
111 + }
112 +}
113 +
114 +func BenchmarkDecodeOneKilo(b *testing.B) {
115 + const in = "3GimCffBLAHhXMCeNxX2nST6dBem9pbUi3KVKykW73LmewcFtMk9oh9eNPdNR2eSzNqp7Z3E21vrWUkGHzJ7w2yqDUDJ4LKo1w5D6aafZ4SUoNQyrSVxyVG3pwgoZkKXMZVixRyiPZVUpekrsTvZuUoW7mB6BQgDTXbDuMMSRoNR7yiUTKpgwTD61DLmhNZopNxfFjn4avpYPgzsTB94iWueq1yU3EoruWCUMvp6fc1CEbDrZY3pkx9oUbUaSMC37rruBKSSGHh1ZE3XK3kQXBCFraMmUQf8dagofMEg5aTnDiLAZjLyWJMdnQwW1FqKKztP8KAQS2JX8GCCfc68KB4VGf2CfEGXtaapnsNWFrHuWi7Wo5vqyuHd21zGm1u5rsiR6tKNCsFC4nzf3WUNxJNoZrDSdF9KERqhTWWmmcM4qdKRCtBWKTrs1DJD2oiK6BK9BgwoW2dfQdKuxojFyFvmxqPKDDAEZPPpJ51wHoFzBFMM1tUBBkN15cT2GpNwKzDcjHPKJAQ6FNRgppfQytzqpq76sSeZaWAB8hhULMJCQGU57ZUjvP7xYAQwtACBnYrjdxA91XwXFbq5AsQJwAmLw6euKVWNyv11BuHrejVmnNViWg5kuZBrtgL6NtzRWHtdxngHDMtuyky3brqGXaGQhUyXrkSpeknkkHL6NLThHH5NPnfFMVPwn2xf5UM5R51X2nTBzADSVcpi4cT7i44dT7o3yRKWtKfUzZiuNyTcSSrfH8KVdLap5ZKLmdPuXM65M2Z5wJVh3Uc4iv6iZKk44RKikM7zs1hqC4sBxRwLZjxhKvvMXDjDcYFkzyUkues4y7fjdCnVTxc4vTYUqcbY2k2WMssyj9SDseVc7dVrEvWCLQtYy79mJFoz1Hmsfk5ynE28ipznzQ3yTBugLHA6j6qW3S74eY4pJ6iynFEuXT4RqqkLGFcMh3goqS7CkphUMzP4wuJyGnzqa5tCno4U3dJ2jUL7Povg8voRqYAfiHyXC8zuhn225EdmRcSnu2pAuutQVV9hN3bkjfzAFUhUWKki8SwXtFSjy6NJyrYUiaze4p7ApsjHQBCgg2zAoBaGCwVN8991Jny31B5vPyYHy1oRSE4xTVZ7tTw9FyQ7w9p1NSEF4sziCxZHh5rFWZKAajc5c7KaMNDvHPNV6S62MTFGTyuKPQNbv9yHRGN4eH6SnZGW6snvEVdYCspWZ1U3Nbxo6vCmBK95UyYpcxHgg1CCGdU4s3edju2NDQkMifyPkJdkabzzHVDhJJbChAJc1ACQfNW74VXXwrBZmeZyA2R28MBctDyXuSuffiwueys2LVowLu9wiTHUox7KQjtHK2c9howk9czzx2mpnYzkVYH42CYsWa5514EM4CJEXPJSSbXSgJJ"
116 + b.SetBytes(int64(len(in))) // 1024
117 + b.ReportAllocs()
118 + for i := 0; i < b.N; i++ {
119 + _ = Decode(in)
120 + }
121 +}
122 +
123 +func BenchmarkEncodeOneKilo(b *testing.B) {
124 + var in = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x00\x00\x04\xff\xfb\x63\xc9\x7e\x5f\x97\x68\xe5\x10\x08\xe5\xa5\x9a\x7c\x24\x75\x35\xbe\xaf\x37\x0b\xc3\xf4\x18\x62\x4a\xb7\x18\xd0\x10\x0b\x6b\x62\xe1\x36\x0f\x62\xa6\xeb\x78\xa5\xf3\x33\x52\xbc\xdf\x04\xcb\x37\xcf\x3d\x97\x5c\xb8\x75\x09\x1f\x18\x9f\xfc\xa9\xda\x1e\x59\x77\x09\x9c\x5d\xb6\xf2\x9e\x45\xb7\x5e\x5d\x11\xf1\x20\x14\x85\xf8\x54\x87\x8c\x1e\x2c\x2e\x15\x57\x89\xe7\x5d\x49\xb6\xae\x24\x3a\x20\x50\x0e\xa7\x5b\x10\xbf\x0a\xb4\x01\x42\xed\xce\x2d\x45\x21\xb6\xe8\x64\x73\x4e\x7e\x0a\x36\x1d\x57\x0a\x5e\x1c\x21\xc2\xb8\xe7\x89\x82\xe4\x04\x7e\x50\xff\xda\x4f\xfe\x11\x95\xfb\x35\xf9\x6d\x32\xce\xef\x8f\x3d\x1b\xdb\x38\xfa\xcd\x26\x36\x12\x93\xa0\x96\xea\x42\xbe\xd6\x85\x86\xc1\xc1\xe2\x55\x41\xd1\x7f\x8d\x0e\x00\x81\x58\xb4\x10\xbb\x64\x92\x05\x07\xa9\xd5\xd9\x40\x28\x8b\x9b\x4c\x8d\x8e\x4e\x69\xf9\xc9\x35\xea\xda\x2f\x61\x87\x35\x2d\x6b\x25\x32\xf0\x7e\x89\x1a\xcb\xc0\xea\x66\x88\x99\x39\xe0\x3b\x24\x3b\x05\x74\xd3\x72\xf6\x48\x15\xdc\x02\x0a\xbf\xc8\x49\x42\x10\x22\xeb\xe9\x44\x71\x55\xaf\x67\x67\xe6\x2a\x40\x31\x81\xb9\x6f\x65\x86\x0f\x0f\x9d\x58\x4c\x51\xc1\x2e\x4e\x60\x7e\xe8\x93\x39\x90\xda\xe5\xbe\xec\xe4\xdd\xbc\x1d\xba\x40\xa6\x85\xd9\xb2\xec\xb4\x26\x74\xee\xc1\xec\xe3\x40\xb9\x49\xa3\xe1\x26\x76\x8a\xeb\x95\xc8\x72\xb0\x85\x36\x19\x3f\x55\x06\x7b\xcd\x3e\xd0\xdf\x7e\x8d\x2a\xea\xa6\x24\xc6\xf6\xfb\xda\xe0\x45\xcf\x32\x0e\xbc\xf4\x41\x7d\x71\x3d\x86\xf9\xb4\xaf\x07\xa0\xd1\x34\x8a\x02\x28\x56\xd4\xcc\x36\x44\x98\x44\xcb\x9d\xc5\xfc\x45\x2d\xc4\x5c\xfe\xce\xaa\x44\xda\x66\x52\x2d\x32\x6e\x13\x32\xac\xaf\x13\x72\x87\x79\xd2\x92\x54\x9f\xc7\xb9\xf3\x21\xae\xdd\x69\x44\xe9\x46\x94\x1c\x62\x84\x03\xe0\xbf\x66\xfb\xe0\x79\xf9\x57\x9e\x22\x9e\x23\x2d\x2a\x73\xeb\x74\x38\xf0\xea\x5d\xb3\x8f\x87\x26\x3e\x3c\x54\x11\xb7\x98\xbd\x7f\x78\x64\xa3\xf1\x8f\xa9\x5e\x4f\x18\x3f\xa7\x1f\x3a\x29\x27\x27\xb7\x49\x40\x16\x18\x1f\xd3\xed\x86\x61\xbd\xc3\x4e\x4a\x53\x37\x78\x5c\x00\xd3\x50\x45\x1c\x55\xc0\x9b\xd7\x62\x29\x88\x2e\xa4\x0d\x6a\x15\x6c\x33\x3c\xe7\x31\xfa\xc1\xaf\xdf\x7a\x3e\x37\x3e\xe5\xbc\xfd\xfb\x9b\x72\x10\x35\x90\x25\x6e\x87\x0d\x74\x1c\xfd\xe3\x0b\xee\xf5\x92\x28\x8d\x22\x8a\x49\x7b\xcd\xbb\xd8\x24\x6b\x5e\x58\x40\xec\x1b\x6c\xed\x8e\xcb\x56\x62\xa6\xb4\x42\x3d\x7d\xa2\xef\x27\x27\x46\x50\xbc\x5e\x37\x9b\x27\x72\xf0\xea\xa7\xe7\x4d\xf4\xae\x7e\x95\x8f\x91\x2e\x58\xc4\x6a\x06\xda\x7a\x06\x5c\x8d\xfe\xef\xf5\xb3\x0f\xb4\x0a\x20\x53\xd8\x35\x80\x02\xca\x97\x81\xb6\x1c\x4b\x8f\xb7\xee\xd0\xc3\x88\x6c\x76\x3e\xb0\x28\xce\xa1\x9f\x76\x5f\xaa\xc3\x53\x44\x09\x70\xa3\x95\xd9\x8c\x54\xba\x8a\x9a\x6b\xce\xc3\x07\xdf\x13\x6d\xea\x0f\x51\x9c\xe2\x81\x87\xf6\x82\x7a\x70\xd8\xfa\xe2\xa8\x32\xc1\x5e\x53\xc2\x85\xe9\x61\x8a\x17\x82\x12\xab\x92\x79\x2b\xed\x07\xca\x1e\x93\x23\x9c\x4b\xd2\x89\x86\xac\x55\xf9\x50\x23\x8f\x9e\xd3\xab\x22\x57\x91\x5a\x0b\x48\xd7\xa2\xb8\x06\xbb\x74\xae\xe9\xca\x06\x41\x8d\x6a\x00\x42\xc4\x40\xa9\xfe\xae\x88\x42\xc2\x83\xe0\x8a\xd8\x5c\xbb\x5a\xb5\x9c\x1d\xa5\xbe\x67\x50\xb1\x4e\xec\x96\x65\xaa\x87\x5b\xb0\x76\x88\xe3\x1b\xcb\x38\x21\x02\x8e\xc9\xe7\xf5\xc7\xe1\x1d\xe8\xeb\x54\x0e\x0b\xea\xd1\x2e\xad\xbb\xec\x22\x21\xb3\x64\x36\x29\x34\x5e\x3a\x22\xe8\x03\x4b\x86\xb1\x67\x7d\x4f\x48\x6d\xfb\x4b\xde\xe6\x4c\xb0\xaf\x40\x66\xab\xe9\x1a\x4e\xae\x1a\x7e\x05\xc5\x67\x2a\x95\x6d\xc2\x61\x35\x20\xfe\x33\xc3\x2c\x7f\x9b\xbe\x9f\x9a\xd5\xf0\x63\x28\xa1\x94\xb1\x5c\xc1\x18\x6b\x5b\x33\xb4\x4d\xcf\xbe\xf7\xb2\x94\x58\xaa\xcf\xad\xc8\x75\x93\x1a\x08\xf4\xd2\xd9\xf6\x95\x03\x3b\xf3\x4e\xfb\x15\xe4\x28\xed\xd5\x79\xd9\xbf\xb7\x8f\xb2\x70\x16\x4c\x2d\x65\xf6\xec\x33\x1e\xaf\xea\x46\x69\xc6\x9a\x6b\xdd\xf3\x57\xe0\x1d\x28\xcd\xf8\x83\x3d\x94\x4c\x2f\x6e\xfd\x51\x3d\xa8\xff\xcb\x33\xad\x32\x42\x0e\xd3\x00\x0a\xe5\x71\x76\x3b\x83\xc9\x2a\x67\x50\xc3\xa5\xeb\x4d\x8d\x67\xd6\xd9\x1b\x9a\x5a\xbe\xdd\xc5\x15\x00\xcf\x97\x0f\x47\x44\x34\x1d\x4e\xb6\x6f\x91\x31\xf3\x45\x0f\x59\x48\x10\x23\x53\x40\x49\x83\xe6\xc8\xdf\x51\x6c\xa8\x9f\x3a\x43\x3d\xb9\xd4\xea\x30\x4d\xe0\xd2\xb8\x44\xf3\x91\x20\x79\xdb\x7b\xe6\x50\xf9\x0f\xfb\x4c\xac\x79\x93\xf6\xf8\x96\x0d\x55\x7c\x41\x9b\x1a\x86\xad\x4b\xd1\xf9\x5d\xed\x3a\x4f\xc9\x64\x72\xd4\x22\x53\x59\x2f\x01\x00\x00\xff\xff\xc6\xfd\xa0\x37\x00\x04\x00\x00")
125 + b.SetBytes(int64(len(in))) // 1024
126 + b.ReportAllocs()
127 + for i := 0; i < b.N; i++ {
128 + _ = Encode(in)
129 + }
130 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/available_unix.go
+1
@@ -1,4 +1,5 @@
1 // +build darwin freebsd dragonfly netbsd openbsd linux
2 +
3 package reuseport
4
5 import (
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_windows.go
+9 -3
@@ -1,8 +1,6 @@
1 package reuseport
2
3 -import (
4 - "net"
5 -)
3 +import "net"
4
5 // TODO. for now, just pass it over to net.Listen/net.Dial
6
@@ -10,6 +8,14 @@ func listen(network, address string) (net.Listener, error) {
8 return net.Listen(network, address)
9 }
10
11 +func listenPacket(netw, laddr string) (net.PacketConn, error) {
12 + return net.ListenPacket(netw, laddr)
13 +}
14 +
15 +func listenStream(netw, addr string) (net.Listener, error) {
16 + return listen(netw, addr)
17 +}
18 +
19 func dial(dialer net.Dialer, network, address string) (net.Conn, error) {
20 return dialer.Dial(network, address)
21 }
Godeps/_workspace/src/github.com/jbenet/go-reuseport/opts_posix.go
+1 -1
@@ -2,7 +2,7 @@
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 -// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
5 +// +build darwin dragonfly freebsd linux netbsd openbsd solaris
6
7 package reuseport
8
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_bsd.go
+1 -1
@@ -1,4 +1,4 @@
1 -// +build darwin freebsd dragonfly netbsd openbsd
1 +// +build darwin,amd64 freebsd dragonfly netbsd openbsd
2
3 package poll
4
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_darwin_386.go new
+57
@@ -0,0 +1,57 @@
1 +package poll
2 +
3 +import (
4 + "syscall"
5 + "time"
6 +)
7 +
8 +type Poller struct {
9 + kqfd int
10 + event syscall.Kevent_t
11 +}
12 +
13 +func New(fd int) (p *Poller, err error) {
14 + p = &Poller{}
15 +
16 + p.kqfd, err = syscall.Kqueue()
17 + if p.kqfd == -1 || err != nil {
18 + return nil, err
19 + }
20 +
21 + p.event = syscall.Kevent_t{
22 + Ident: uint32(fd),
23 + Filter: syscall.EVFILT_WRITE,
24 + Flags: syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_ONESHOT,
25 + Fflags: 0,
26 + Data: 0,
27 + Udata: nil,
28 + }
29 + return p, nil
30 +}
31 +
32 +func (p *Poller) Close() error {
33 + return syscall.Close(p.kqfd)
34 +}
35 +
36 +func (p *Poller) WaitWrite(deadline time.Time) error {
37 +
38 + // setup timeout
39 + var timeout *syscall.Timespec
40 + if !deadline.IsZero() {
41 + d := deadline.Sub(time.Now())
42 + t := syscall.NsecToTimespec(d.Nanoseconds())
43 + timeout = &t
44 + }
45 +
46 + // wait on kevent
47 + events := make([]syscall.Kevent_t, 1)
48 + n, err := syscall.Kevent(p.kqfd, []syscall.Kevent_t{p.event}, events, timeout)
49 + if err != nil {
50 + return err
51 + }
52 +
53 + if n < 1 {
54 + return errTimeout
55 + }
56 + return nil
57 +}
Godeps/_workspace/src/github.com/kardianos/osext/osext_procfs.go
+7 -1
@@ -11,12 +11,18 @@ import (
11 "fmt"
12 "os"
13 "runtime"
14 + "strings"
15 )
16
17 func executable() (string, error) {
18 switch runtime.GOOS {
19 case "linux":
19 - return os.Readlink("/proc/self/exe")
20 + const deletedSuffix = " (deleted)"
21 + execpath, err := os.Readlink("/proc/self/exe")
22 + if err != nil {
23 + return execpath, err
24 + }
25 + return strings.TrimSuffix(execpath, deletedSuffix), nil
26 case "netbsd":
27 return os.Readlink("/proc/curproc/exe")
28 case "openbsd", "dragonfly":
Godeps/_workspace/src/github.com/kardianos/osext/osext_test.go
+119 -18
@@ -7,35 +7,42 @@
7 package osext
8
9 import (
10 + "bytes"
11 "fmt"
12 + "io"
13 "os"
12 - oexec "os/exec"
14 + "os/exec"
15 "path/filepath"
16 "runtime"
17 "testing"
18 )
19
18 -const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
20 +const (
21 + executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE"
22
20 -func TestExecPath(t *testing.T) {
23 + executableEnvValueMatch = "match"
24 + executableEnvValueDelete = "delete"
25 +)
26 +
27 +func TestExecutableMatch(t *testing.T) {
28 ep, err := Executable()
29 if err != nil {
23 - t.Fatalf("ExecPath failed: %v", err)
30 + t.Fatalf("Executable failed: %v", err)
31 }
25 - // we want fn to be of the form "dir/prog"
32 +
33 + // fullpath to be of the form "dir/prog".
34 dir := filepath.Dir(filepath.Dir(ep))
27 - fn, err := filepath.Rel(dir, ep)
35 + fullpath, err := filepath.Rel(dir, ep)
36 if err != nil {
37 t.Fatalf("filepath.Rel: %v", err)
38 }
31 - cmd := &oexec.Cmd{}
32 - // make child start with a relative program path
33 - cmd.Dir = dir
34 - cmd.Path = fn
35 - // forge argv[0] for child, so that we can verify we could correctly
36 - // get real path of the executable without influenced by argv[0].
37 - cmd.Args = []string{"-", "-test.run=XXXX"}
38 - cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
39 + // Make child start with a relative program path.
40 + // Alter argv[0] for child to verify getting real path without argv[0].
41 + cmd := &exec.Cmd{
42 + Dir: dir,
43 + Path: fullpath,
44 + Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)},
45 + }
46 out, err := cmd.CombinedOutput()
47 if err != nil {
48 t.Fatalf("exec(self) failed: %v", err)
@@ -49,6 +56,63 @@ func TestExecPath(t *testing.T) {
56 }
57 }
58
59 +func TestExecutableDelete(t *testing.T) {
60 + if runtime.GOOS != "linux" {
61 + t.Skip()
62 + }
63 + fpath, err := Executable()
64 + if err != nil {
65 + t.Fatalf("Executable failed: %v", err)
66 + }
67 +
68 + r, w := io.Pipe()
69 + stderrBuff := &bytes.Buffer{}
70 + stdoutBuff := &bytes.Buffer{}
71 + cmd := &exec.Cmd{
72 + Path: fpath,
73 + Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)},
74 + Stdin: r,
75 + Stderr: stderrBuff,
76 + Stdout: stdoutBuff,
77 + }
78 + err = cmd.Start()
79 + if err != nil {
80 + t.Fatalf("exec(self) start failed: %v", err)
81 + }
82 +
83 + tempPath := fpath + "_copy"
84 + _ = os.Remove(tempPath)
85 +
86 + err = copyFile(tempPath, fpath)
87 + if err != nil {
88 + t.Fatalf("copy file failed: %v", err)
89 + }
90 + err = os.Remove(fpath)
91 + if err != nil {
92 + t.Fatalf("remove running test file failed: %v", err)
93 + }
94 + err = os.Rename(tempPath, fpath)
95 + if err != nil {
96 + t.Fatalf("rename copy to previous name failed: %v", err)
97 + }
98 +
99 + w.Write([]byte{0})
100 + w.Close()
101 +
102 + err = cmd.Wait()
103 + if err != nil {
104 + t.Fatalf("exec wait failed: %v", err)
105 + }
106 +
107 + childPath := stderrBuff.String()
108 + if !filepath.IsAbs(childPath) {
109 + t.Fatalf("Child returned %q, want an absolute path", childPath)
110 + }
111 + if !sameFile(childPath, fpath) {
112 + t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath)
113 + }
114 +}
115 +
116 func sameFile(fn1, fn2 string) bool {
117 fi1, err := os.Stat(fn1)
118 if err != nil {
@@ -60,10 +124,30 @@ func sameFile(fn1, fn2 string) bool {
124 }
125 return os.SameFile(fi1, fi2)
126 }
127 +func copyFile(dest, src string) error {
128 + df, err := os.Create(dest)
129 + if err != nil {
130 + return err
131 + }
132 + defer df.Close()
133 +
134 + sf, err := os.Open(src)
135 + if err != nil {
136 + return err
137 + }
138 + defer sf.Close()
139
64 -func init() {
65 - if e := os.Getenv(execPath_EnvVar); e != "" {
66 - // first chdir to another path
140 + _, err = io.Copy(df, sf)
141 + return err
142 +}
143 +
144 +func TestMain(m *testing.M) {
145 + env := os.Getenv(executableEnvVar)
146 + switch env {
147 + case "":
148 + os.Exit(m.Run())
149 + case executableEnvValueMatch:
150 + // First chdir to another path.
151 dir := "/"
152 if runtime.GOOS == "windows" {
153 dir = filepath.VolumeName(".")
@@ -74,6 +158,23 @@ func init() {
158 } else {
159 fmt.Fprint(os.Stderr, ep)
160 }
77 - os.Exit(0)
161 + case executableEnvValueDelete:
162 + bb := make([]byte, 1)
163 + var err error
164 + n, err := os.Stdin.Read(bb)
165 + if err != nil {
166 + fmt.Fprint(os.Stderr, "ERROR: ", err)
167 + os.Exit(2)
168 + }
169 + if n != 1 {
170 + fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n)
171 + os.Exit(2)
172 + }
173 + if ep, err := Executable(); err != nil {
174 + fmt.Fprint(os.Stderr, "ERROR: ", err)
175 + } else {
176 + fmt.Fprint(os.Stderr, ep)
177 + }
178 }
179 + os.Exit(0)
180 }
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/bench2_test.go renamed
+1 -1
@@ -4,7 +4,7 @@
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6
7 -// +build go1.3
7 +// +build !go1.2
8
9 package leveldb
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/bench2_test.go new
+30
@@ -0,0 +1,30 @@
1 +// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2 +// All rights reserved.
3 +//
4 +// Use of this source code is governed by a BSD-style license that can be
5 +// found in the LICENSE file.
6 +
7 +// +build !go1.2
8 +
9 +package cache
10 +
11 +import (
12 + "math/rand"
13 + "testing"
14 +)
15 +
16 +func BenchmarkLRUCache(b *testing.B) {
17 + c := NewCache(NewLRU(10000))
18 +
19 + b.SetParallelism(10)
20 + b.RunParallel(func(pb *testing.PB) {
21 + r := rand.New(rand.NewSource(time.Now().UnixNano()))
22 +
23 + for pb.Next() {
24 + key := uint64(r.Intn(1000000))
25 + c.Get(0, key, func() (int, Value) {
26 + return 1, key
27 + }).Release()
28 + }
29 + })
30 +}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache.go
+639 -122
@@ -8,152 +8,669 @@
8 package cache
9
10 import (
11 + "sync"
12 "sync/atomic"
13 + "unsafe"
14 +
15 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
16 )
17
14 -// SetFunc is the function that will be called by Namespace.Get to create
15 -// a cache object, if charge is less than one than the cache object will
16 -// not be registered to cache tree, if value is nil then the cache object
17 -// will not be created.
18 -type SetFunc func() (charge int, value interface{})
18 +// Cacher provides interface to implements a caching functionality.
19 +// An implementation must be goroutine-safe.
20 +type Cacher interface {
21 + // Capacity returns cache capacity.
22 + Capacity() int
23 +
24 + // SetCapacity sets cache capacity.
25 + SetCapacity(capacity int)
26
20 -// DelFin is the function that will be called as the result of a delete operation.
21 -// Exist == true is indication that the object is exist, and pending == true is
22 -// indication of deletion already happen but haven't done yet (wait for all handles
23 -// to be released). And exist == false means the object doesn't exist.
24 -type DelFin func(exist, pending bool)
27 + // Promote promotes the 'cache node'.
28 + Promote(n *Node)
29
26 -// PurgeFin is the function that will be called as the result of a purge operation.
27 -type PurgeFin func(ns, key uint64)
30 + // Ban evicts the 'cache node' and prevent subsequent 'promote'.
31 + Ban(n *Node)
32
29 -// Cache is a cache tree. A cache instance must be goroutine-safe.
30 -type Cache interface {
31 - // SetCapacity sets cache tree capacity.
32 - SetCapacity(capacity int)
33 + // Evict evicts the 'cache node'.
34 + Evict(n *Node)
35
34 - // Capacity returns cache tree capacity.
35 - Capacity() int
36 + // EvictNS evicts 'cache node' with the given namespace.
37 + EvictNS(ns uint64)
38
37 - // Used returns used cache tree capacity.
38 - Used() int
39 -
40 - // Size returns entire alive cache objects size.
41 - Size() int
42 -
43 - // NumObjects returns number of alive objects.
44 - NumObjects() int
45 -
46 - // GetNamespace gets cache namespace with the given id.
47 - // GetNamespace is never return nil.
48 - GetNamespace(id uint64) Namespace
49 -
50 - // PurgeNamespace purges cache namespace with the given id from this cache tree.
51 - // Also read Namespace.Purge.
52 - PurgeNamespace(id uint64, fin PurgeFin)
53 -
54 - // ZapNamespace detaches cache namespace with the given id from this cache tree.
55 - // Also read Namespace.Zap.
56 - ZapNamespace(id uint64)
57 -
58 - // Purge purges all cache namespace from this cache tree.
59 - // This is behave the same as calling Namespace.Purge method on all cache namespace.
60 - Purge(fin PurgeFin)
61 -
62 - // Zap detaches all cache namespace from this cache tree.
63 - // This is behave the same as calling Namespace.Zap method on all cache namespace.
64 - Zap()
65 -}
66 -
67 -// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
68 -type Namespace interface {
69 - // Get gets cache object with the given key.
70 - // If cache object is not found and setf is not nil, Get will atomically creates
71 - // the cache object by calling setf. Otherwise Get will returns nil.
72 - //
73 - // The returned cache handle should be released after use by calling Release
74 - // method.
75 - Get(key uint64, setf SetFunc) Handle
76 -
77 - // Delete removes cache object with the given key from cache tree.
78 - // A deleted cache object will be released as soon as all of its handles have
79 - // been released.
80 - // Delete only happen once, subsequent delete will consider cache object doesn't
81 - // exist, even if the cache object ins't released yet.
82 - //
83 - // If not nil, fin will be called if the cache object doesn't exist or when
84 - // finally be released.
85 - //
86 - // Delete returns true if such cache object exist and never been deleted.
87 - Delete(key uint64, fin DelFin) bool
88 -
89 - // Purge removes all cache objects within this namespace from cache tree.
90 - // This is the same as doing delete on all cache objects.
91 - //
92 - // If not nil, fin will be called on all cache objects when its finally be
93 - // released.
94 - Purge(fin PurgeFin)
95 -
96 - // Zap detaches namespace from cache tree and release all its cache objects.
97 - // A zapped namespace can never be filled again.
98 - // Calling Get on zapped namespace will always return nil.
99 - Zap()
100 -}
101 -
102 -// Handle is a cache handle.
103 -type Handle interface {
104 - // Release releases this cache handle. This method can be safely called mutiple
105 - // times.
106 - Release()
107 -
108 - // Value returns value of this cache handle.
109 - // Value will returns nil after this cache handle have be released.
110 - Value() interface{}
39 + // EvictAll evicts all 'cache node'.
40 + EvictAll()
41 +
42 + // Close closes the 'cache tree'
43 + Close() error
44 }
45
113 -const (
114 - DelNotExist = iota
115 - DelExist
116 - DelPendig
117 -)
46 +// Value is a 'cacheable object'. It may implements util.Releaser, if
47 +// so the the Release method will be called once object is released.
48 +type Value interface{}
49
119 -// Namespace state.
120 -type nsState int
50 +type CacheGetter struct {
51 + Cache *Cache
52 + NS uint64
53 +}
54
122 -const (
123 - nsEffective nsState = iota
124 - nsZapped
125 -)
55 +func (g *CacheGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle {
56 + return g.Cache.Get(g.NS, key, setFunc)
57 +}
58
127 -// Node state.
128 -type nodeState int
59 +// The hash tables implementation is based on:
60 +// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, Kunlong Zhang, and Michael Spear. ACM Symposium on Principles of Distributed Computing, Jul 2014.
61
62 const (
131 - nodeZero nodeState = iota
132 - nodeEffective
133 - nodeEvicted
134 - nodeDeleted
63 + mInitialSize = 1 << 4
64 + mOverflowThreshold = 1 << 5
65 + mOverflowGrowThreshold = 1 << 7
66 )
67
137 -// Fake handle.
138 -type fakeHandle struct {
139 - value interface{}
140 - fin func()
141 - once uint32
68 +type mBucket struct {
69 + mu sync.Mutex
70 + node []*Node
71 + frozen bool
72 +}
73 +
74 +func (b *mBucket) freeze() []*Node {
75 + b.mu.Lock()
76 + defer b.mu.Unlock()
77 + if !b.frozen {
78 + b.frozen = true
79 + }
80 + return b.node
81 +}
82 +
83 +func (b *mBucket) get(r *Cache, h *mNode, hash uint32, ns, key uint64, noset bool) (done, added bool, n *Node) {
84 + b.mu.Lock()
85 +
86 + if b.frozen {
87 + b.mu.Unlock()
88 + return
89 + }
90 +
91 + // Scan the node.
92 + for _, n := range b.node {
93 + if n.hash == hash && n.ns == ns && n.key == key {
94 + atomic.AddInt32(&n.ref, 1)
95 + b.mu.Unlock()
96 + return true, false, n
97 + }
98 + }
99 +
100 + // Get only.
101 + if noset {
102 + b.mu.Unlock()
103 + return true, false, nil
104 + }
105 +
106 + // Create node.
107 + n = &Node{
108 + r: r,
109 + hash: hash,
110 + ns: ns,
111 + key: key,
112 + ref: 1,
113 + }
114 + // Add node to bucket.
115 + b.node = append(b.node, n)
116 + bLen := len(b.node)
117 + b.mu.Unlock()
118 +
119 + // Update counter.
120 + grow := atomic.AddInt32(&r.nodes, 1) >= h.growThreshold
121 + if bLen > mOverflowThreshold {
122 + grow = grow || atomic.AddInt32(&h.overflow, 1) >= mOverflowGrowThreshold
123 + }
124 +
125 + // Grow.
126 + if grow && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
127 + nhLen := len(h.buckets) << 1
128 + nh := &mNode{
129 + buckets: make([]unsafe.Pointer, nhLen),
130 + mask: uint32(nhLen) - 1,
131 + pred: unsafe.Pointer(h),
132 + growThreshold: int32(nhLen * mOverflowThreshold),
133 + shrinkThreshold: int32(nhLen >> 1),
134 + }
135 + ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
136 + if !ok {
137 + panic("BUG: failed swapping head")
138 + }
139 + go nh.initBuckets()
140 + }
141 +
142 + return true, true, n
143 }
144
144 -func (h *fakeHandle) Value() interface{} {
145 - if atomic.LoadUint32(&h.once) == 0 {
146 - return h.value
145 +func (b *mBucket) delete(r *Cache, h *mNode, hash uint32, ns, key uint64) (done, deleted bool) {
146 + b.mu.Lock()
147 +
148 + if b.frozen {
149 + b.mu.Unlock()
150 + return
151 + }
152 +
153 + // Scan the node.
154 + var (
155 + n *Node
156 + bLen int
157 + )
158 + for i := range b.node {
159 + n = b.node[i]
160 + if n.ns == ns && n.key == key {
161 + if atomic.LoadInt32(&n.ref) == 0 {
162 + deleted = true
163 +
164 + // Call releaser.
165 + if n.value != nil {
166 + if r, ok := n.value.(util.Releaser); ok {
167 + r.Release()
168 + }
169 + n.value = nil
170 + }
171 +
172 + // Remove node from bucket.
173 + b.node = append(b.node[:i], b.node[i+1:]...)
174 + bLen = len(b.node)
175 + }
176 + break
177 + }
178 + }
179 + b.mu.Unlock()
180 +
181 + if deleted {
182 + // Call OnDel.
183 + for _, f := range n.onDel {
184 + f()
185 + }
186 +
187 + // Update counter.
188 + atomic.AddInt32(&r.size, int32(n.size)*-1)
189 + shrink := atomic.AddInt32(&r.nodes, -1) < h.shrinkThreshold
190 + if bLen >= mOverflowThreshold {
191 + atomic.AddInt32(&h.overflow, -1)
192 + }
193 +
194 + // Shrink.
195 + if shrink && len(h.buckets) > mInitialSize && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) {
196 + nhLen := len(h.buckets) >> 1
197 + nh := &mNode{
198 + buckets: make([]unsafe.Pointer, nhLen),
199 + mask: uint32(nhLen) - 1,
200 + pred: unsafe.Pointer(h),
201 + growThreshold: int32(nhLen * mOverflowThreshold),
202 + shrinkThreshold: int32(nhLen >> 1),
203 + }
204 + ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh))
205 + if !ok {
206 + panic("BUG: failed swapping head")
207 + }
208 + go nh.initBuckets()
209 + }
210 + }
211 +
212 + return true, deleted
213 +}
214 +
215 +type mNode struct {
216 + buckets []unsafe.Pointer // []*mBucket
217 + mask uint32
218 + pred unsafe.Pointer // *mNode
219 + resizeInProgess int32
220 +
221 + overflow int32
222 + growThreshold int32
223 + shrinkThreshold int32
224 +}
225 +
226 +func (n *mNode) initBucket(i uint32) *mBucket {
227 + if b := (*mBucket)(atomic.LoadPointer(&n.buckets[i])); b != nil {
228 + return b
229 + }
230 +
231 + p := (*mNode)(atomic.LoadPointer(&n.pred))
232 + if p != nil {
233 + var node []*Node
234 + if n.mask > p.mask {
235 + // Grow.
236 + pb := (*mBucket)(atomic.LoadPointer(&p.buckets[i&p.mask]))
237 + if pb == nil {
238 + pb = p.initBucket(i & p.mask)
239 + }
240 + m := pb.freeze()
241 + // Split nodes.
242 + for _, x := range m {
243 + if x.hash&n.mask == i {
244 + node = append(node, x)
245 + }
246 + }
247 + } else {
248 + // Shrink.
249 + pb0 := (*mBucket)(atomic.LoadPointer(&p.buckets[i]))
250 + if pb0 == nil {
251 + pb0 = p.initBucket(i)
252 + }
253 + pb1 := (*mBucket)(atomic.LoadPointer(&p.buckets[i+uint32(len(n.buckets))]))
254 + if pb1 == nil {
255 + pb1 = p.initBucket(i + uint32(len(n.buckets)))
256 + }
257 + m0 := pb0.freeze()
258 + m1 := pb1.freeze()
259 + // Merge nodes.
260 + node = make([]*Node, 0, len(m0)+len(m1))
261 + node = append(node, m0...)
262 + node = append(node, m1...)
263 + }
264 + b := &mBucket{node: node}
265 + if atomic.CompareAndSwapPointer(&n.buckets[i], nil, unsafe.Pointer(b)) {
266 + if len(node) > mOverflowThreshold {
267 + atomic.AddInt32(&n.overflow, int32(len(node)-mOverflowThreshold))
268 + }
269 + return b
270 + }
271 + }
272 +
273 + return (*mBucket)(atomic.LoadPointer(&n.buckets[i]))
274 +}
275 +
276 +func (n *mNode) initBuckets() {
277 + for i := range n.buckets {
278 + n.initBucket(uint32(i))
279 + }
280 + atomic.StorePointer(&n.pred, nil)
281 +}
282 +
283 +// Cache is a 'cache map'.
284 +type Cache struct {
285 + mu sync.RWMutex
286 + mHead unsafe.Pointer // *mNode
287 + nodes int32
288 + size int32
289 + cacher Cacher
290 + closed bool
291 +}
292 +
293 +// NewCache creates a new 'cache map'. The cacher is optional and
294 +// may be nil.
295 +func NewCache(cacher Cacher) *Cache {
296 + h := &mNode{
297 + buckets: make([]unsafe.Pointer, mInitialSize),
298 + mask: mInitialSize - 1,
299 + growThreshold: int32(mInitialSize * mOverflowThreshold),
300 + shrinkThreshold: 0,
301 + }
302 + for i := range h.buckets {
303 + h.buckets[i] = unsafe.Pointer(&mBucket{})
304 + }
305 + r := &Cache{
306 + mHead: unsafe.Pointer(h),
307 + cacher: cacher,
308 + }
309 + return r
310 +}
311 +
312 +func (r *Cache) getBucket(hash uint32) (*mNode, *mBucket) {
313 + h := (*mNode)(atomic.LoadPointer(&r.mHead))
314 + i := hash & h.mask
315 + b := (*mBucket)(atomic.LoadPointer(&h.buckets[i]))
316 + if b == nil {
317 + b = h.initBucket(i)
318 + }
319 + return h, b
320 +}
321 +
322 +func (r *Cache) delete(n *Node) bool {
323 + for {
324 + h, b := r.getBucket(n.hash)
325 + done, deleted := b.delete(r, h, n.hash, n.ns, n.key)
326 + if done {
327 + return deleted
328 + }
329 + }
330 + return false
331 +}
332 +
333 +// Nodes returns number of 'cache node' in the map.
334 +func (r *Cache) Nodes() int {
335 + return int(atomic.LoadInt32(&r.nodes))
336 +}
337 +
338 +// Size returns sums of 'cache node' size in the map.
339 +func (r *Cache) Size() int {
340 + return int(atomic.LoadInt32(&r.size))
341 +}
342 +
343 +// Capacity returns cache capacity.
344 +func (r *Cache) Capacity() int {
345 + if r.cacher == nil {
346 + return 0
347 + }
348 + return r.cacher.Capacity()
349 +}
350 +
351 +// SetCapacity sets cache capacity.
352 +func (r *Cache) SetCapacity(capacity int) {
353 + if r.cacher != nil {
354 + r.cacher.SetCapacity(capacity)
355 + }
356 +}
357 +
358 +// Get gets 'cache node' with the given namespace and key.
359 +// If cache node is not found and setFunc is not nil, Get will atomically creates
360 +// the 'cache node' by calling setFunc. Otherwise Get will returns nil.
361 +//
362 +// The returned 'cache handle' should be released after use by calling Release
363 +// method.
364 +func (r *Cache) Get(ns, key uint64, setFunc func() (size int, value Value)) *Handle {
365 + r.mu.RLock()
366 + defer r.mu.RUnlock()
367 + if r.closed {
368 + return nil
369 + }
370 +
371 + hash := murmur32(ns, key, 0xf00)
372 + for {
373 + h, b := r.getBucket(hash)
374 + done, _, n := b.get(r, h, hash, ns, key, setFunc == nil)
375 + if done {
376 + if n != nil {
377 + n.mu.Lock()
378 + if n.value == nil {
379 + if setFunc == nil {
380 + n.mu.Unlock()
381 + n.unref()
382 + return nil
383 + }
384 +
385 + n.size, n.value = setFunc()
386 + if n.value == nil {
387 + n.size = 0
388 + n.mu.Unlock()
389 + n.unref()
390 + return nil
391 + }
392 + atomic.AddInt32(&r.size, int32(n.size))
393 + }
394 + n.mu.Unlock()
395 + if r.cacher != nil {
396 + r.cacher.Promote(n)
397 + }
398 + return &Handle{unsafe.Pointer(n)}
399 + }
400 +
401 + break
402 + }
403 }
404 return nil
405 }
406
151 -func (h *fakeHandle) Release() {
152 - if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
407 +// Delete removes and ban 'cache node' with the given namespace and key.
408 +// A banned 'cache node' will never inserted into the 'cache tree'. Ban
409 +// only attributed to the particular 'cache node', so when a 'cache node'
410 +// is recreated it will not be banned.
411 +//
412 +// If onDel is not nil, then it will be executed if such 'cache node'
413 +// doesn't exist or once the 'cache node' is released.
414 +//
415 +// Delete return true is such 'cache node' exist.
416 +func (r *Cache) Delete(ns, key uint64, onDel func()) bool {
417 + r.mu.RLock()
418 + defer r.mu.RUnlock()
419 + if r.closed {
420 + return false
421 + }
422 +
423 + hash := murmur32(ns, key, 0xf00)
424 + for {
425 + h, b := r.getBucket(hash)
426 + done, _, n := b.get(r, h, hash, ns, key, true)
427 + if done {
428 + if n != nil {
429 + if onDel != nil {
430 + n.mu.Lock()
431 + n.onDel = append(n.onDel, onDel)
432 + n.mu.Unlock()
433 + }
434 + if r.cacher != nil {
435 + r.cacher.Ban(n)
436 + }
437 + n.unref()
438 + return true
439 + }
440 +
441 + break
442 + }
443 + }
444 +
445 + if onDel != nil {
446 + onDel()
447 + }
448 +
449 + return false
450 +}
451 +
452 +// Evict evicts 'cache node' with the given namespace and key. This will
453 +// simply call Cacher.Evict.
454 +//
455 +// Evict return true is such 'cache node' exist.
456 +func (r *Cache) Evict(ns, key uint64) bool {
457 + r.mu.RLock()
458 + defer r.mu.RUnlock()
459 + if r.closed {
460 + return false
461 + }
462 +
463 + hash := murmur32(ns, key, 0xf00)
464 + for {
465 + h, b := r.getBucket(hash)
466 + done, _, n := b.get(r, h, hash, ns, key, true)
467 + if done {
468 + if n != nil {
469 + if r.cacher != nil {
470 + r.cacher.Evict(n)
471 + }
472 + n.unref()
473 + return true
474 + }
475 +
476 + break
477 + }
478 + }
479 +
480 + return false
481 +}
482 +
483 +// EvictNS evicts 'cache node' with the given namespace. This will
484 +// simply call Cacher.EvictNS.
485 +func (r *Cache) EvictNS(ns uint64) {
486 + r.mu.RLock()
487 + defer r.mu.RUnlock()
488 + if r.closed {
489 + return
490 + }
491 +
492 + if r.cacher != nil {
493 + r.cacher.EvictNS(ns)
494 + }
495 +}
496 +
497 +// EvictAll evicts all 'cache node'. This will simply call Cacher.EvictAll.
498 +func (r *Cache) EvictAll() {
499 + r.mu.RLock()
500 + defer r.mu.RUnlock()
501 + if r.closed {
502 return
503 }
155 - if h.fin != nil {
156 - h.fin()
157 - h.fin = nil
504 +
505 + if r.cacher != nil {
506 + r.cacher.EvictAll()
507 + }
508 +}
509 +
510 +// Close closes the 'cache map' and releases all 'cache node'.
511 +func (r *Cache) Close() error {
512 + r.mu.Lock()
513 + if !r.closed {
514 + r.closed = true
515 +
516 + if r.cacher != nil {
517 + if err := r.cacher.Close(); err != nil {
518 + return err
519 + }
520 + }
521 +
522 + h := (*mNode)(r.mHead)
523 + h.initBuckets()
524 +
525 + for i := range h.buckets {
526 + b := (*mBucket)(h.buckets[i])
527 + for _, n := range b.node {
528 + // Call releaser.
529 + if n.value != nil {
530 + if r, ok := n.value.(util.Releaser); ok {
531 + r.Release()
532 + }
533 + n.value = nil
534 + }
535 +
536 + // Call OnDel.
537 + for _, f := range n.onDel {
538 + f()
539 + }
540 + }
541 + }
542 + }
543 + r.mu.Unlock()
544 + return nil
545 +}
546 +
547 +// Node is a 'cache node'.
548 +type Node struct {
549 + r *Cache
550 +
551 + hash uint32
552 + ns, key uint64
553 +
554 + mu sync.Mutex
555 + size int
556 + value Value
557 +
558 + ref int32
559 + onDel []func()
560 +
561 + CacheData unsafe.Pointer
562 +}
563 +
564 +// NS returns this 'cache node' namespace.
565 +func (n *Node) NS() uint64 {
566 + return n.ns
567 +}
568 +
569 +// Key returns this 'cache node' key.
570 +func (n *Node) Key() uint64 {
571 + return n.key
572 +}
573 +
574 +// Size returns this 'cache node' size.
575 +func (n *Node) Size() int {
576 + return n.size
577 +}
578 +
579 +// Value returns this 'cache node' value.
580 +func (n *Node) Value() Value {
581 + return n.value
582 +}
583 +
584 +// Ref returns this 'cache node' ref counter.
585 +func (n *Node) Ref() int32 {
586 + return atomic.LoadInt32(&n.ref)
587 +}
588 +
589 +// GetHandle returns an handle for this 'cache node'.
590 +func (n *Node) GetHandle() *Handle {
591 + if atomic.AddInt32(&n.ref, 1) <= 1 {
592 + panic("BUG: Node.GetHandle on zero ref")
593 + }
594 + return &Handle{unsafe.Pointer(n)}
595 +}
596 +
597 +func (n *Node) unref() {
598 + if atomic.AddInt32(&n.ref, -1) == 0 {
599 + n.r.delete(n)
600 + }
601 +}
602 +
603 +func (n *Node) unrefLocked() {
604 + if atomic.AddInt32(&n.ref, -1) == 0 {
605 + n.r.mu.RLock()
606 + if !n.r.closed {
607 + n.r.delete(n)
608 + }
609 + n.r.mu.RUnlock()
610 }
611 }
612 +
613 +type Handle struct {
614 + n unsafe.Pointer // *Node
615 +}
616 +
617 +func (h *Handle) Value() Value {
618 + n := (*Node)(atomic.LoadPointer(&h.n))
619 + if n != nil {
620 + return n.value
621 + }
622 + return nil
623 +}
624 +
625 +func (h *Handle) Release() {
626 + nPtr := atomic.LoadPointer(&h.n)
627 + if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) {
628 + n := (*Node)(nPtr)
629 + n.unrefLocked()
630 + }
631 +}
632 +
633 +func murmur32(ns, key uint64, seed uint32) uint32 {
634 + const (
635 + m = uint32(0x5bd1e995)
636 + r = 24
637 + )
638 +
639 + k1 := uint32(ns >> 32)
640 + k2 := uint32(ns)
641 + k3 := uint32(key >> 32)
642 + k4 := uint32(key)
643 +
644 + k1 *= m
645 + k1 ^= k1 >> r
646 + k1 *= m
647 +
648 + k2 *= m
649 + k2 ^= k2 >> r
650 + k2 *= m
651 +
652 + k3 *= m
653 + k3 ^= k3 >> r
654 + k3 *= m
655 +
656 + k4 *= m
657 + k4 ^= k4 >> r
658 + k4 *= m
659 +
660 + h := seed
661 +
662 + h *= m
663 + h ^= k1
664 + h *= m
665 + h ^= k2
666 + h *= m
667 + h ^= k3
668 + h *= m
669 + h ^= k4
670 +
671 + h ^= h >> 13
672 + h *= m
673 + h ^= h >> 15
674 +
675 + return h
676 +}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache_test.go
+359 -460
@@ -13,11 +13,26 @@ import (
13 "sync/atomic"
14 "testing"
15 "time"
16 + "unsafe"
17 )
18
19 +type int32o int32
20 +
21 +func (o *int32o) acquire() {
22 + if atomic.AddInt32((*int32)(o), 1) != 1 {
23 + panic("BUG: invalid ref")
24 + }
25 +}
26 +
27 +func (o *int32o) Release() {
28 + if atomic.AddInt32((*int32)(o), -1) != 0 {
29 + panic("BUG: invalid ref")
30 + }
31 +}
32 +
33 type releaserFunc struct {
34 fn func()
20 - value interface{}
35 + value Value
36 }
37
38 func (r releaserFunc) Release() {
@@ -26,8 +41,8 @@ func (r releaserFunc) Release() {
41 }
42 }
43
29 -func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) Handle {
30 - return ns.Get(key, func() (int, interface{}) {
44 +func set(c *Cache, ns, key uint64, value Value, charge int, relf func()) *Handle {
45 + return c.Get(ns, key, func() (int, Value) {
46 if relf != nil {
47 return charge, releaserFunc{relf, value}
48 } else {
@@ -36,7 +51,246 @@ func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) H
51 })
52 }
53
39 -func TestCache_HitMiss(t *testing.T) {
54 +func TestCacheMap(t *testing.T) {
55 + runtime.GOMAXPROCS(runtime.NumCPU())
56 +
57 + nsx := []struct {
58 + nobjects, nhandles, concurrent, repeat int
59 + }{
60 + {10000, 400, 50, 3},
61 + {100000, 1000, 100, 10},
62 + }
63 +
64 + var (
65 + objects [][]int32o
66 + handles [][]unsafe.Pointer
67 + )
68 +
69 + for _, x := range nsx {
70 + objects = append(objects, make([]int32o, x.nobjects))
71 + handles = append(handles, make([]unsafe.Pointer, x.nhandles))
72 + }
73 +
74 + c := NewCache(nil)
75 +
76 + wg := new(sync.WaitGroup)
77 + var done int32
78 +
79 + for ns, x := range nsx {
80 + for i := 0; i < x.concurrent; i++ {
81 + wg.Add(1)
82 + go func(ns, i, repeat int, objects []int32o, handles []unsafe.Pointer) {
83 + defer wg.Done()
84 + r := rand.New(rand.NewSource(time.Now().UnixNano()))
85 +
86 + for j := len(objects) * repeat; j >= 0; j-- {
87 + key := uint64(r.Intn(len(objects)))
88 + h := c.Get(uint64(ns), key, func() (int, Value) {
89 + o := &objects[key]
90 + o.acquire()
91 + return 1, o
92 + })
93 + if v := h.Value().(*int32o); v != &objects[key] {
94 + t.Fatalf("#%d invalid value: want=%p got=%p", ns, &objects[key], v)
95 + }
96 + if objects[key] != 1 {
97 + t.Fatalf("#%d invalid object %d: %d", ns, key, objects[key])
98 + }
99 + if !atomic.CompareAndSwapPointer(&handles[r.Intn(len(handles))], nil, unsafe.Pointer(h)) {
100 + h.Release()
101 + }
102 + }
103 + }(ns, i, x.repeat, objects[ns], handles[ns])
104 + }
105 +
106 + go func(handles []unsafe.Pointer) {
107 + r := rand.New(rand.NewSource(time.Now().UnixNano()))
108 +
109 + for atomic.LoadInt32(&done) == 0 {
110 + i := r.Intn(len(handles))
111 + h := (*Handle)(atomic.LoadPointer(&handles[i]))
112 + if h != nil && atomic.CompareAndSwapPointer(&handles[i], unsafe.Pointer(h), nil) {
113 + h.Release()
114 + }
115 + time.Sleep(time.Millisecond)
116 + }
117 + }(handles[ns])
118 + }
119 +
120 + go func() {
121 + handles := make([]*Handle, 100000)
122 + for atomic.LoadInt32(&done) == 0 {
123 + for i := range handles {
124 + handles[i] = c.Get(999999999, uint64(i), func() (int, Value) {
125 + return 1, 1
126 + })
127 + }
128 + for _, h := range handles {
129 + h.Release()
130 + }
131 + }
132 + }()
133 +
134 + wg.Wait()
135 +
136 + atomic.StoreInt32(&done, 1)
137 +
138 + for _, handles0 := range handles {
139 + for i := range handles0 {
140 + h := (*Handle)(atomic.LoadPointer(&handles0[i]))
141 + if h != nil && atomic.CompareAndSwapPointer(&handles0[i], unsafe.Pointer(h), nil) {
142 + h.Release()
143 + }
144 + }
145 + }
146 +
147 + for ns, objects0 := range objects {
148 + for i, o := range objects0 {
149 + if o != 0 {
150 + t.Fatalf("invalid object #%d.%d: ref=%d", ns, i, o)
151 + }
152 + }
153 + }
154 +}
155 +
156 +func TestCacheMap_NodesAndSize(t *testing.T) {
157 + c := NewCache(nil)
158 + if c.Nodes() != 0 {
159 + t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
160 + }
161 + if c.Size() != 0 {
162 + t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
163 + }
164 + set(c, 0, 1, 1, 1, nil)
165 + set(c, 0, 2, 2, 2, nil)
166 + set(c, 1, 1, 3, 3, nil)
167 + set(c, 2, 1, 4, 1, nil)
168 + if c.Nodes() != 4 {
169 + t.Errorf("invalid nodes counter: want=%d got=%d", 4, c.Nodes())
170 + }
171 + if c.Size() != 7 {
172 + t.Errorf("invalid size counter: want=%d got=%d", 4, c.Size())
173 + }
174 +}
175 +
176 +func TestLRUCache_Capacity(t *testing.T) {
177 + c := NewCache(NewLRU(10))
178 + if c.Capacity() != 10 {
179 + t.Errorf("invalid capacity: want=%d got=%d", 10, c.Capacity())
180 + }
181 + set(c, 0, 1, 1, 1, nil).Release()
182 + set(c, 0, 2, 2, 2, nil).Release()
183 + set(c, 1, 1, 3, 3, nil).Release()
184 + set(c, 2, 1, 4, 1, nil).Release()
185 + set(c, 2, 2, 5, 1, nil).Release()
186 + set(c, 2, 3, 6, 1, nil).Release()
187 + set(c, 2, 4, 7, 1, nil).Release()
188 + set(c, 2, 5, 8, 1, nil).Release()
189 + if c.Nodes() != 7 {
190 + t.Errorf("invalid nodes counter: want=%d got=%d", 7, c.Nodes())
191 + }
192 + if c.Size() != 10 {
193 + t.Errorf("invalid size counter: want=%d got=%d", 10, c.Size())
194 + }
195 + c.SetCapacity(9)
196 + if c.Capacity() != 9 {
197 + t.Errorf("invalid capacity: want=%d got=%d", 9, c.Capacity())
198 + }
199 + if c.Nodes() != 6 {
200 + t.Errorf("invalid nodes counter: want=%d got=%d", 6, c.Nodes())
201 + }
202 + if c.Size() != 8 {
203 + t.Errorf("invalid size counter: want=%d got=%d", 8, c.Size())
204 + }
205 +}
206 +
207 +func TestCacheMap_NilValue(t *testing.T) {
208 + c := NewCache(NewLRU(10))
209 + h := c.Get(0, 0, func() (size int, value Value) {
210 + return 1, nil
211 + })
212 + if h != nil {
213 + t.Error("cache handle is non-nil")
214 + }
215 + if c.Nodes() != 0 {
216 + t.Errorf("invalid nodes counter: want=%d got=%d", 0, c.Nodes())
217 + }
218 + if c.Size() != 0 {
219 + t.Errorf("invalid size counter: want=%d got=%d", 0, c.Size())
220 + }
221 +}
222 +
223 +func TestLRUCache_GetLatency(t *testing.T) {
224 + runtime.GOMAXPROCS(runtime.NumCPU())
225 +
226 + const (
227 + concurrentSet = 30
228 + concurrentGet = 3
229 + duration = 3 * time.Second
230 + delay = 3 * time.Millisecond
231 + maxkey = 100000
232 + )
233 +
234 + var (
235 + set, getHit, getAll int32
236 + getMaxLatency, getDuration int64
237 + )
238 +
239 + c := NewCache(NewLRU(5000))
240 + wg := &sync.WaitGroup{}
241 + until := time.Now().Add(duration)
242 + for i := 0; i < concurrentSet; i++ {
243 + wg.Add(1)
244 + go func(i int) {
245 + defer wg.Done()
246 + r := rand.New(rand.NewSource(time.Now().UnixNano()))
247 + for time.Now().Before(until) {
248 + c.Get(0, uint64(r.Intn(maxkey)), func() (int, Value) {
249 + time.Sleep(delay)
250 + atomic.AddInt32(&set, 1)
251 + return 1, 1
252 + }).Release()
253 + }
254 + }(i)
255 + }
256 + for i := 0; i < concurrentGet; i++ {
257 + wg.Add(1)
258 + go func(i int) {
259 + defer wg.Done()
260 + r := rand.New(rand.NewSource(time.Now().UnixNano()))
261 + for {
262 + mark := time.Now()
263 + if mark.Before(until) {
264 + h := c.Get(0, uint64(r.Intn(maxkey)), nil)
265 + latency := int64(time.Now().Sub(mark))
266 + m := atomic.LoadInt64(&getMaxLatency)
267 + if latency > m {
268 + atomic.CompareAndSwapInt64(&getMaxLatency, m, latency)
269 + }
270 + atomic.AddInt64(&getDuration, latency)
271 + if h != nil {
272 + atomic.AddInt32(&getHit, 1)
273 + h.Release()
274 + }
275 + atomic.AddInt32(&getAll, 1)
276 + } else {
277 + break
278 + }
279 + }
280 + }(i)
281 + }
282 +
283 + wg.Wait()
284 + getAvglatency := time.Duration(getDuration) / time.Duration(getAll)
285 + t.Logf("set=%d getHit=%d getAll=%d getMaxLatency=%v getAvgLatency=%v",
286 + set, getHit, getAll, time.Duration(getMaxLatency), getAvglatency)
287 +
288 + if getAvglatency > delay/3 {
289 + t.Errorf("get avg latency > %v: got=%v", delay/3, getAvglatency)
290 + }
291 +}
292 +
293 +func TestLRUCache_HitMiss(t *testing.T) {
294 cases := []struct {
295 key uint64
296 value string
@@ -54,14 +308,13 @@ func TestCache_HitMiss(t *testing.T) {
308 }
309
310 setfin := 0
57 - c := NewLRUCache(1000)
58 - ns := c.GetNamespace(0)
311 + c := NewCache(NewLRU(1000))
312 for i, x := range cases {
60 - set(ns, x.key, x.value, len(x.value), func() {
313 + set(c, 0, x.key, x.value, len(x.value), func() {
314 setfin++
315 }).Release()
316 for j, y := range cases {
64 - h := ns.Get(y.key, nil)
317 + h := c.Get(0, y.key, nil)
318 if j <= i {
319 // should hit
320 if h == nil {
@@ -85,7 +338,7 @@ func TestCache_HitMiss(t *testing.T) {
338
339 for i, x := range cases {
340 finalizerOk := false
88 - ns.Delete(x.key, func(exist, pending bool) {
341 + c.Delete(0, x.key, func() {
342 finalizerOk = true
343 })
344
@@ -94,7 +347,7 @@ func TestCache_HitMiss(t *testing.T) {
347 }
348
349 for j, y := range cases {
97 - h := ns.Get(y.key, nil)
350 + h := c.Get(0, y.key, nil)
351 if j > i {
352 // should hit
353 if h == nil {
@@ -122,20 +375,19 @@ func TestCache_HitMiss(t *testing.T) {
375 }
376
377 func TestLRUCache_Eviction(t *testing.T) {
125 - c := NewLRUCache(12)
126 - ns := c.GetNamespace(0)
127 - o1 := set(ns, 1, 1, 1, nil)
128 - set(ns, 2, 2, 1, nil).Release()
129 - set(ns, 3, 3, 1, nil).Release()
130 - set(ns, 4, 4, 1, nil).Release()
131 - set(ns, 5, 5, 1, nil).Release()
132 - if h := ns.Get(2, nil); h != nil { // 1,3,4,5,2
378 + c := NewCache(NewLRU(12))
379 + o1 := set(c, 0, 1, 1, 1, nil)
380 + set(c, 0, 2, 2, 1, nil).Release()
381 + set(c, 0, 3, 3, 1, nil).Release()
382 + set(c, 0, 4, 4, 1, nil).Release()
383 + set(c, 0, 5, 5, 1, nil).Release()
384 + if h := c.Get(0, 2, nil); h != nil { // 1,3,4,5,2
385 h.Release()
386 }
135 - set(ns, 9, 9, 10, nil).Release() // 5,2,9
387 + set(c, 0, 9, 9, 10, nil).Release() // 5,2,9
388
389 for _, key := range []uint64{9, 2, 5, 1} {
138 - h := ns.Get(key, nil)
390 + h := c.Get(0, key, nil)
391 if h == nil {
392 t.Errorf("miss for key '%d'", key)
393 } else {
@@ -147,7 +399,7 @@ func TestLRUCache_Eviction(t *testing.T) {
399 }
400 o1.Release()
401 for _, key := range []uint64{1, 2, 5} {
150 - h := ns.Get(key, nil)
402 + h := c.Get(0, key, nil)
403 if h == nil {
404 t.Errorf("miss for key '%d'", key)
405 } else {
@@ -158,7 +410,7 @@ func TestLRUCache_Eviction(t *testing.T) {
410 }
411 }
412 for _, key := range []uint64{3, 4, 9} {
161 - h := ns.Get(key, nil)
413 + h := c.Get(0, key, nil)
414 if h != nil {
415 t.Errorf("hit for key '%d'", key)
416 if x := h.Value().(int); x != int(key) {
@@ -169,487 +421,134 @@ func TestLRUCache_Eviction(t *testing.T) {
421 }
422 }
423
172 -func TestLRUCache_SetGet(t *testing.T) {
173 - c := NewLRUCache(13)
174 - ns := c.GetNamespace(0)
175 - for i := 0; i < 200; i++ {
176 - n := uint64(rand.Intn(99999) % 20)
177 - set(ns, n, n, 1, nil).Release()
178 - if h := ns.Get(n, nil); h != nil {
179 - if h.Value() == nil {
180 - t.Errorf("key '%d' contains nil value", n)
424 +func TestLRUCache_Evict(t *testing.T) {
425 + c := NewCache(NewLRU(6))
426 + set(c, 0, 1, 1, 1, nil).Release()
427 + set(c, 0, 2, 2, 1, nil).Release()
428 + set(c, 1, 1, 4, 1, nil).Release()
429 + set(c, 1, 2, 5, 1, nil).Release()
430 + set(c, 2, 1, 6, 1, nil).Release()
431 + set(c, 2, 2, 7, 1, nil).Release()
432 +
433 + for ns := 0; ns < 3; ns++ {
434 + for key := 1; key < 3; key++ {
435 + if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
436 + h.Release()
437 } else {
182 - if x := h.Value().(uint64); x != n {
183 - t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, x)
184 - }
438 + t.Errorf("Cache.Get on #%d.%d return nil", ns, key)
439 }
186 - h.Release()
187 - } else {
188 - t.Errorf("key '%d' doesn't exist", n)
440 }
441 }
191 -}
442
193 -func TestLRUCache_Purge(t *testing.T) {
194 - c := NewLRUCache(3)
195 - ns1 := c.GetNamespace(0)
196 - o1 := set(ns1, 1, 1, 1, nil)
197 - o2 := set(ns1, 2, 2, 1, nil)
198 - ns1.Purge(nil)
199 - set(ns1, 3, 3, 1, nil).Release()
200 - for _, key := range []uint64{1, 2, 3} {
201 - h := ns1.Get(key, nil)
202 - if h == nil {
203 - t.Errorf("miss for key '%d'", key)
204 - } else {
205 - if x := h.Value().(int); x != int(key) {
206 - t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
207 - }
208 - h.Release()
209 - }
443 + if ok := c.Evict(0, 1); !ok {
444 + t.Error("first Cache.Evict on #0.1 return false")
445 }
211 - o1.Release()
212 - o2.Release()
213 - for _, key := range []uint64{1, 2} {
214 - h := ns1.Get(key, nil)
215 - if h != nil {
216 - t.Errorf("hit for key '%d'", key)
217 - if x := h.Value().(int); x != int(key) {
218 - t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
219 - }
220 - h.Release()
221 - }
446 + if ok := c.Evict(0, 1); ok {
447 + t.Error("second Cache.Evict on #0.1 return true")
448 }
223 -}
224 -
225 -type testingCacheObjectCounter struct {
226 - created uint
227 - released uint
228 -}
229 -
230 -func (c *testingCacheObjectCounter) createOne() {
231 - c.created++
232 -}
233 -
234 -func (c *testingCacheObjectCounter) releaseOne() {
235 - c.released++
236 -}
237 -
238 -type testingCacheObject struct {
239 - t *testing.T
240 - cnt *testingCacheObjectCounter
241 -
242 - ns, key uint64
243 -
244 - releaseCalled bool
245 -}
246 -
247 -func (x *testingCacheObject) Release() {
248 - if !x.releaseCalled {
249 - x.releaseCalled = true
250 - x.cnt.releaseOne()
251 - } else {
252 - x.t.Errorf("duplicate setfin NS#%d KEY#%d", x.ns, x.key)
449 + if h := c.Get(0, 1, nil); h != nil {
450 + t.Errorf("Cache.Get on #0.1 return non-nil: %v", h.Value())
451 }
254 -}
255 -
256 -func TestLRUCache_ConcurrentSetGet(t *testing.T) {
257 - runtime.GOMAXPROCS(runtime.NumCPU())
258 -
259 - seed := time.Now().UnixNano()
260 - t.Logf("seed=%d", seed)
452
262 - const (
263 - N = 2000000
264 - M = 4000
265 - C = 3
266 - )
267 -
268 - var set, get uint32
269 -
270 - wg := &sync.WaitGroup{}
271 - c := NewLRUCache(M / 4)
272 - for ni := uint64(0); ni < C; ni++ {
273 - r0 := rand.New(rand.NewSource(seed + int64(ni)))
274 - r1 := rand.New(rand.NewSource(seed + int64(ni) + 1))
275 - ns := c.GetNamespace(ni)
276 -
277 - wg.Add(2)
278 - go func(ns Namespace, r *rand.Rand) {
279 - for i := 0; i < N; i++ {
280 - x := uint64(r.Int63n(M))
281 - o := ns.Get(x, func() (int, interface{}) {
282 - atomic.AddUint32(&set, 1)
283 - return 1, x
284 - })
285 - if v := o.Value().(uint64); v != x {
286 - t.Errorf("#%d invalid value, got=%d", x, v)
287 - }
288 - o.Release()
289 - }
290 - wg.Done()
291 - }(ns, r0)
292 - go func(ns Namespace, r *rand.Rand) {
293 - for i := 0; i < N; i++ {
294 - x := uint64(r.Int63n(M))
295 - o := ns.Get(x, nil)
296 - if o != nil {
297 - atomic.AddUint32(&get, 1)
298 - if v := o.Value().(uint64); v != x {
299 - t.Errorf("#%d invalid value, got=%d", x, v)
300 - }
301 - o.Release()
302 - }
303 - }
304 - wg.Done()
305 - }(ns, r1)
453 + c.EvictNS(1)
454 + if h := c.Get(1, 1, nil); h != nil {
455 + t.Errorf("Cache.Get on #1.1 return non-nil: %v", h.Value())
456 }
307 -
308 - wg.Wait()
309 -
310 - t.Logf("set=%d get=%d", set, get)
311 -}
312 -
313 -func TestLRUCache_Finalizer(t *testing.T) {
314 - const (
315 - capacity = 100
316 - goroutines = 100
317 - iterations = 10000
318 - keymax = 8000
319 - )
320 -
321 - cnt := &testingCacheObjectCounter{}
322 -
323 - c := NewLRUCache(capacity)
324 -
325 - type instance struct {
326 - seed int64
327 - rnd *rand.Rand
328 - nsid uint64
329 - ns Namespace
330 - effective int
331 - handles []Handle
332 - handlesMap map[uint64]int
333 -
334 - delete bool
335 - purge bool
336 - zap bool
337 - wantDel int
338 - delfinCalled int
339 - delfinCalledAll int
340 - delfinCalledEff int
341 - purgefinCalled int
342 - }
343 -
344 - instanceGet := func(p *instance, key uint64) {
345 - h := p.ns.Get(key, func() (charge int, value interface{}) {
346 - to := &testingCacheObject{
347 - t: t, cnt: cnt,
348 - ns: p.nsid,
349 - key: key,
350 - }
351 - p.effective++
352 - cnt.createOne()
353 - return 1, releaserFunc{func() {
354 - to.Release()
355 - p.effective--
356 - }, to}
357 - })
358 - p.handles = append(p.handles, h)
359 - p.handlesMap[key] = p.handlesMap[key] + 1
360 - }
361 - instanceRelease := func(p *instance, i int) {
362 - h := p.handles[i]
363 - key := h.Value().(releaserFunc).value.(*testingCacheObject).key
364 - if n := p.handlesMap[key]; n == 0 {
365 - t.Fatal("key ref == 0")
366 - } else if n > 1 {
367 - p.handlesMap[key] = n - 1
368 - } else {
369 - delete(p.handlesMap, key)
370 - }
371 - h.Release()
372 - p.handles = append(p.handles[:i], p.handles[i+1:]...)
373 - p.handles[len(p.handles) : len(p.handles)+1][0] = nil
374 - }
375 -
376 - seed := time.Now().UnixNano()
377 - t.Logf("seed=%d", seed)
378 -
379 - instances := make([]*instance, goroutines)
380 - for i := range instances {
381 - p := &instance{}
382 - p.handlesMap = make(map[uint64]int)
383 - p.seed = seed + int64(i)
384 - p.rnd = rand.New(rand.NewSource(p.seed))
385 - p.nsid = uint64(i)
386 - p.ns = c.GetNamespace(p.nsid)
387 - p.delete = i%6 == 0
388 - p.purge = i%8 == 0
389 - p.zap = i%12 == 0 || i%3 == 0
390 - instances[i] = p
391 - }
392 -
393 - runr := rand.New(rand.NewSource(seed - 1))
394 - run := func(rnd *rand.Rand, x []*instance, init func(p *instance) bool, fn func(p *instance, i int) bool) {
395 - var (
396 - rx []*instance
397 - rn []int
398 - )
399 - if init == nil {
400 - rx = append([]*instance{}, x...)
401 - rn = make([]int, len(x))
402 - } else {
403 - for _, p := range x {
404 - if init(p) {
405 - rx = append(rx, p)
406 - rn = append(rn, 0)
407 - }
408 - }
409 - }
410 - for len(rx) > 0 {
411 - i := rand.Intn(len(rx))
412 - if fn(rx[i], rn[i]) {
413 - rn[i]++
414 - } else {
415 - rx = append(rx[:i], rx[i+1:]...)
416 - rn = append(rn[:i], rn[i+1:]...)
417 - }
418 - }
457 + if h := c.Get(1, 2, nil); h != nil {
458 + t.Errorf("Cache.Get on #1.2 return non-nil: %v", h.Value())
459 }
460
421 - // Get and release.
422 - run(runr, instances, nil, func(p *instance, i int) bool {
423 - if i < iterations {
424 - if len(p.handles) == 0 || p.rnd.Int()%2 == 0 {
425 - instanceGet(p, uint64(p.rnd.Intn(keymax)))
426 - } else {
427 - instanceRelease(p, p.rnd.Intn(len(p.handles)))
461 + c.EvictAll()
462 + for ns := 0; ns < 3; ns++ {
463 + for key := 1; key < 3; key++ {
464 + if h := c.Get(uint64(ns), uint64(key), nil); h != nil {
465 + t.Errorf("Cache.Get on #%d.%d return non-nil: %v", ns, key, h.Value())
466 }
429 - return true
430 - } else {
431 - return false
432 - }
433 - })
434 -
435 - if used, cap := c.Used(), c.Capacity(); used > cap {
436 - t.Errorf("Used > capacity, used=%d cap=%d", used, cap)
437 - }
438 -
439 - // Check effective objects.
440 - for i, p := range instances {
441 - if int(p.effective) < len(p.handlesMap) {
442 - t.Errorf("#%d effective objects < acquired handle, eo=%d ah=%d", i, p.effective, len(p.handlesMap))
467 }
468 }
469 +}
470
446 - if want := int(cnt.created - cnt.released); c.Size() != want {
447 - t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
471 +func TestLRUCache_Delete(t *testing.T) {
472 + delFuncCalled := 0
473 + delFunc := func() {
474 + delFuncCalled++
475 }
476
450 - // First delete.
451 - run(runr, instances, func(p *instance) bool {
452 - p.wantDel = p.effective
453 - return p.delete
454 - }, func(p *instance, i int) bool {
455 - key := uint64(i)
456 - if key < keymax {
457 - _, wantExist := p.handlesMap[key]
458 - gotExist := p.ns.Delete(key, func(exist, pending bool) {
459 - p.delfinCalledAll++
460 - if exist {
461 - p.delfinCalledEff++
462 - }
463 - })
464 - if !gotExist && wantExist {
465 - t.Errorf("delete on NS#%d KEY#%d not found", p.nsid, key)
466 - }
467 - return true
468 - } else {
469 - return false
470 - }
471 - })
472 -
473 - // Second delete.
474 - run(runr, instances, func(p *instance) bool {
475 - p.delfinCalled = 0
476 - return p.delete
477 - }, func(p *instance, i int) bool {
478 - key := uint64(i)
479 - if key < keymax {
480 - gotExist := p.ns.Delete(key, func(exist, pending bool) {
481 - if exist && !pending {
482 - t.Errorf("delete fin on NS#%d KEY#%d exist and not pending for deletion", p.nsid, key)
483 - }
484 - p.delfinCalled++
485 - })
486 - if gotExist {
487 - t.Errorf("delete on NS#%d KEY#%d found", p.nsid, key)
488 - }
489 - return true
490 - } else {
491 - if p.delfinCalled != keymax {
492 - t.Errorf("(2) NS#%d not all delete fin called, diff=%d", p.nsid, keymax-p.delfinCalled)
493 - }
494 - return false
495 - }
496 - })
477 + c := NewCache(NewLRU(2))
478 + set(c, 0, 1, 1, 1, nil).Release()
479 + set(c, 0, 2, 2, 1, nil).Release()
480
498 - // Purge.
499 - run(runr, instances, func(p *instance) bool {
500 - return p.purge
501 - }, func(p *instance, i int) bool {
502 - p.ns.Purge(func(ns, key uint64) {
503 - p.purgefinCalled++
504 - })
505 - return false
506 - })
507 -
508 - if want := int(cnt.created - cnt.released); c.Size() != want {
509 - t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
481 + if ok := c.Delete(0, 1, delFunc); !ok {
482 + t.Error("Cache.Delete on #1 return false")
483 }
511 -
512 - // Release.
513 - run(runr, instances, func(p *instance) bool {
514 - return !p.zap
515 - }, func(p *instance, i int) bool {
516 - if len(p.handles) > 0 {
517 - instanceRelease(p, len(p.handles)-1)
518 - return true
519 - } else {
520 - return false
521 - }
522 - })
523 -
524 - if want := int(cnt.created - cnt.released); c.Size() != want {
525 - t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
484 + if h := c.Get(0, 1, nil); h != nil {
485 + t.Errorf("Cache.Get on #1 return non-nil: %v", h.Value())
486 }
527 -
528 - // Zap.
529 - run(runr, instances, func(p *instance) bool {
530 - return p.zap
531 - }, func(p *instance, i int) bool {
532 - p.ns.Zap()
533 - p.handles = nil
534 - p.handlesMap = nil
535 - return false
536 - })
537 -
538 - if want := int(cnt.created - cnt.released); c.Size() != want {
539 - t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
487 + if ok := c.Delete(0, 1, delFunc); ok {
488 + t.Error("Cache.Delete on #1 return true")
489 }
490
542 - if notrel, used := int(cnt.created-cnt.released), c.Used(); notrel != used {
543 - t.Errorf("Invalid used value, want=%d got=%d", notrel, used)
491 + h2 := c.Get(0, 2, nil)
492 + if h2 == nil {
493 + t.Error("Cache.Get on #2 return nil")
494 + }
495 + if ok := c.Delete(0, 2, delFunc); !ok {
496 + t.Error("(1) Cache.Delete on #2 return false")
497 + }
498 + if ok := c.Delete(0, 2, delFunc); !ok {
499 + t.Error("(2) Cache.Delete on #2 return false")
500 }
501
546 - c.Purge(nil)
502 + set(c, 0, 3, 3, 1, nil).Release()
503 + set(c, 0, 4, 4, 1, nil).Release()
504 + c.Get(0, 2, nil).Release()
505
548 - for _, p := range instances {
549 - if p.delete {
550 - if p.delfinCalledAll != keymax {
551 - t.Errorf("#%d not all delete fin called, purge=%v zap=%v diff=%d", p.nsid, p.purge, p.zap, keymax-p.delfinCalledAll)
552 - }
553 - if p.delfinCalledEff != p.wantDel {
554 - t.Errorf("#%d not all effective delete fin called, diff=%d", p.nsid, p.wantDel-p.delfinCalledEff)
555 - }
556 - if p.purge && p.purgefinCalled > 0 {
557 - t.Errorf("#%d some purge fin called, delete=%v zap=%v n=%d", p.nsid, p.delete, p.zap, p.purgefinCalled)
558 - }
506 + for key := 2; key <= 4; key++ {
507 + if h := c.Get(0, uint64(key), nil); h != nil {
508 + h.Release()
509 } else {
560 - if p.purge {
561 - if p.purgefinCalled != p.wantDel {
562 - t.Errorf("#%d not all purge fin called, delete=%v zap=%v diff=%d", p.nsid, p.delete, p.zap, p.wantDel-p.purgefinCalled)
563 - }
564 - }
510 + t.Errorf("Cache.Get on #%d return nil", key)
511 }
512 }
513
568 - if cnt.created != cnt.released {
569 - t.Errorf("Some cache object weren't released, created=%d released=%d", cnt.created, cnt.released)
514 + h2.Release()
515 + if h := c.Get(0, 2, nil); h != nil {
516 + t.Errorf("Cache.Get on #2 return non-nil: %v", h.Value())
517 }
571 -}
518
573 -func BenchmarkLRUCache_Set(b *testing.B) {
574 - c := NewLRUCache(0)
575 - ns := c.GetNamespace(0)
576 - b.ResetTimer()
577 - for i := uint64(0); i < uint64(b.N); i++ {
578 - set(ns, i, "", 1, nil)
519 + if delFuncCalled != 4 {
520 + t.Errorf("delFunc isn't called 4 times: got=%d", delFuncCalled)
521 }
522 }
523
582 -func BenchmarkLRUCache_Get(b *testing.B) {
583 - c := NewLRUCache(0)
584 - ns := c.GetNamespace(0)
585 - b.ResetTimer()
586 - for i := uint64(0); i < uint64(b.N); i++ {
587 - set(ns, i, "", 1, nil)
524 +func TestLRUCache_Close(t *testing.T) {
525 + relFuncCalled := 0
526 + relFunc := func() {
527 + relFuncCalled++
528 }
589 - b.ResetTimer()
590 - for i := uint64(0); i < uint64(b.N); i++ {
591 - ns.Get(i, nil)
529 + delFuncCalled := 0
530 + delFunc := func() {
531 + delFuncCalled++
532 }
593 -}
533
595 -func BenchmarkLRUCache_Get2(b *testing.B) {
596 - c := NewLRUCache(0)
597 - ns := c.GetNamespace(0)
598 - b.ResetTimer()
599 - for i := uint64(0); i < uint64(b.N); i++ {
600 - set(ns, i, "", 1, nil)
601 - }
602 - b.ResetTimer()
603 - for i := uint64(0); i < uint64(b.N); i++ {
604 - ns.Get(i, func() (charge int, value interface{}) {
605 - return 0, nil
606 - })
607 - }
608 -}
534 + c := NewCache(NewLRU(2))
535 + set(c, 0, 1, 1, 1, relFunc).Release()
536 + set(c, 0, 2, 2, 1, relFunc).Release()
537
610 -func BenchmarkLRUCache_Release(b *testing.B) {
611 - c := NewLRUCache(0)
612 - ns := c.GetNamespace(0)
613 - handles := make([]Handle, b.N)
614 - for i := uint64(0); i < uint64(b.N); i++ {
615 - handles[i] = set(ns, i, "", 1, nil)
538 + h3 := set(c, 0, 3, 3, 1, relFunc)
539 + if h3 == nil {
540 + t.Error("Cache.Get on #3 return nil")
541 }
617 - b.ResetTimer()
618 - for _, h := range handles {
619 - h.Release()
542 + if ok := c.Delete(0, 3, delFunc); !ok {
543 + t.Error("Cache.Delete on #3 return false")
544 }
621 -}
545
623 -func BenchmarkLRUCache_SetRelease(b *testing.B) {
624 - capacity := b.N / 100
625 - if capacity <= 0 {
626 - capacity = 10
627 - }
628 - c := NewLRUCache(capacity)
629 - ns := c.GetNamespace(0)
630 - b.ResetTimer()
631 - for i := uint64(0); i < uint64(b.N); i++ {
632 - set(ns, i, "", 1, nil).Release()
633 - }
634 -}
635 -
636 -func BenchmarkLRUCache_SetReleaseTwice(b *testing.B) {
637 - capacity := b.N / 100
638 - if capacity <= 0 {
639 - capacity = 10
640 - }
641 - c := NewLRUCache(capacity)
642 - ns := c.GetNamespace(0)
643 - b.ResetTimer()
644 -
645 - na := b.N / 2
646 - nb := b.N - na
546 + c.Close()
547
648 - for i := uint64(0); i < uint64(na); i++ {
649 - set(ns, i, "", 1, nil).Release()
548 + if relFuncCalled != 3 {
549 + t.Errorf("relFunc isn't called 3 times: got=%d", relFuncCalled)
550 }
651 -
652 - for i := uint64(0); i < uint64(nb); i++ {
653 - set(ns, i, "", 1, nil).Release()
551 + if delFuncCalled != 1 {
552 + t.Errorf("delFunc isn't called 1 times: got=%d", delFuncCalled)
553 }
554 }
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru.go new
+195
@@ -0,0 +1,195 @@
1 +// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2 +// All rights reserved.
3 +//
4 +// Use of this source code is governed by a BSD-style license that can be
5 +// found in the LICENSE file.
6 +
7 +package cache
8 +
9 +import (
10 + "sync"
11 + "unsafe"
12 +)
13 +
14 +type lruNode struct {
15 + n *Node
16 + h *Handle
17 + ban bool
18 +
19 + next, prev *lruNode
20 +}
21 +
22 +func (n *lruNode) insert(at *lruNode) {
23 + x := at.next
24 + at.next = n
25 + n.prev = at
26 + n.next = x
27 + x.prev = n
28 +}
29 +
30 +func (n *lruNode) remove() {
31 + if n.prev != nil {
32 + n.prev.next = n.next
33 + n.next.prev = n.prev
34 + n.prev = nil
35 + n.next = nil
36 + } else {
37 + panic("BUG: removing removed node")
38 + }
39 +}
40 +
41 +type lru struct {
42 + mu sync.Mutex
43 + capacity int
44 + used int
45 + recent lruNode
46 +}
47 +
48 +func (r *lru) reset() {
49 + r.recent.next = &r.recent
50 + r.recent.prev = &r.recent
51 + r.used = 0
52 +}
53 +
54 +func (r *lru) Capacity() int {
55 + r.mu.Lock()
56 + defer r.mu.Unlock()
57 + return r.capacity
58 +}
59 +
60 +func (r *lru) SetCapacity(capacity int) {
61 + var evicted []*lruNode
62 +
63 + r.mu.Lock()
64 + r.capacity = capacity
65 + for r.used > r.capacity {
66 + rn := r.recent.prev
67 + if rn == nil {
68 + panic("BUG: invalid LRU used or capacity counter")
69 + }
70 + rn.remove()
71 + rn.n.CacheData = nil
72 + r.used -= rn.n.Size()
73 + evicted = append(evicted, rn)
74 + }
75 + r.mu.Unlock()
76 +
77 + for _, rn := range evicted {
78 + rn.h.Release()
79 + }
80 +}
81 +
82 +func (r *lru) Promote(n *Node) {
83 + var evicted []*lruNode
84 +
85 + r.mu.Lock()
86 + if n.CacheData == nil {
87 + if n.Size() <= r.capacity {
88 + rn := &lruNode{n: n, h: n.GetHandle()}
89 + rn.insert(&r.recent)
90 + n.CacheData = unsafe.Pointer(rn)
91 + r.used += n.Size()
92 +
93 + for r.used > r.capacity {
94 + rn := r.recent.prev
95 + if rn == nil {
96 + panic("BUG: invalid LRU used or capacity counter")
97 + }
98 + rn.remove()
99 + rn.n.CacheData = nil
100 + r.used -= rn.n.Size()
101 + evicted = append(evicted, rn)
102 + }
103 + }
104 + } else {
105 + rn := (*lruNode)(n.CacheData)
106 + if !rn.ban {
107 + rn.remove()
108 + rn.insert(&r.recent)
109 + }
110 + }
111 + r.mu.Unlock()
112 +
113 + for _, rn := range evicted {
114 + rn.h.Release()
115 + }
116 +}
117 +
118 +func (r *lru) Ban(n *Node) {
119 + r.mu.Lock()
120 + if n.CacheData == nil {
121 + n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
122 + } else {
123 + rn := (*lruNode)(n.CacheData)
124 + if !rn.ban {
125 + rn.remove()
126 + rn.ban = true
127 + r.used -= rn.n.Size()
128 + r.mu.Unlock()
129 +
130 + rn.h.Release()
131 + rn.h = nil
132 + return
133 + }
134 + }
135 + r.mu.Unlock()
136 +}
137 +
138 +func (r *lru) Evict(n *Node) {
139 + r.mu.Lock()
140 + rn := (*lruNode)(n.CacheData)
141 + if rn == nil || rn.ban {
142 + r.mu.Unlock()
143 + return
144 + }
145 + n.CacheData = nil
146 + r.mu.Unlock()
147 +
148 + rn.h.Release()
149 +}
150 +
151 +func (r *lru) EvictNS(ns uint64) {
152 + var evicted []*lruNode
153 +
154 + r.mu.Lock()
155 + for e := r.recent.prev; e != &r.recent; {
156 + rn := e
157 + e = e.prev
158 + if rn.n.NS() == ns {
159 + rn.remove()
160 + rn.n.CacheData = nil
161 + r.used -= rn.n.Size()
162 + evicted = append(evicted, rn)
163 + }
164 + }
165 + r.mu.Unlock()
166 +
167 + for _, rn := range evicted {
168 + rn.h.Release()
169 + }
170 +}
171 +
172 +func (r *lru) EvictAll() {
173 + r.mu.Lock()
174 + back := r.recent.prev
175 + for rn := back; rn != &r.recent; rn = rn.prev {
176 + rn.n.CacheData = nil
177 + }
178 + r.reset()
179 + r.mu.Unlock()
180 +
181 + for rn := back; rn != &r.recent; rn = rn.prev {
182 + rn.h.Release()
183 + }
184 +}
185 +
186 +func (r *lru) Close() error {
187 + return nil
188 +}
189 +
190 +// NewLRU create a new LRU-cache.
191 +func NewLRU(capacity int) Cacher {
192 + r := &lru{capacity: capacity}
193 + r.reset()
194 + return r
195 +}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru_cache.go deleted
-622
@@ -1,622 +0,0 @@
1 -// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2 -// All rights reserved.
3 -//
4 -// Use of this source code is governed by a BSD-style license that can be
5 -// found in the LICENSE file.
6 -
7 -package cache
8 -
9 -import (
10 - "sync"
11 - "sync/atomic"
12 -
13 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
14 -)
15 -
16 -// The LLRB implementation were taken from https://github.com/petar/GoLLRB.
17 -// Which contains the following header:
18 -//
19 -// Copyright 2010 Petar Maymounkov. All rights reserved.
20 -// Use of this source code is governed by a BSD-style
21 -// license that can be found in the LICENSE file.
22 -
23 -// lruCache represent a LRU cache state.
24 -type lruCache struct {
25 - mu sync.Mutex
26 - recent lruNode
27 - table map[uint64]*lruNs
28 - capacity int
29 - used, size, alive int
30 -}
31 -
32 -// NewLRUCache creates a new initialized LRU cache with the given capacity.
33 -func NewLRUCache(capacity int) Cache {
34 - c := &lruCache{
35 - table: make(map[uint64]*lruNs),
36 - capacity: capacity,
37 - }
38 - c.recent.rNext = &c.recent
39 - c.recent.rPrev = &c.recent
40 - return c
41 -}
42 -
43 -func (c *lruCache) Capacity() int {
44 - c.mu.Lock()
45 - defer c.mu.Unlock()
46 - return c.capacity
47 -}
48 -
49 -func (c *lruCache) Used() int {
50 - c.mu.Lock()
51 - defer c.mu.Unlock()
52 - return c.used
53 -}
54 -
55 -func (c *lruCache) Size() int {
56 - c.mu.Lock()
57 - defer c.mu.Unlock()
58 - return c.size
59 -}
60 -
61 -func (c *lruCache) NumObjects() int {
62 - c.mu.Lock()
63 - defer c.mu.Unlock()
64 - return c.alive
65 -}
66 -
67 -// SetCapacity set cache capacity.
68 -func (c *lruCache) SetCapacity(capacity int) {
69 - c.mu.Lock()
70 - c.capacity = capacity
71 - c.evict()
72 - c.mu.Unlock()
73 -}
74 -
75 -// GetNamespace return namespace object for given id.
76 -func (c *lruCache) GetNamespace(id uint64) Namespace {
77 - c.mu.Lock()
78 - defer c.mu.Unlock()
79 -
80 - if ns, ok := c.table[id]; ok {
81 - return ns
82 - }
83 -
84 - ns := &lruNs{lru: c, id: id}
85 - c.table[id] = ns
86 - return ns
87 -}
88 -
89 -func (c *lruCache) ZapNamespace(id uint64) {
90 - c.mu.Lock()
91 - if ns, exist := c.table[id]; exist {
92 - ns.zapNB()
93 - delete(c.table, id)
94 - }
95 - c.mu.Unlock()
96 -}
97 -
98 -func (c *lruCache) PurgeNamespace(id uint64, fin PurgeFin) {
99 - c.mu.Lock()
100 - if ns, exist := c.table[id]; exist {
101 - ns.purgeNB(fin)
102 - }
103 - c.mu.Unlock()
104 -}
105 -
106 -// Purge purge entire cache.
107 -func (c *lruCache) Purge(fin PurgeFin) {
108 - c.mu.Lock()
109 - for _, ns := range c.table {
110 - ns.purgeNB(fin)
111 - }
112 - c.mu.Unlock()
113 -}
114 -
115 -func (c *lruCache) Zap() {
116 - c.mu.Lock()
117 - for _, ns := range c.table {
118 - ns.zapNB()
119 - }
120 - c.table = make(map[uint64]*lruNs)
121 - c.mu.Unlock()
122 -}
123 -
124 -func (c *lruCache) evict() {
125 - top := &c.recent
126 - for n := c.recent.rPrev; c.used > c.capacity && n != top; {
127 - if n.state != nodeEffective {
128 - panic("evicting non effective node")
129 - }
130 - n.state = nodeEvicted
131 - n.rRemove()
132 - n.derefNB()
133 - c.used -= n.charge
134 - n = c.recent.rPrev
135 - }
136 -}
137 -
138 -type lruNs struct {
139 - lru *lruCache
140 - id uint64
141 - rbRoot *lruNode
142 - state nsState
143 -}
144 -
145 -func (ns *lruNs) rbGetOrCreateNode(h *lruNode, key uint64) (hn, n *lruNode) {
146 - if h == nil {
147 - n = &lruNode{ns: ns, key: key}
148 - return n, n
149 - }
150 -
151 - if key < h.key {
152 - hn, n = ns.rbGetOrCreateNode(h.rbLeft, key)
153 - if hn != nil {
154 - h.rbLeft = hn
155 - } else {
156 - return nil, n
157 - }
158 - } else if key > h.key {
159 - hn, n = ns.rbGetOrCreateNode(h.rbRight, key)
160 - if hn != nil {
161 - h.rbRight = hn
162 - } else {
163 - return nil, n
164 - }
165 - } else {
166 - return nil, h
167 - }
168 -
169 - if rbIsRed(h.rbRight) && !rbIsRed(h.rbLeft) {
170 - h = rbRotLeft(h)
171 - }
172 - if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
173 - h = rbRotRight(h)
174 - }
175 - if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
176 - rbFlip(h)
177 - }
178 - return h, n
179 -}
180 -
181 -func (ns *lruNs) getOrCreateNode(key uint64) *lruNode {
182 - hn, n := ns.rbGetOrCreateNode(ns.rbRoot, key)
183 - if hn != nil {
184 - ns.rbRoot = hn
185 - ns.rbRoot.rbBlack = true
186 - }
187 - return n
188 -}
189 -
190 -func (ns *lruNs) rbGetNode(key uint64) *lruNode {
191 - h := ns.rbRoot
192 - for h != nil {
193 - switch {
194 - case key < h.key:
195 - h = h.rbLeft
196 - case key > h.key:
197 - h = h.rbRight
198 - default:
199 - return h
200 - }
201 - }
202 - return nil
203 -}
204 -
205 -func (ns *lruNs) getNode(key uint64) *lruNode {
206 - return ns.rbGetNode(key)
207 -}
208 -
209 -func (ns *lruNs) rbDeleteNode(h *lruNode, key uint64) *lruNode {
210 - if h == nil {
211 - return nil
212 - }
213 -
214 - if key < h.key {
215 - if h.rbLeft == nil { // key not present. Nothing to delete
216 - return h
217 - }
218 - if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
219 - h = rbMoveLeft(h)
220 - }
221 - h.rbLeft = ns.rbDeleteNode(h.rbLeft, key)
222 - } else {
223 - if rbIsRed(h.rbLeft) {
224 - h = rbRotRight(h)
225 - }
226 - // If @key equals @h.key and no right children at @h
227 - if h.key == key && h.rbRight == nil {
228 - return nil
229 - }
230 - if h.rbRight != nil && !rbIsRed(h.rbRight) && !rbIsRed(h.rbRight.rbLeft) {
231 - h = rbMoveRight(h)
232 - }
233 - // If @key equals @h.key, and (from above) 'h.Right != nil'
234 - if h.key == key {
235 - var x *lruNode
236 - h.rbRight, x = rbDeleteMin(h.rbRight)
237 - if x == nil {
238 - panic("logic")
239 - }
240 - x.rbLeft, h.rbLeft = h.rbLeft, nil
241 - x.rbRight, h.rbRight = h.rbRight, nil
242 - x.rbBlack = h.rbBlack
243 - h = x
244 - } else { // Else, @key is bigger than @h.key
245 - h.rbRight = ns.rbDeleteNode(h.rbRight, key)
246 - }
247 - }
248 -
249 - return rbFixup(h)
250 -}
251 -
252 -func (ns *lruNs) deleteNode(key uint64) {
253 - ns.rbRoot = ns.rbDeleteNode(ns.rbRoot, key)
254 - if ns.rbRoot != nil {
255 - ns.rbRoot.rbBlack = true
256 - }
257 -}
258 -
259 -func (ns *lruNs) rbIterateNodes(h *lruNode, pivot uint64, iter func(n *lruNode) bool) bool {
260 - if h == nil {
261 - return true
262 - }
263 - if h.key >= pivot {
264 - if !ns.rbIterateNodes(h.rbLeft, pivot, iter) {
265 - return false
266 - }
267 - if !iter(h) {
268 - return false
269 - }
270 - }
271 - return ns.rbIterateNodes(h.rbRight, pivot, iter)
272 -}
273 -
274 -func (ns *lruNs) iterateNodes(iter func(n *lruNode) bool) {
275 - ns.rbIterateNodes(ns.rbRoot, 0, iter)
276 -}
277 -
278 -func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
279 - ns.lru.mu.Lock()
280 - defer ns.lru.mu.Unlock()
281 -
282 - if ns.state != nsEffective {
283 - return nil
284 - }
285 -
286 - var n *lruNode
287 - if setf == nil {
288 - n = ns.getNode(key)
289 - if n == nil {
290 - return nil
291 - }
292 - } else {
293 - n = ns.getOrCreateNode(key)
294 - }
295 - switch n.state {
296 - case nodeZero:
297 - charge, value := setf()
298 - if value == nil {
299 - ns.deleteNode(key)
300 - return nil
301 - }
302 - if charge < 0 {
303 - charge = 0
304 - }
305 -
306 - n.value = value
307 - n.charge = charge
308 - n.state = nodeEvicted
309 -
310 - ns.lru.size += charge
311 - ns.lru.alive++
312 -
313 - fallthrough
314 - case nodeEvicted:
315 - if n.charge == 0 {
316 - break
317 - }
318 -
319 - // Insert to recent list.
320 - n.state = nodeEffective
321 - n.ref++
322 - ns.lru.used += n.charge
323 - ns.lru.evict()
324 -
325 - fallthrough
326 - case nodeEffective:
327 - // Bump to front.
328 - n.rRemove()
329 - n.rInsert(&ns.lru.recent)
330 - case nodeDeleted:
331 - // Do nothing.
332 - default:
333 - panic("invalid state")
334 - }
335 - n.ref++
336 -
337 - return &lruHandle{node: n}
338 -}
339 -
340 -func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
341 - ns.lru.mu.Lock()
342 - defer ns.lru.mu.Unlock()
343 -
344 - if ns.state != nsEffective {
345 - if fin != nil {
346 - fin(false, false)
347 - }
348 - return false
349 - }
350 -
351 - n := ns.getNode(key)
352 - if n == nil {
353 - if fin != nil {
354 - fin(false, false)
355 - }
356 - return false
357 -
358 - }
359 -
360 - switch n.state {
361 - case nodeEffective:
362 - ns.lru.used -= n.charge
363 - n.state = nodeDeleted
364 - n.delfin = fin
365 - n.rRemove()
366 - n.derefNB()
367 - case nodeEvicted:
368 - n.state = nodeDeleted
369 - n.delfin = fin
370 - case nodeDeleted:
371 - if fin != nil {
372 - fin(true, true)
373 - }
374 - return false
375 - default:
376 - panic("invalid state")
377 - }
378 -
379 - return true
380 -}
381 -
382 -func (ns *lruNs) purgeNB(fin PurgeFin) {
383 - if ns.state == nsEffective {
384 - var nodes []*lruNode
385 - ns.iterateNodes(func(n *lruNode) bool {
386 - nodes = append(nodes, n)
387 - return true
388 - })
389 - for _, n := range nodes {
390 - switch n.state {
391 - case nodeEffective:
392 - ns.lru.used -= n.charge
393 - n.state = nodeDeleted
394 - n.purgefin = fin
395 - n.rRemove()
396 - n.derefNB()
397 - case nodeEvicted:
398 - n.state = nodeDeleted
399 - n.purgefin = fin
400 - case nodeDeleted:
401 - default:
402 - panic("invalid state")
403 - }
404 - }
405 - }
406 -}
407 -
408 -func (ns *lruNs) Purge(fin PurgeFin) {
409 - ns.lru.mu.Lock()
410 - ns.purgeNB(fin)
411 - ns.lru.mu.Unlock()
412 -}
413 -
414 -func (ns *lruNs) zapNB() {
415 - if ns.state == nsEffective {
416 - ns.state = nsZapped
417 -
418 - ns.iterateNodes(func(n *lruNode) bool {
419 - if n.state == nodeEffective {
420 - ns.lru.used -= n.charge
421 - n.rRemove()
422 - }
423 - ns.lru.size -= n.charge
424 - n.state = nodeDeleted
425 - n.fin()
426 -
427 - return true
428 - })
429 - ns.rbRoot = nil
430 - }
431 -}
432 -
433 -func (ns *lruNs) Zap() {
434 - ns.lru.mu.Lock()
435 - ns.zapNB()
436 - delete(ns.lru.table, ns.id)
437 - ns.lru.mu.Unlock()
438 -}
439 -
440 -type lruNode struct {
441 - ns *lruNs
442 -
443 - rNext, rPrev *lruNode
444 - rbLeft, rbRight *lruNode
445 - rbBlack bool
446 -
447 - key uint64
448 - value interface{}
449 - charge int
450 - ref int
451 - state nodeState
452 - delfin DelFin
453 - purgefin PurgeFin
454 -}
455 -
456 -func (n *lruNode) rInsert(at *lruNode) {
457 - x := at.rNext
458 - at.rNext = n
459 - n.rPrev = at
460 - n.rNext = x
461 - x.rPrev = n
462 -}
463 -
464 -func (n *lruNode) rRemove() bool {
465 - if n.rPrev == nil {
466 - return false
467 - }
468 -
469 - n.rPrev.rNext = n.rNext
470 - n.rNext.rPrev = n.rPrev
471 - n.rPrev = nil
472 - n.rNext = nil
473 -
474 - return true
475 -}
476 -
477 -func (n *lruNode) fin() {
478 - if r, ok := n.value.(util.Releaser); ok {
479 - r.Release()
480 - }
481 - if n.purgefin != nil {
482 - if n.delfin != nil {
483 - panic("conflicting delete and purge fin")
484 - }
485 - n.purgefin(n.ns.id, n.key)
486 - n.purgefin = nil
487 - } else if n.delfin != nil {
488 - n.delfin(true, false)
489 - n.delfin = nil
490 - }
491 -}
492 -
493 -func (n *lruNode) derefNB() {
494 - n.ref--
495 - if n.ref == 0 {
496 - if n.ns.state == nsEffective {
497 - // Remove elemement.
498 - n.ns.deleteNode(n.key)
499 - n.ns.lru.size -= n.charge
500 - n.ns.lru.alive--
501 - n.fin()
502 - }
503 - n.value = nil
504 - } else if n.ref < 0 {
505 - panic("leveldb/cache: lruCache: negative node reference")
506 - }
507 -}
508 -
509 -func (n *lruNode) deref() {
510 - n.ns.lru.mu.Lock()
511 - n.derefNB()
512 - n.ns.lru.mu.Unlock()
513 -}
514 -
515 -type lruHandle struct {
516 - node *lruNode
517 - once uint32
518 -}
519 -
520 -func (h *lruHandle) Value() interface{} {
521 - if atomic.LoadUint32(&h.once) == 0 {
522 - return h.node.value
523 - }
524 - return nil
525 -}
526 -
527 -func (h *lruHandle) Release() {
528 - if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
529 - return
530 - }
531 - h.node.deref()
532 - h.node = nil
533 -}
534 -
535 -func rbIsRed(h *lruNode) bool {
536 - if h == nil {
537 - return false
538 - }
539 - return !h.rbBlack
540 -}
541 -
542 -func rbRotLeft(h *lruNode) *lruNode {
543 - x := h.rbRight
544 - if x.rbBlack {
545 - panic("rotating a black link")
546 - }
547 - h.rbRight = x.rbLeft
548 - x.rbLeft = h
549 - x.rbBlack = h.rbBlack
550 - h.rbBlack = false
551 - return x
552 -}
553 -
554 -func rbRotRight(h *lruNode) *lruNode {
555 - x := h.rbLeft
556 - if x.rbBlack {
557 - panic("rotating a black link")
558 - }
559 - h.rbLeft = x.rbRight
560 - x.rbRight = h
561 - x.rbBlack = h.rbBlack
562 - h.rbBlack = false
563 - return x
564 -}
565 -
566 -func rbFlip(h *lruNode) {
567 - h.rbBlack = !h.rbBlack
568 - h.rbLeft.rbBlack = !h.rbLeft.rbBlack
569 - h.rbRight.rbBlack = !h.rbRight.rbBlack
570 -}
571 -
572 -func rbMoveLeft(h *lruNode) *lruNode {
573 - rbFlip(h)
574 - if rbIsRed(h.rbRight.rbLeft) {
575 - h.rbRight = rbRotRight(h.rbRight)
576 - h = rbRotLeft(h)
577 - rbFlip(h)
578 - }
579 - return h
580 -}
581 -
582 -func rbMoveRight(h *lruNode) *lruNode {
583 - rbFlip(h)
584 - if rbIsRed(h.rbLeft.rbLeft) {
585 - h = rbRotRight(h)
586 - rbFlip(h)
587 - }
588 - return h
589 -}
590 -
591 -func rbFixup(h *lruNode) *lruNode {
592 - if rbIsRed(h.rbRight) {
593 - h = rbRotLeft(h)
594 - }
595 -
596 - if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
597 - h = rbRotRight(h)
598 - }
599 -
600 - if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
601 - rbFlip(h)
602 - }
603 -
604 - return h
605 -}
606 -
607 -func rbDeleteMin(h *lruNode) (hn, n *lruNode) {
608 - if h == nil {
609 - return nil, nil
610 - }
611 - if h.rbLeft == nil {
612 - return nil, h
613 - }
614 -
615 - if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
616 - h = rbMoveLeft(h)
617 - }
618 -
619 - h.rbLeft, n = rbDeleteMin(h.rbLeft)
620 -
621 - return rbFixup(h), n
622 -}
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/corrupt_test.go
+8 -10
@@ -9,14 +9,12 @@ package leveldb
9 import (
10 "bytes"
11 "fmt"
12 - "io"
13 - "math/rand"
14 - "testing"
15 -
16 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
12 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
13 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
14 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
15 + "io"
16 + "math/rand"
17 + "testing"
18 )
19
20 const ctValSize = 1000
@@ -33,8 +31,8 @@ func newDbCorruptHarnessWopt(t *testing.T, o *opt.Options) *dbCorruptHarness {
31
32 func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
33 return newDbCorruptHarnessWopt(t, &opt.Options{
36 - BlockCache: cache.NewLRUCache(100),
37 - Strict: opt.StrictJournalChecksum,
34 + BlockCacheCapacity: 100,
35 + Strict: opt.StrictJournalChecksum,
36 })
37 }
38
@@ -269,9 +267,9 @@ func TestCorruptDB_TableIndex(t *testing.T) {
267 func TestCorruptDB_MissingManifest(t *testing.T) {
268 rnd := rand.New(rand.NewSource(0x0badda7a))
269 h := newDbCorruptHarnessWopt(t, &opt.Options{
272 - BlockCache: cache.NewLRUCache(100),
273 - Strict: opt.StrictJournalChecksum,
274 - WriteBuffer: 1000 * 60,
270 + BlockCacheCapacity: 100,
271 + Strict: opt.StrictJournalChecksum,
272 + WriteBuffer: 1000 * 60,
273 })
274
275 h.build(1000)
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db.go
+11 -10
@@ -27,8 +27,7 @@ import (
27 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
28 )
29
30 -// DB is a LevelDB database.
31 -type DB struct {
30 +type DB struct { // DB is a LevelDB database.
31 // Need 64-bit alignment.
32 seq uint64
33
@@ -347,12 +346,14 @@ func recoverTable(s *session, o *opt.Options) error {
346 return err
347 }
348 iter := tr.NewIterator(nil, nil)
350 - iter.(iterator.ErrorCallbackSetter).SetErrorCallback(func(err error) {
351 - if errors.IsCorrupted(err) {
352 - s.logf("table@recovery block corruption @%d %q", file.Num(), err)
353 - tcorruptedBlock++
354 - }
355 - })
349 + if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok {
350 + itererr.SetErrorCallback(func(err error) {
351 + if errors.IsCorrupted(err) {
352 + s.logf("table@recovery block corruption @%d %q", file.Num(), err)
353 + tcorruptedBlock++
354 + }
355 + })
356 + }
357
358 // Scan the table.
359 for iter.Next() {
@@ -823,8 +824,8 @@ func (db *DB) GetProperty(name string) (value string, err error) {
824 case p == "blockpool":
825 value = fmt.Sprintf("%v", db.s.tops.bpool)
826 case p == "cachedblock":
826 - if bc := db.s.o.GetBlockCache(); bc != nil {
827 - value = fmt.Sprintf("%d", bc.Size())
827 + if db.s.tops.bcache != nil {
828 + value = fmt.Sprintf("%d", db.s.tops.bcache.Size())
829 } else {
830 value = "<nil>"
831 }
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_test.go
+7 -7
@@ -1271,7 +1271,7 @@ func TestDB_DeletionMarkers2(t *testing.T) {
1271 }
1272
1273 func TestDB_CompactionTableOpenError(t *testing.T) {
1274 - h := newDbHarnessWopt(t, &opt.Options{CachedOpenFiles: -1})
1274 + h := newDbHarnessWopt(t, &opt.Options{OpenFilesCacheCapacity: -1})
1275 defer h.close()
1276
1277 im := 10
@@ -1629,8 +1629,8 @@ func TestDB_ManualCompaction(t *testing.T) {
1629
1630 func TestDB_BloomFilter(t *testing.T) {
1631 h := newDbHarnessWopt(t, &opt.Options{
1632 - BlockCache: opt.NoCache,
1633 - Filter: filter.NewBloomFilter(10),
1632 + DisableBlockCache: true,
1633 + Filter: filter.NewBloomFilter(10),
1634 })
1635 defer h.close()
1636
@@ -2066,8 +2066,8 @@ func TestDB_GetProperties(t *testing.T) {
2066
2067 func TestDB_GoleveldbIssue72and83(t *testing.T) {
2068 h := newDbHarnessWopt(t, &opt.Options{
2069 - WriteBuffer: 1 * opt.MiB,
2070 - CachedOpenFiles: 3,
2069 + WriteBuffer: 1 * opt.MiB,
2070 + OpenFilesCacheCapacity: 3,
2071 })
2072 defer h.close()
2073
@@ -2200,7 +2200,7 @@ func TestDB_GoleveldbIssue72and83(t *testing.T) {
2200 func TestDB_TransientError(t *testing.T) {
2201 h := newDbHarnessWopt(t, &opt.Options{
2202 WriteBuffer: 128 * opt.KiB,
2203 - CachedOpenFiles: 3,
2203 + OpenFilesCacheCapacity: 3,
2204 DisableCompactionBackoff: true,
2205 })
2206 defer h.close()
@@ -2410,7 +2410,7 @@ func TestDB_TableCompactionBuilder(t *testing.T) {
2410 CompactionTableSize: 43 * opt.KiB,
2411 CompactionExpandLimitFactor: 1,
2412 CompactionGPOverlapsFactor: 1,
2413 - BlockCache: opt.NoCache,
2413 + DisableBlockCache: true,
2414 }
2415 s, err := newSession(stor, o)
2416 if err != nil {
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/external_test.go
+8 -8
@@ -17,14 +17,14 @@ import (
17 var _ = testutil.Defer(func() {
18 Describe("Leveldb external", func() {
19 o := &opt.Options{
20 - BlockCache: opt.NoCache,
21 - BlockRestartInterval: 5,
22 - BlockSize: 80,
23 - Compression: opt.NoCompression,
24 - CachedOpenFiles: -1,
25 - Strict: opt.StrictAll,
26 - WriteBuffer: 1000,
27 - CompactionTableSize: 2000,
20 + DisableBlockCache: true,
21 + BlockRestartInterval: 5,
22 + BlockSize: 80,
23 + Compression: opt.NoCompression,
24 + OpenFilesCacheCapacity: -1,
25 + Strict: opt.StrictAll,
26 + WriteBuffer: 1000,
27 + CompactionTableSize: 2000,
28 }
29
30 Describe("write test", func() {
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt/options.go
+86 -47
@@ -20,8 +20,9 @@ const (
20 GiB = MiB * 1024
21 )
22
23 -const (
24 - DefaultBlockCacheSize = 8 * MiB
23 +var (
24 + DefaultBlockCacher = LRUCacher
25 + DefaultBlockCacheCapacity = 8 * MiB
26 DefaultBlockRestartInterval = 16
27 DefaultBlockSize = 4 * KiB
28 DefaultCompactionExpandLimitFactor = 25
@@ -33,7 +34,8 @@ const (
34 DefaultCompactionTotalSize = 10 * MiB
35 DefaultCompactionTotalSizeMultiplier = 10.0
36 DefaultCompressionType = SnappyCompression
36 - DefaultCachedOpenFiles = 500
37 + DefaultOpenFilesCacher = LRUCacher
38 + DefaultOpenFilesCacheCapacity = 500
39 DefaultMaxMemCompationLevel = 2
40 DefaultNumLevel = 7
41 DefaultWriteBuffer = 4 * MiB
@@ -41,22 +43,33 @@ const (
43 DefaultWriteL0SlowdownTrigger = 8
44 )
45
44 -type noCache struct{}
46 +// Cacher is a caching algorithm.
47 +type Cacher interface {
48 + New(capacity int) cache.Cacher
49 +}
50
46 -func (noCache) SetCapacity(capacity int) {}
47 -func (noCache) Capacity() int { return 0 }
48 -func (noCache) Used() int { return 0 }
49 -func (noCache) Size() int { return 0 }
50 -func (noCache) NumObjects() int { return 0 }
51 -func (noCache) GetNamespace(id uint64) cache.Namespace { return nil }
52 -func (noCache) PurgeNamespace(id uint64, fin cache.PurgeFin) {}
53 -func (noCache) ZapNamespace(id uint64) {}
54 -func (noCache) Purge(fin cache.PurgeFin) {}
55 -func (noCache) Zap() {}
51 +type CacherFunc struct {
52 + NewFunc func(capacity int) cache.Cacher
53 +}
54
57 -var NoCache cache.Cache = noCache{}
55 +func (f *CacherFunc) New(capacity int) cache.Cacher {
56 + if f.NewFunc != nil {
57 + return f.NewFunc(capacity)
58 + }
59 + return nil
60 +}
61 +
62 +func noCacher(int) cache.Cacher { return nil }
63 +
64 +var (
65 + // LRUCacher is the LRU-cache algorithm.
66 + LRUCacher = &CacherFunc{cache.NewLRU}
67 +
68 + // NoCacher is the value to disable caching algorithm.
69 + NoCacher = &CacherFunc{}
70 +)
71
59 -// Compression is the per-block compression algorithm to use.
72 +// Compression is the 'sorted table' block compression algorithm to use.
73 type Compression uint
74
75 func (c Compression) String() string {
@@ -133,16 +146,17 @@ type Options struct {
146 // The default value is nil
147 AltFilters []filter.Filter
148
136 - // BlockCache provides per-block caching for LevelDB. Specify NoCache to
137 - // disable block caching.
149 + // BlockCacher provides cache algorithm for LevelDB 'sorted table' block caching.
150 + // Specify NoCacher to disable caching algorithm.
151 //
139 - // By default LevelDB will create LRU-cache with capacity of BlockCacheSize.
140 - BlockCache cache.Cache
152 + // The default value is LRUCacher.
153 + BlockCacher Cacher
154
142 - // BlockCacheSize defines the capacity of the default 'block cache'.
155 + // BlockCacheCapacity defines the capacity of the 'sorted table' block caching.
156 + // Use -1 for zero, this has same effect as specifying NoCacher to BlockCacher.
157 //
158 // The default value is 8MiB.
145 - BlockCacheSize int
159 + BlockCacheCapacity int
160
161 // BlockRestartInterval is the number of keys between restart points for
162 // delta encoding of keys.
@@ -156,13 +170,6 @@ type Options struct {
170 // The default value is 4KiB.
171 BlockSize int
172
159 - // CachedOpenFiles defines number of open files to kept around when not
160 - // in-use, the counting includes still in-use files.
161 - // Set this to negative value to disable caching.
162 - //
163 - // The default value is 500.
164 - CachedOpenFiles int
165 -
173 // CompactionExpandLimitFactor limits compaction size after expanded.
174 // This will be multiplied by table size limit at compaction target level.
175 //
@@ -237,11 +244,17 @@ type Options struct {
244 // The default value uses the same ordering as bytes.Compare.
245 Comparer comparer.Comparer
246
240 - // Compression defines the per-block compression to use.
247 + // Compression defines the 'sorted table' block compression to use.
248 //
249 // The default value (DefaultCompression) uses snappy compression.
250 Compression Compression
251
252 + // DisableBlockCache allows disable use of cache.Cache functionality on
253 + // 'sorted table' block.
254 + //
255 + // The default value is false.
256 + DisableBlockCache bool
257 +
258 // DisableCompactionBackoff allows disable compaction retry backoff.
259 //
260 // The default value is false.
@@ -288,6 +301,18 @@ type Options struct {
301 // The default is 7.
302 NumLevel int
303
304 + // OpenFilesCacher provides cache algorithm for open files caching.
305 + // Specify NoCacher to disable caching algorithm.
306 + //
307 + // The default value is LRUCacher.
308 + OpenFilesCacher Cacher
309 +
310 + // OpenFilesCacheCapacity defines the capacity of the open files caching.
311 + // Use -1 for zero, this has same effect as specifying NoCacher to OpenFilesCacher.
312 + //
313 + // The default value is 500.
314 + OpenFilesCacheCapacity int
315 +
316 // Strict defines the DB strict level.
317 Strict Strict
318
@@ -320,18 +345,22 @@ func (o *Options) GetAltFilters() []filter.Filter {
345 return o.AltFilters
346 }
347
323 -func (o *Options) GetBlockCache() cache.Cache {
324 - if o == nil {
348 +func (o *Options) GetBlockCacher() Cacher {
349 + if o == nil || o.BlockCacher == nil {
350 + return DefaultBlockCacher
351 + } else if o.BlockCacher == NoCacher {
352 return nil
353 }
327 - return o.BlockCache
354 + return o.BlockCacher
355 }
356
330 -func (o *Options) GetBlockCacheSize() int {
331 - if o == nil || o.BlockCacheSize <= 0 {
332 - return DefaultBlockCacheSize
357 +func (o *Options) GetBlockCacheCapacity() int {
358 + if o == nil || o.BlockCacheCapacity == 0 {
359 + return DefaultBlockCacheCapacity
360 + } else if o.BlockCacheCapacity < 0 {
361 + return 0
362 }
334 - return o.BlockCacheSize
363 + return o.BlockCacheCapacity
364 }
365
366 func (o *Options) GetBlockRestartInterval() int {
@@ -348,15 +377,6 @@ func (o *Options) GetBlockSize() int {
377 return o.BlockSize
378 }
379
351 -func (o *Options) GetCachedOpenFiles() int {
352 - if o == nil || o.CachedOpenFiles == 0 {
353 - return DefaultCachedOpenFiles
354 - } else if o.CachedOpenFiles < 0 {
355 - return 0
356 - }
357 - return o.CachedOpenFiles
358 -}
359 -
380 func (o *Options) GetCompactionExpandLimit(level int) int {
381 factor := DefaultCompactionExpandLimitFactor
382 if o != nil && o.CompactionExpandLimitFactor > 0 {
@@ -477,7 +497,7 @@ func (o *Options) GetMaxMemCompationLevel() int {
497 if o != nil {
498 if o.MaxMemCompationLevel > 0 {
499 level = o.MaxMemCompationLevel
480 - } else if o.MaxMemCompationLevel == -1 {
500 + } else if o.MaxMemCompationLevel < 0 {
501 level = 0
502 }
503 }
@@ -494,6 +514,25 @@ func (o *Options) GetNumLevel() int {
514 return o.NumLevel
515 }
516
517 +func (o *Options) GetOpenFilesCacher() Cacher {
518 + if o == nil || o.OpenFilesCacher == nil {
519 + return DefaultOpenFilesCacher
520 + }
521 + if o.OpenFilesCacher == NoCacher {
522 + return nil
523 + }
524 + return o.OpenFilesCacher
525 +}
526 +
527 +func (o *Options) GetOpenFilesCacheCapacity() int {
528 + if o == nil || o.OpenFilesCacheCapacity == 0 {
529 + return DefaultOpenFilesCacheCapacity
530 + } else if o.OpenFilesCacheCapacity < 0 {
531 + return 0
532 + }
533 + return o.OpenFilesCacheCapacity
534 +}
535 +
536 func (o *Options) GetStrict(strict Strict) bool {
537 if o == nil || o.Strict == 0 {
538 return DefaultStrict&strict != 0
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/options.go
-8
@@ -7,7 +7,6 @@
7 package leveldb
8
9 import (
10 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
10 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
11 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
12 )
@@ -32,13 +31,6 @@ func (s *session) setOptions(o *opt.Options) {
31 no.AltFilters[i] = &iFilter{filter}
32 }
33 }
35 - // Block cache.
36 - switch o.GetBlockCache() {
37 - case nil:
38 - no.BlockCache = cache.NewLRUCache(o.GetBlockCacheSize())
39 - case opt.NoCache:
40 - no.BlockCache = nil
41 - }
34 // Comparer.
35 s.icmp = &iComparer{o.GetComparer()}
36 no.Comparer = s.icmp
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/session.go
+1 -4
@@ -73,7 +73,7 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
73 stCompPtrs: make([]iKey, o.GetNumLevel()),
74 }
75 s.setOptions(o)
76 - s.tops = newTableOps(s, s.o.GetCachedOpenFiles())
76 + s.tops = newTableOps(s)
77 s.setVersion(newVersion(s))
78 s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
79 return
@@ -82,9 +82,6 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
82 // Close session.
83 func (s *session) close() {
84 s.tops.close()
85 - if bc := s.o.GetBlockCache(); bc != nil {
86 - bc.Purge(nil)
87 - }
85 if s.manifest != nil {
86 s.manifest.Close()
87 }
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/session_util.go
+1 -3
@@ -14,10 +14,8 @@ import (
14 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
15 )
16
17 -// Logging.
18 -
17 type dropper struct {
20 - s *session
18 + s *session // Logging.
19 file storage.File
20 }
21
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table.go
+43 -27
@@ -286,10 +286,10 @@ func (x *tFilesSortByNum) Less(i, j int) bool {
286
287 // Table operations.
288 type tOps struct {
289 - s *session
290 - cache cache.Cache
291 - cacheNS cache.Namespace
292 - bpool *util.BufferPool
289 + s *session
290 + cache *cache.Cache
291 + bcache *cache.Cache
292 + bpool *util.BufferPool
293 }
294
295 // Creates an empty table and returns table writer.
@@ -338,26 +338,28 @@ func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
338
339 // Opens table. It returns a cache handle, which should
340 // be released after use.
341 -func (t *tOps) open(f *tFile) (ch cache.Handle, err error) {
341 +func (t *tOps) open(f *tFile) (ch *cache.Handle, err error) {
342 num := f.file.Num()
343 - ch = t.cacheNS.Get(num, func() (charge int, value interface{}) {
343 + ch = t.cache.Get(0, num, func() (size int, value cache.Value) {
344 var r storage.Reader
345 r, err = f.file.Open()
346 if err != nil {
347 return 0, nil
348 }
349
350 - var bcacheNS cache.Namespace
351 - if bc := t.s.o.GetBlockCache(); bc != nil {
352 - bcacheNS = bc.GetNamespace(num)
350 + var bcache *cache.CacheGetter
351 + if t.bcache != nil {
352 + bcache = &cache.CacheGetter{Cache: t.bcache, NS: num}
353 }
354 +
355 var tr *table.Reader
355 - tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcacheNS, t.bpool, t.s.o.Options)
356 + tr, err = table.NewReader(r, int64(f.size), storage.NewFileInfo(f.file), bcache, t.bpool, t.s.o.Options)
357 if err != nil {
358 r.Close()
359 return 0, nil
360 }
361 return 1, tr
362 +
363 })
364 if ch == nil && err == nil {
365 err = ErrClosed
@@ -412,16 +414,14 @@ func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) ite
414 // no one use the the table.
415 func (t *tOps) remove(f *tFile) {
416 num := f.file.Num()
415 - t.cacheNS.Delete(num, func(exist, pending bool) {
416 - if !pending {
417 - if err := f.file.Remove(); err != nil {
418 - t.s.logf("table@remove removing @%d %q", num, err)
419 - } else {
420 - t.s.logf("table@remove removed @%d", num)
421 - }
422 - if bc := t.s.o.GetBlockCache(); bc != nil {
423 - bc.ZapNamespace(num)
424 - }
417 + t.cache.Delete(0, num, func() {
418 + if err := f.file.Remove(); err != nil {
419 + t.s.logf("table@remove removing @%d %q", num, err)
420 + } else {
421 + t.s.logf("table@remove removed @%d", num)
422 + }
423 + if t.bcache != nil {
424 + t.bcache.EvictNS(num)
425 }
426 })
427 }
@@ -429,18 +429,34 @@ func (t *tOps) remove(f *tFile) {
429 // Closes the table ops instance. It will close all tables,
430 // regadless still used or not.
431 func (t *tOps) close() {
432 - t.cache.Zap()
432 t.bpool.Close()
433 + t.cache.Close()
434 + if t.bcache != nil {
435 + t.bcache.Close()
436 + }
437 }
438
439 // Creates new initialized table ops instance.
437 -func newTableOps(s *session, cacheCap int) *tOps {
438 - c := cache.NewLRUCache(cacheCap)
440 +func newTableOps(s *session) *tOps {
441 + var (
442 + cacher cache.Cacher
443 + bcache *cache.Cache
444 + )
445 + if s.o.GetOpenFilesCacheCapacity() > 0 {
446 + cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity())
447 + }
448 + if !s.o.DisableBlockCache {
449 + var bcacher cache.Cacher
450 + if s.o.GetBlockCacheCapacity() > 0 {
451 + bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity())
452 + }
453 + bcache = cache.NewCache(bcacher)
454 + }
455 return &tOps{
440 - s: s,
441 - cache: c,
442 - cacheNS: c.GetNamespace(0),
443 - bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
456 + s: s,
457 + cache: cache.NewCache(cacher),
458 + bcache: bcache,
459 + bpool: util.NewBufferPool(s.o.GetBlockSize() + 5),
460 }
461 }
462
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/reader.go
+35 -28
@@ -14,8 +14,6 @@ import (
14 "strings"
15 "sync"
16
17 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
18 -
17 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache"
18 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/comparer"
19 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/errors"
@@ -24,6 +22,7 @@ import (
22 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
23 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage"
24 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
25 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
26 )
27
28 var (
@@ -509,7 +508,7 @@ type Reader struct {
508 mu sync.RWMutex
509 fi *storage.FileInfo
510 reader io.ReaderAt
512 - cache cache.Namespace
511 + cache *cache.CacheGetter
512 err error
513 bpool *util.BufferPool
514 // Options
@@ -613,18 +612,22 @@ func (r *Reader) readBlock(bh blockHandle, verifyChecksum bool) (*block, error)
612
613 func (r *Reader) readBlockCached(bh blockHandle, verifyChecksum, fillCache bool) (*block, util.Releaser, error) {
614 if r.cache != nil {
616 - var err error
617 - ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
618 - if !fillCache {
619 - return 0, nil
620 - }
621 - var b *block
622 - b, err = r.readBlock(bh, verifyChecksum)
623 - if err != nil {
624 - return 0, nil
625 - }
626 - return cap(b.data), b
627 - })
615 + var (
616 + err error
617 + ch *cache.Handle
618 + )
619 + if fillCache {
620 + ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
621 + var b *block
622 + b, err = r.readBlock(bh, verifyChecksum)
623 + if err != nil {
624 + return 0, nil
625 + }
626 + return cap(b.data), b
627 + })
628 + } else {
629 + ch = r.cache.Get(bh.offset, nil)
630 + }
631 if ch != nil {
632 b, ok := ch.Value().(*block)
633 if !ok {
@@ -667,18 +670,22 @@ func (r *Reader) readFilterBlock(bh blockHandle) (*filterBlock, error) {
670
671 func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) {
672 if r.cache != nil {
670 - var err error
671 - ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
672 - if !fillCache {
673 - return 0, nil
674 - }
675 - var b *filterBlock
676 - b, err = r.readFilterBlock(bh)
677 - if err != nil {
678 - return 0, nil
679 - }
680 - return cap(b.data), b
681 - })
673 + var (
674 + err error
675 + ch *cache.Handle
676 + )
677 + if fillCache {
678 + ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) {
679 + var b *filterBlock
680 + b, err = r.readFilterBlock(bh)
681 + if err != nil {
682 + return 0, nil
683 + }
684 + return cap(b.data), b
685 + })
686 + } else {
687 + ch = r.cache.Get(bh.offset, nil)
688 + }
689 if ch != nil {
690 b, ok := ch.Value().(*filterBlock)
691 if !ok {
@@ -980,7 +987,7 @@ func (r *Reader) Release() {
987 // The fi, cache and bpool is optional and can be nil.
988 //
989 // The returned table reader instance is goroutine-safe.
983 -func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache cache.Namespace, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
990 +func NewReader(f io.ReaderAt, size int64, fi *storage.FileInfo, cache *cache.CacheGetter, bpool *util.BufferPool, o *opt.Options) (*Reader, error) {
991 if f == nil {
992 return nil, errors.New("leveldb/table: nil file")
993 }
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/writer.go
+1 -2
@@ -12,12 +12,11 @@ import (
12 "fmt"
13 "io"
14
15 - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
16 -
15 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/comparer"
16 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/filter"
17 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt"
18 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util"
19 + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy"
20 )
21
22 func sharedPrefixLen(a, b []byte) int {
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/decode.go
+170 -2
@@ -7,10 +7,15 @@ package snappy
7 import (
8 "encoding/binary"
9 "errors"
10 + "io"
11 )
12
12 -// ErrCorrupt reports that the input is invalid.
13 -var ErrCorrupt = errors.New("snappy: corrupt input")
13 +var (
14 + // ErrCorrupt reports that the input is invalid.
15 + ErrCorrupt = errors.New("snappy: corrupt input")
16 + // ErrUnsupported reports that the input isn't supported.
17 + ErrUnsupported = errors.New("snappy: unsupported input")
18 +)
19
20 // DecodedLen returns the length of the decoded block.
21 func DecodedLen(src []byte) (int, error) {
@@ -122,3 +127,166 @@ func Decode(dst, src []byte) ([]byte, error) {
127 }
128 return dst[:d], nil
129 }
130 +
131 +// NewReader returns a new Reader that decompresses from r, using the framing
132 +// format described at
133 +// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
134 +func NewReader(r io.Reader) *Reader {
135 + return &Reader{
136 + r: r,
137 + decoded: make([]byte, maxUncompressedChunkLen),
138 + buf: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
139 + }
140 +}
141 +
142 +// Reader is an io.Reader than can read Snappy-compressed bytes.
143 +type Reader struct {
144 + r io.Reader
145 + err error
146 + decoded []byte
147 + buf []byte
148 + // decoded[i:j] contains decoded bytes that have not yet been passed on.
149 + i, j int
150 + readHeader bool
151 +}
152 +
153 +// Reset discards any buffered data, resets all state, and switches the Snappy
154 +// reader to read from r. This permits reusing a Reader rather than allocating
155 +// a new one.
156 +func (r *Reader) Reset(reader io.Reader) {
157 + r.r = reader
158 + r.err = nil
159 + r.i = 0
160 + r.j = 0
161 + r.readHeader = false
162 +}
163 +
164 +func (r *Reader) readFull(p []byte) (ok bool) {
165 + if _, r.err = io.ReadFull(r.r, p); r.err != nil {
166 + if r.err == io.ErrUnexpectedEOF {
167 + r.err = ErrCorrupt
168 + }
169 + return false
170 + }
171 + return true
172 +}
173 +
174 +// Read satisfies the io.Reader interface.
175 +func (r *Reader) Read(p []byte) (int, error) {
176 + if r.err != nil {
177 + return 0, r.err
178 + }
179 + for {
180 + if r.i < r.j {
181 + n := copy(p, r.decoded[r.i:r.j])
182 + r.i += n
183 + return n, nil
184 + }
185 + if !r.readFull(r.buf[:4]) {
186 + return 0, r.err
187 + }
188 + chunkType := r.buf[0]
189 + if !r.readHeader {
190 + if chunkType != chunkTypeStreamIdentifier {
191 + r.err = ErrCorrupt
192 + return 0, r.err
193 + }
194 + r.readHeader = true
195 + }
196 + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
197 + if chunkLen > len(r.buf) {
198 + r.err = ErrUnsupported
199 + return 0, r.err
200 + }
201 +
202 + // The chunk types are specified at
203 + // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
204 + switch chunkType {
205 + case chunkTypeCompressedData:
206 + // Section 4.2. Compressed data (chunk type 0x00).
207 + if chunkLen < checksumSize {
208 + r.err = ErrCorrupt
209 + return 0, r.err
210 + }
211 + buf := r.buf[:chunkLen]
212 + if !r.readFull(buf) {
213 + return 0, r.err
214 + }
215 + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
216 + buf = buf[checksumSize:]
217 +
218 + n, err := DecodedLen(buf)
219 + if err != nil {
220 + r.err = err
221 + return 0, r.err
222 + }
223 + if n > len(r.decoded) {
224 + r.err = ErrCorrupt
225 + return 0, r.err
226 + }
227 + if _, err := Decode(r.decoded, buf); err != nil {
228 + r.err = err
229 + return 0, r.err
230 + }
231 + if crc(r.decoded[:n]) != checksum {
232 + r.err = ErrCorrupt
233 + return 0, r.err
234 + }
235 + r.i, r.j = 0, n
236 + continue
237 +
238 + case chunkTypeUncompressedData:
239 + // Section 4.3. Uncompressed data (chunk type 0x01).
240 + if chunkLen < checksumSize {
241 + r.err = ErrCorrupt
242 + return 0, r.err
243 + }
244 + buf := r.buf[:checksumSize]
245 + if !r.readFull(buf) {
246 + return 0, r.err
247 + }
248 + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
249 + // Read directly into r.decoded instead of via r.buf.
250 + n := chunkLen - checksumSize
251 + if !r.readFull(r.decoded[:n]) {
252 + return 0, r.err
253 + }
254 + if crc(r.decoded[:n]) != checksum {
255 + r.err = ErrCorrupt
256 + return 0, r.err
257 + }
258 + r.i, r.j = 0, n
259 + continue
260 +
261 + case chunkTypeStreamIdentifier:
262 + // Section 4.1. Stream identifier (chunk type 0xff).
263 + if chunkLen != len(magicBody) {
264 + r.err = ErrCorrupt
265 + return 0, r.err
266 + }
267 + if !r.readFull(r.buf[:len(magicBody)]) {
268 + return 0, r.err
269 + }
270 + for i := 0; i < len(magicBody); i++ {
271 + if r.buf[i] != magicBody[i] {
272 + r.err = ErrCorrupt
273 + return 0, r.err
274 + }
275 + }
276 + continue
277 + }
278 +
279 + if chunkType <= 0x7f {
280 + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
281 + r.err = ErrUnsupported
282 + return 0, r.err
283 +
284 + } else {
285 + // Section 4.4 Padding (chunk type 0xfe).
286 + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
287 + if !r.readFull(r.buf[:chunkLen]) {
288 + return 0, r.err
289 + }
290 + }
291 + }
292 +}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/encode.go
+84
@@ -6,6 +6,7 @@ package snappy
6
7 import (
8 "encoding/binary"
9 + "io"
10 )
11
12 // We limit how far copy back-references can go, the same as the C++ code.
@@ -172,3 +173,86 @@ func MaxEncodedLen(srcLen int) int {
173 // This last factor dominates the blowup, so the final estimate is:
174 return 32 + srcLen + srcLen/6
175 }
176 +
177 +// NewWriter returns a new Writer that compresses to w, using the framing
178 +// format described at
179 +// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
180 +func NewWriter(w io.Writer) *Writer {
181 + return &Writer{
182 + w: w,
183 + enc: make([]byte, MaxEncodedLen(maxUncompressedChunkLen)),
184 + }
185 +}
186 +
187 +// Writer is an io.Writer than can write Snappy-compressed bytes.
188 +type Writer struct {
189 + w io.Writer
190 + err error
191 + enc []byte
192 + buf [checksumSize + chunkHeaderSize]byte
193 + wroteHeader bool
194 +}
195 +
196 +// Reset discards the writer's state and switches the Snappy writer to write to
197 +// w. This permits reusing a Writer rather than allocating a new one.
198 +func (w *Writer) Reset(writer io.Writer) {
199 + w.w = writer
200 + w.err = nil
201 + w.wroteHeader = false
202 +}
203 +
204 +// Write satisfies the io.Writer interface.
205 +func (w *Writer) Write(p []byte) (n int, errRet error) {
206 + if w.err != nil {
207 + return 0, w.err
208 + }
209 + if !w.wroteHeader {
210 + copy(w.enc, magicChunk)
211 + if _, err := w.w.Write(w.enc[:len(magicChunk)]); err != nil {
212 + w.err = err
213 + return n, err
214 + }
215 + w.wroteHeader = true
216 + }
217 + for len(p) > 0 {
218 + var uncompressed []byte
219 + if len(p) > maxUncompressedChunkLen {
220 + uncompressed, p = p[:maxUncompressedChunkLen], p[maxUncompressedChunkLen:]
221 + } else {
222 + uncompressed, p = p, nil
223 + }
224 + checksum := crc(uncompressed)
225 +
226 + // Compress the buffer, discarding the result if the improvement
227 + // isn't at least 12.5%.
228 + chunkType := uint8(chunkTypeCompressedData)
229 + chunkBody, err := Encode(w.enc, uncompressed)
230 + if err != nil {
231 + w.err = err
232 + return n, err
233 + }
234 + if len(chunkBody) >= len(uncompressed)-len(uncompressed)/8 {
235 + chunkType, chunkBody = chunkTypeUncompressedData, uncompressed
236 + }
237 +
238 + chunkLen := 4 + len(chunkBody)
239 + w.buf[0] = chunkType
240 + w.buf[1] = uint8(chunkLen >> 0)
241 + w.buf[2] = uint8(chunkLen >> 8)
242 + w.buf[3] = uint8(chunkLen >> 16)
243 + w.buf[4] = uint8(checksum >> 0)
244 + w.buf[5] = uint8(checksum >> 8)
245 + w.buf[6] = uint8(checksum >> 16)
246 + w.buf[7] = uint8(checksum >> 24)
247 + if _, err = w.w.Write(w.buf[:]); err != nil {
248 + w.err = err
249 + return n, err
250 + }
251 + if _, err = w.w.Write(chunkBody); err != nil {
252 + w.err = err
253 + return n, err
254 + }
255 + n += len(uncompressed)
256 + }
257 + return n, nil
258 +}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/snappy.go
+30
@@ -8,6 +8,10 @@
8 // The C++ snappy implementation is at http://code.google.com/p/snappy/
9 package snappy
10
11 +import (
12 + "hash/crc32"
13 +)
14 +
15 /*
16 Each encoded block begins with the varint-encoded length of the decoded data,
17 followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
@@ -36,3 +40,29 @@ const (
40 tagCopy2 = 0x02
41 tagCopy4 = 0x03
42 )
43 +
44 +const (
45 + checksumSize = 4
46 + chunkHeaderSize = 4
47 + magicChunk = "\xff\x06\x00\x00" + magicBody
48 + magicBody = "sNaPpY"
49 + // https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt says
50 + // that "the uncompressed data in a chunk must be no longer than 65536 bytes".
51 + maxUncompressedChunkLen = 65536
52 +)
53 +
54 +const (
55 + chunkTypeCompressedData = 0x00
56 + chunkTypeUncompressedData = 0x01
57 + chunkTypePadding = 0xfe
58 + chunkTypeStreamIdentifier = 0xff
59 +)
60 +
61 +var crcTable = crc32.MakeTable(crc32.Castagnoli)
62 +
63 +// crc implements the checksum specified in section 3 of
64 +// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
65 +func crc(b []byte) uint32 {
66 + c := crc32.Update(0, crcTable, b)
67 + return uint32(c>>15|c<<17) + 0xa282ead8
68 +}
Godeps/_workspace/src/github.com/syndtr/gosnappy/snappy/snappy_test.go
+152 -49
@@ -18,7 +18,10 @@ import (
18 "testing"
19 )
20
21 -var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
21 +var (
22 + download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
23 + testdata = flag.String("testdata", "testdata", "Directory containing the test data")
24 +)
25
26 func roundtrip(b, ebuf, dbuf []byte) error {
27 e, err := Encode(ebuf, b)
@@ -55,11 +58,11 @@ func TestSmallCopy(t *testing.T) {
58 }
59
60 func TestSmallRand(t *testing.T) {
58 - rand.Seed(27354294)
61 + rng := rand.New(rand.NewSource(27354294))
62 for n := 1; n < 20000; n += 23 {
63 b := make([]byte, n)
61 - for i, _ := range b {
62 - b[i] = uint8(rand.Uint32())
64 + for i := range b {
65 + b[i] = uint8(rng.Uint32())
66 }
67 if err := roundtrip(b, nil, nil); err != nil {
68 t.Fatal(err)
@@ -70,7 +73,7 @@ func TestSmallRand(t *testing.T) {
73 func TestSmallRegular(t *testing.T) {
74 for n := 1; n < 20000; n += 23 {
75 b := make([]byte, n)
73 - for i, _ := range b {
76 + for i := range b {
77 b[i] = uint8(i%10 + 'a')
78 }
79 if err := roundtrip(b, nil, nil); err != nil {
@@ -79,6 +82,120 @@ func TestSmallRegular(t *testing.T) {
82 }
83 }
84
85 +func cmp(a, b []byte) error {
86 + if len(a) != len(b) {
87 + return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
88 + }
89 + for i := range a {
90 + if a[i] != b[i] {
91 + return fmt.Errorf("byte #%d: got 0x%02x, want 0x%02x", i, a[i], b[i])
92 + }
93 + }
94 + return nil
95 +}
96 +
97 +func TestFramingFormat(t *testing.T) {
98 + // src is comprised of alternating 1e5-sized sequences of random
99 + // (incompressible) bytes and repeated (compressible) bytes. 1e5 was chosen
100 + // because it is larger than maxUncompressedChunkLen (64k).
101 + src := make([]byte, 1e6)
102 + rng := rand.New(rand.NewSource(1))
103 + for i := 0; i < 10; i++ {
104 + if i%2 == 0 {
105 + for j := 0; j < 1e5; j++ {
106 + src[1e5*i+j] = uint8(rng.Intn(256))
107 + }
108 + } else {
109 + for j := 0; j < 1e5; j++ {
110 + src[1e5*i+j] = uint8(i)
111 + }
112 + }
113 + }
114 +
115 + buf := new(bytes.Buffer)
116 + if _, err := NewWriter(buf).Write(src); err != nil {
117 + t.Fatalf("Write: encoding: %v", err)
118 + }
119 + dst, err := ioutil.ReadAll(NewReader(buf))
120 + if err != nil {
121 + t.Fatalf("ReadAll: decoding: %v", err)
122 + }
123 + if err := cmp(dst, src); err != nil {
124 + t.Fatal(err)
125 + }
126 +}
127 +
128 +func TestReaderReset(t *testing.T) {
129 + gold := bytes.Repeat([]byte("All that is gold does not glitter,\n"), 10000)
130 + buf := new(bytes.Buffer)
131 + if _, err := NewWriter(buf).Write(gold); err != nil {
132 + t.Fatalf("Write: %v", err)
133 + }
134 + encoded, invalid, partial := buf.String(), "invalid", "partial"
135 + r := NewReader(nil)
136 + for i, s := range []string{encoded, invalid, partial, encoded, partial, invalid, encoded, encoded} {
137 + if s == partial {
138 + r.Reset(strings.NewReader(encoded))
139 + if _, err := r.Read(make([]byte, 101)); err != nil {
140 + t.Errorf("#%d: %v", i, err)
141 + continue
142 + }
143 + continue
144 + }
145 + r.Reset(strings.NewReader(s))
146 + got, err := ioutil.ReadAll(r)
147 + switch s {
148 + case encoded:
149 + if err != nil {
150 + t.Errorf("#%d: %v", i, err)
151 + continue
152 + }
153 + if err := cmp(got, gold); err != nil {
154 + t.Errorf("#%d: %v", i, err)
155 + continue
156 + }
157 + case invalid:
158 + if err == nil {
159 + t.Errorf("#%d: got nil error, want non-nil", i)
160 + continue
161 + }
162 + }
163 + }
164 +}
165 +
166 +func TestWriterReset(t *testing.T) {
167 + gold := bytes.Repeat([]byte("Not all those who wander are lost;\n"), 10000)
168 + var gots, wants [][]byte
169 + const n = 20
170 + w, failed := NewWriter(nil), false
171 + for i := 0; i <= n; i++ {
172 + buf := new(bytes.Buffer)
173 + w.Reset(buf)
174 + want := gold[:len(gold)*i/n]
175 + if _, err := w.Write(want); err != nil {
176 + t.Errorf("#%d: Write: %v", i, err)
177 + failed = true
178 + continue
179 + }
180 + got, err := ioutil.ReadAll(NewReader(buf))
181 + if err != nil {
182 + t.Errorf("#%d: ReadAll: %v", i, err)
183 + failed = true
184 + continue
185 + }
186 + gots = append(gots, got)
187 + wants = append(wants, want)
188 + }
189 + if failed {
190 + return
191 + }
192 + for i := range gots {
193 + if err := cmp(gots[i], wants[i]); err != nil {
194 + t.Errorf("#%d: %v", i, err)
195 + }
196 + }
197 +}
198 +
199 func benchDecode(b *testing.B, src []byte) {
200 encoded, err := Encode(nil, src)
201 if err != nil {
@@ -102,7 +219,7 @@ func benchEncode(b *testing.B, src []byte) {
219 }
220 }
221
105 -func readFile(b *testing.B, filename string) []byte {
222 +func readFile(b testing.TB, filename string) []byte {
223 src, err := ioutil.ReadFile(filename)
224 if err != nil {
225 b.Fatalf("failed reading %s: %s", filename, err)
@@ -144,7 +261,7 @@ func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
261 func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
262
263 // testFiles' values are copied directly from
147 -// https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc.
264 +// https://raw.githubusercontent.com/google/snappy/master/snappy_unittest.cc
265 // The label field is unused in snappy-go.
266 var testFiles = []struct {
267 label string
@@ -152,29 +269,36 @@ var testFiles = []struct {
269 }{
270 {"html", "html"},
271 {"urls", "urls.10K"},
155 - {"jpg", "house.jpg"},
156 - {"pdf", "mapreduce-osdi-1.pdf"},
272 + {"jpg", "fireworks.jpeg"},
273 + {"jpg_200", "fireworks.jpeg"},
274 + {"pdf", "paper-100k.pdf"},
275 {"html4", "html_x_4"},
158 - {"cp", "cp.html"},
159 - {"c", "fields.c"},
160 - {"lsp", "grammar.lsp"},
161 - {"xls", "kennedy.xls"},
276 {"txt1", "alice29.txt"},
277 {"txt2", "asyoulik.txt"},
278 {"txt3", "lcet10.txt"},
279 {"txt4", "plrabn12.txt"},
166 - {"bin", "ptt5"},
167 - {"sum", "sum"},
168 - {"man", "xargs.1"},
280 {"pb", "geo.protodata"},
281 {"gaviota", "kppkn.gtb"},
282 }
283
284 // The test data files are present at this canonical URL.
174 -const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/"
285 +const baseURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
286
287 func downloadTestdata(basename string) (errRet error) {
177 - filename := filepath.Join("testdata", basename)
288 + filename := filepath.Join(*testdata, basename)
289 + if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
290 + return nil
291 + }
292 +
293 + if !*download {
294 + return fmt.Errorf("test data not found; skipping benchmark without the -download flag")
295 + }
296 + // Download the official snappy C++ implementation reference test data
297 + // files for benchmarking.
298 + if err := os.Mkdir(*testdata, 0777); err != nil && !os.IsExist(err) {
299 + return fmt.Errorf("failed to create testdata: %s", err)
300 + }
301 +
302 f, err := os.Create(filename)
303 if err != nil {
304 return fmt.Errorf("failed to create %s: %s", filename, err)
@@ -185,36 +309,27 @@ func downloadTestdata(basename string) (errRet error) {
309 os.Remove(filename)
310 }
311 }()
188 - resp, err := http.Get(baseURL + basename)
312 + url := baseURL + basename
313 + resp, err := http.Get(url)
314 if err != nil {
190 - return fmt.Errorf("failed to download %s: %s", baseURL+basename, err)
315 + return fmt.Errorf("failed to download %s: %s", url, err)
316 }
317 defer resp.Body.Close()
318 + if s := resp.StatusCode; s != http.StatusOK {
319 + return fmt.Errorf("downloading %s: HTTP status code %d (%s)", url, s, http.StatusText(s))
320 + }
321 _, err = io.Copy(f, resp.Body)
322 if err != nil {
195 - return fmt.Errorf("failed to write %s: %s", filename, err)
323 + return fmt.Errorf("failed to download %s to %s: %s", url, filename, err)
324 }
325 return nil
326 }
327
328 func benchFile(b *testing.B, n int, decode bool) {
201 - filename := filepath.Join("testdata", testFiles[n].filename)
202 - if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 {
203 - if !*download {
204 - b.Fatal("test data not found; skipping benchmark without the -download flag")
205 - }
206 - // Download the official snappy C++ implementation reference test data
207 - // files for benchmarking.
208 - if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) {
209 - b.Fatalf("failed to create testdata: %s", err)
210 - }
211 - for _, tf := range testFiles {
212 - if err := downloadTestdata(tf.filename); err != nil {
213 - b.Fatalf("failed to download testdata: %s", err)
214 - }
215 - }
329 + if err := downloadTestdata(testFiles[n].filename); err != nil {
330 + b.Fatalf("failed to download testdata: %s", err)
331 }
217 - data := readFile(b, filename)
332 + data := readFile(b, filepath.Join(*testdata, testFiles[n].filename))
333 if decode {
334 benchDecode(b, data)
335 } else {
@@ -235,12 +350,6 @@ func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
350 func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
351 func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
352 func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
238 -func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) }
239 -func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) }
240 -func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) }
241 -func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) }
242 -func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) }
243 -func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) }
353 func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
354 func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
355 func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
@@ -253,9 +362,3 @@ func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
362 func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
363 func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
364 func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
256 -func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) }
257 -func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) }
258 -func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) }
259 -func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) }
260 -func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) }
261 -func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) }